MongoDB Integration

  • Do define a separated MongoDbContext interface and class for each module.

MongoDbContext Interface

  • Do define an interface for the MongoDbContext that inherits from IAbpMongoDbContext.
  • Do add a ConnectionStringName attribute to the MongoDbContext interface.
  • Do add IMongoCollection<TEntity> properties to the MongoDbContext 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 the AbpMongoDbContext class.
  • Do add a ConnectionStringName attribute to the MongoDbContext class.
  • Do implement the corresponding interface for the MongoDbContext 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 the DbContext 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 the MongoDbContext. Example:
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
    base.CreateModel(modelBuilder);

    modelBuilder.ConfigureIdentity();
}
  • Do not configure model directly in the CreateModel method. Instead, create an extension method for the IMongoModelBuilder. 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 cancellationToken to the MongoDB Driver using the GetCancellationToken helper method. Example:
public async Task<IdentityUser> FindByNormalizedUserNameAsync(
    string normalizedUserName, 
    bool includeDetails = true,
    CancellationToken cancellationToken = default)
{
    return await (await GetMongoQueryableAsync())
        .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 GetMongoQueryableAsync() method to obtain an IQueryable<TEntity> to perform queries wherever possible. Because;
    • GetMongoQueryableAsync() method automatically uses the ApplyDataFilters 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 the IServiceCollection using the AddMongoDbContext<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.

In this document
Mastering ABP Framework
Mastering ABP Framework Book

Build maintainable .NET solutions by following software development best practices using ABP.

See Details