MongoDB Integration
This document offers best practices for implementing MongoDB integration in your modules and applications.
Ensure you've read the MongoDB Integration document first.
General
- Do define a separated MongoDbContextinterface and class for each module.
MongoDbContext Interface
- Do define an interface for the MongoDbContextthat inherits fromIAbpMongoDbContext.
- Do add a ConnectionStringNameattribute to theMongoDbContextinterface.
- Do add IMongoCollection<TEntity>properties to theMongoDbContextinterface 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 MongoDbContextfrom theAbpMongoDbContextclass.
- Do add a ConnectionStringNameattribute to theMongoDbContextclass.
- Do implement the corresponding interfacefor theMongoDbContextclass. 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 CollectionPrefixproperty to theDbContextclass. 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 CollectionPrefixvalue for a module to create unique collection names in a shared database.Abpcollection prefix is reserved for ABP core modules.
Collection Mapping
- Do explicitly configure all aggregate roots by overriding the CreateModelmethod of theMongoDbContext. Example:
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
    base.CreateModel(modelBuilder);
    modelBuilder.ConfigureIdentity();
}
- Do not configure model directly in the  CreateModelmethod. 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));
        builder.Entity<IdentityUser>(b =>
        {
            b.CollectionName = AbpIdentityDbProperties.DbTablePrefix + "Users";
        });
        builder.Entity<IdentityRole>(b =>
        {
            b.CollectionName = AbpIdentityDbProperties.DbTablePrefix + "Roles";
        });
    }
}
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 cancellationTokento the MongoDB Driver using theGetCancellationTokenhelper method. Example:
public async Task<IdentityUser> FindByNormalizedUserNameAsync(
    string normalizedUserName, 
    bool includeDetails = true,
    CancellationToken cancellationToken = default)
{
    return await (await GetQueryableAsync())
        .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 includeDetailsparameters for the repository implementation since MongoDB loads the aggregate root as a whole (including sub collections) by default.
- Do use the GetQueryableAsync()method to obtain anIQueryable<TEntity>to perform queries wherever possible. Because;- GetQueryableAsync()method automatically uses the- ApplyDataFiltersmethod 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 GetQueryableAsync()method.
Module Class
- Do define a module class for the MongoDB integration package.
- Do add MongoDbContextto theIServiceCollectionusing 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.
 
                                             
                                    