MongoDB Integration
- Do define a separated
MongoDbContext
interface and class for each module.
MongoDbContext Interface
- Do define an interface for the
MongoDbContext
that inherits fromIAbpMongoDbContext
. - Do add a
ConnectionStringName
attribute to theMongoDbContext
interface. - Do add
IMongoCollection<TEntity>
properties to theMongoDbContext
interface only for the aggregate roots. Example:
[ConnectionStringName("AbpIdentity")]
public interface IAbpIdentityMongoDbContext : IAbpMongoDbContext
{
IMongoCollection<IdentityUser> Users { get; }
IMongoCollection<IdentityRole> Roles { get; }
}
MongoDbContext class
- Do inherit the
MongoDbContext
from theAbpMongoDbContext
class. - Do add a
ConnectionStringName
attribute to theMongoDbContext
class. - Do implement the corresponding
interface
for theMongoDbContext
class. Example:
[ConnectionStringName("AbpIdentity")]
public class AbpIdentityMongoDbContext : AbpMongoDbContext, IAbpIdentityMongoDbContext
{
public IMongoCollection<IdentityUser> Users => Collection<IdentityUser>();
public IMongoCollection<IdentityRole> Roles => Collection<IdentityRole>();
//code omitted for brevity
}
Collection Prefix
- Do add static
CollectionPrefix
property to theDbContext
class. Set default value from a constant. Example:
public static string CollectionPrefix { get; set; } = AbpIdentityConsts.DefaultDbTablePrefix;
Used the same constant defined for the EF Core integration table prefix in this example.
- Do always use a short
CollectionPrefix
value for a module to create unique collection names in a shared database.Abp
collection prefix is reserved for ABP core modules.
Collection Mapping
- Do explicitly configure all aggregate roots by overriding the
CreateModel
method of theMongoDbContext
. Example:
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
base.CreateModel(modelBuilder);
modelBuilder.ConfigureIdentity(options =>
{
options.CollectionPrefix = CollectionPrefix;
});
}
- Do not configure model directly in the
CreateModel
method. Instead, create an extension method for theIMongoModelBuilder
. Use ConfigureModuleName as the method name. Example:
public static class AbpIdentityMongoDbContextExtensions
{
public static void ConfigureIdentity(
this IMongoModelBuilder builder,
Action<IdentityMongoModelBuilderConfigurationOptions> optionsAction = null)
{
Check.NotNull(builder, nameof(builder));
var options = new IdentityMongoModelBuilderConfigurationOptions();
optionsAction?.Invoke(options);
builder.Entity<IdentityUser>(b =>
{
b.CollectionName = options.CollectionPrefix + "Users";
});
builder.Entity<IdentityRole>(b =>
{
b.CollectionName = options.CollectionPrefix + "Roles";
});
}
}
- Do create a configuration options class by inheriting from the
MongoModelBuilderConfigurationOptions
. Example:
public class IdentityMongoModelBuilderConfigurationOptions
: MongoModelBuilderConfigurationOptions
{
public IdentityMongoModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix)
{
}
}
Repository Implementation
- Do inherit the repository from the
MongoDbRepository<TMongoDbContext, TEntity, TKey>
class and implement the corresponding repository interface. Example:
public class MongoIdentityUserRepository
: MongoDbRepository<IAbpIdentityMongoDbContext, IdentityUser, Guid>,
IIdentityUserRepository
{
public MongoIdentityUserRepository(
IMongoDbContextProvider<IAbpIdentityMongoDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}
- Do pass the
cancellationToken
to the MongoDB Driver using theGetCancellationToken
helper method. Example:
public async Task<IdentityUser> FindByNormalizedUserNameAsync(
string normalizedUserName,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await GetMongoQueryable()
.FirstOrDefaultAsync(
u => u.NormalizedUserName == normalizedUserName,
GetCancellationToken(cancellationToken)
);
}
GetCancellationToken
fallbacks to the ICancellationTokenProvider.Token
to obtain the cancellation token if it is not provided by the caller code.
- Do ignore the
includeDetails
parameters for the repository implementation since MongoDB loads the aggregate root as a whole (including sub collections) by default. - Do use the
GetMongoQueryable()
method to obtain anIQueryable<TEntity>
to perform queries wherever possible. Because;GetMongoQueryable()
method automatically uses theApplyDataFilters
method to filter the data based on the current data filters (like soft delete and multi-tenancy).- Using
IQueryable<TEntity>
makes the code as much as similar to the EF Core repository implementation and easy to write and read.
- Do implement data filtering if it is not possible to use the
GetMongoQueryable()
method.
Module Class
- Do define a module class for the MongoDB integration package.
- Do add
MongoDbContext
to theIServiceCollection
using theAddMongoDbContext<TMongoDbContext>
method. - Do add implemented repositories to the options for the
AddMongoDbContext<TMongoDbContext>
method. Example:
[DependsOn(
typeof(AbpIdentityDomainModule),
typeof(AbpUsersMongoDbModule)
)]
public class AbpIdentityMongoDbModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddMongoDbContext<AbpIdentityMongoDbContext>(options =>
{
options.AddRepository<IdentityUser, MongoIdentityUserRepository>();
options.AddRepository<IdentityRole, MongoIdentityRoleRepository>();
});
}
}
Notice that this module class also calls the static BsonClassMap
configuration method defined above.