项目

MongoDB 集成指南

本文档提供了在模块和应用程序中实现MongoDB集成的最佳实践。

请确保您已首先阅读 MongoDB集成 文档。

通用原则

  • 务必为每个模块定义独立的MongoDbContext接口和类。

MongoDbContext接口

  • 务必MongoDbContext定义一个继承自IAbpMongoDbContext接口
  • 务必MongoDbContext接口添加ConnectionStringName特性
  • 务必仅在MongoDbContext接口中为聚合根添加IMongoCollection<TEntity>属性。示例:
[ConnectionStringName("AbpIdentity")]
public interface IAbpIdentityMongoDbContext : IAbpMongoDbContext
{
    IMongoCollection<IdentityUser> Users { get; }
    IMongoCollection<IdentityRole> Roles { get; }
}

MongoDbContext类

  • 务必MongoDbContext继承自AbpMongoDbContext类。
  • 务必MongoDbContext类添加ConnectionStringName特性。
  • 务必MongoDbContext类实现对应的接口。示例:
[ConnectionStringName("AbpIdentity")]
public class AbpIdentityMongoDbContext : AbpMongoDbContext, IAbpIdentityMongoDbContext
{
    public IMongoCollection<IdentityUser> Users => Collection<IdentityUser>();
    public IMongoCollection<IdentityRole> Roles => Collection<IdentityRole>();

    //为简洁起见省略部分代码
}

集合前缀

  • 务必DbContext类中添加静态CollectionPrefix属性。使用常量设置默认值。示例:
public static string CollectionPrefix { get; set; } = AbpIdentityConsts.DefaultDbTablePrefix;

此示例使用了为EF Core集成表前缀定义的相同常量。

  • 务必为模块使用简短的CollectionPrefix值,以便在共享数据库中创建唯一集合名称Abp集合前缀为ABP核心模块保留使用。

集合映射

  • 务必通过重写MongoDbContextCreateModel方法显式配置所有聚合根。示例:
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
    base.CreateModel(modelBuilder);

    modelBuilder.ConfigureIdentity();
}
  • 切勿CreateModel方法中直接配置模型。相反,应为IMongoModelBuilder创建扩展方法。使用Configure模块名作为方法名称。示例:
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";
        });
    }
}

仓储实现

  • 务必让仓储继承自MongoDbRepository<TMongoDbContext, TEntity, TKey>类并实现对应的仓储接口。示例:
public class MongoIdentityUserRepository
    : MongoDbRepository<IAbpIdentityMongoDbContext, IdentityUser, Guid>,
      IIdentityUserRepository
{
    public MongoIdentityUserRepository(
        IMongoDbContextProvider<IAbpIdentityMongoDbContext> dbContextProvider) 
        : base(dbContextProvider)
    {
    }
}
  • 务必使用GetCancellationToken辅助方法将cancellationToken传递给MongoDB驱动。示例:
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会回退到ICancellationTokenProvider.Token来获取取消令牌。

  • 务必忽略仓储实现中的includeDetails参数,因为MongoDB默认会完整加载聚合根(包括子集合)。
  • 务必尽可能使用GetQueryableAsync()方法获取IQueryable<TEntity>来执行查询。因为:
    • GetQueryableAsync()方法会自动使用ApplyDataFilters方法根据当前数据过滤器(如软删除和多租户)过滤数据。
    • 使用IQueryable<TEntity>可使代码与EF Core仓储实现尽可能相似,易于编写和阅读。
  • 务必在无法使用GetQueryableAsync()方法时实现数据过滤。

模块类

  • 务必为MongoDB集成包定义一个模块类。
  • 务必使用AddMongoDbContext<TMongoDbContext>方法将MongoDbContext添加到IServiceCollection
  • 务必AddMongoDbContext<TMongoDbContext>方法的选项添加已实现的仓储。示例:
[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>();
        });
    }
}

请注意,此模块类还会调用上面定义的静态BsonClassMap配置方法。

另请参阅

在本文档中