MongoDB 集成

  • 推荐 为每个模块定义一个独立的 MongoDbContext 接口与实现类.

MongoDbContext 接口

  • 推荐 定义 MongoDbContext 接口 时继承自 IAbpMongoDbContext.
  • 推荐 添加 ConnectionStringName attributeMongoDbContext 接口.
  • 推荐 只把聚合根做为 IMongoCollection<TEntity> properties 添加到 MongoDbContext 接口. 示例:
  1. [ConnectionStringName("AbpIdentity")]
  2. public interface IAbpIdentityMongoDbContext : IAbpMongoDbContext
  3. {
  4. IMongoCollection<IdentityUser> Users { get; }
  5. IMongoCollection<IdentityRole> Roles { get; }
  6. }

MongoDbContext 类

  • 推荐 MongoDbContext 继承自 AbpMongoDbContext 类.
  • 推荐 添加 ConnectionStringName attributeMongoDbContext 类.
  • 推荐 MongoDbContext 类实现相对应的接口. 示例:
  1. [ConnectionStringName("AbpIdentity")]
  2. public class AbpIdentityMongoDbContext : AbpMongoDbContext, IAbpIdentityMongoDbContext
  3. {
  4. public IMongoCollection<IdentityUser> Users => Collection<IdentityUser>();
  5. public IMongoCollection<IdentityRole> Roles => Collection<IdentityRole>();
  6. //code omitted for brevity
  7. }

Collection 前缀

  • 推荐 添加静态 CollectionPrefix propertyDbContext 类中并使用常量为其设置默认值. 示例:
  1. public static string CollectionPrefix { get; set; } = AbpIdentityConsts.DefaultDbTablePrefix;

在此示例中使用与EF Core集成表前缀相同的常量.

  • 推荐 总是使用简短的 CollectionPrefix 值为模块在共享数据库中创建 unique collection names. Abp collection前缀是为ABP Core模块保留的.

Collection 映射

  • 推荐 通过重写 MongoDbContextCreateModel 方法 配置所有的聚合根 . 示例:
  1. protected override void CreateModel(IMongoModelBuilder modelBuilder)
  2. {
  3. base.CreateModel(modelBuilder);
  4. modelBuilder.ConfigureIdentity(options =>
  5. {
  6. options.CollectionPrefix = CollectionPrefix;
  7. });
  8. }
  • 不推荐 直接在 CreateModel 方法中配置model,而是为 IMongoModelBuilder 定义一个 扩展方法. 使用ConfigureModuleName作为方法名称. 示例:
  1. public static class AbpIdentityMongoDbContextExtensions
  2. {
  3. public static void ConfigureIdentity(
  4. this IMongoModelBuilder builder,
  5. Action<IdentityMongoModelBuilderConfigurationOptions> optionsAction = null)
  6. {
  7. Check.NotNull(builder, nameof(builder));
  8. var options = new IdentityMongoModelBuilderConfigurationOptions();
  9. optionsAction?.Invoke(options);
  10. builder.Entity<IdentityUser>(b =>
  11. {
  12. b.CollectionName = options.CollectionPrefix + "Users";
  13. });
  14. builder.Entity<IdentityRole>(b =>
  15. {
  16. b.CollectionName = options.CollectionPrefix + "Roles";
  17. });
  18. }
  19. }
  • 推荐 通过继承 AbpMongoModelBuilderConfigurationOptions 来创建 configuration Options 类. 示例:
  1. public class IdentityMongoModelBuilderConfigurationOptions
  2. : AbpMongoModelBuilderConfigurationOptions
  3. {
  4. public IdentityMongoModelBuilderConfigurationOptions()
  5. : base(AbpIdentityConsts.DefaultDbTablePrefix)
  6. {
  7. }
  8. }
  • 推荐 创建一个静态方法, 显示地为所有的实体配置 BsonClassMap. 示例:
  1. public static class AbpIdentityBsonClassMap
  2. {
  3. private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
  4. public static void Configure()
  5. {
  6. OneTimeRunner.Run(() =>
  7. {
  8. BsonClassMap.RegisterClassMap<IdentityUser>(map =>
  9. {
  10. map.AutoMap();
  11. map.ConfigureExtraProperties();
  12. });
  13. BsonClassMap.RegisterClassMap<IdentityRole>(map =>
  14. {
  15. map.AutoMap();
  16. });
  17. });
  18. }
  19. }

BsonClassMap 适用于静态方法. 所以只需要在应用程序配置一次实体. OneTimeRunner 以线程安全的方式运行, 并且在应用程序生命周期中只运行一次. 上面代码中的映射确保单元测试可以正确运行. 此代码将由下面的模块类调用.

仓储实现

  • 推荐 仓储 继承自 MongoDbRepository<TMongoDbContext, TEntity, TKey> 类并且实现其相应的接口. 示例:
  1. public class MongoIdentityUserRepository
  2. : MongoDbRepository<IAbpIdentityMongoDbContext, IdentityUser, Guid>,
  3. IIdentityUserRepository
  4. {
  5. public MongoIdentityUserRepository(
  6. IMongoDbContextProvider<IAbpIdentityMongoDbContext> dbContextProvider)
  7. : base(dbContextProvider)
  8. {
  9. }
  10. }
  • 推荐 使用 GetCancellationToken 帮助方法将 cancellationToken 传递给MongoDB驱动程序. 示例:
  1. public async Task<IdentityUser> FindByNormalizedUserNameAsync(
  2. string normalizedUserName,
  3. bool includeDetails = true,
  4. CancellationToken cancellationToken = default)
  5. {
  6. return await GetMongoQueryable()
  7. .FirstOrDefaultAsync(
  8. u => u.NormalizedUserName == normalizedUserName,
  9. GetCancellationToken(cancellationToken)
  10. );
  11. }

如果调用者代码中未提供取消令牌, 则 GetCancellationToken 会从ICancellationTokenProvider.Token 获取取消令牌 GetCancellationToken.

  • 推荐 忽略仓储实现中的 includeDetails 参数, 因为MongoDB在默认情况下将聚合根作为一个整体(包括子集合)加载.
  • 推荐 使用 GetMongoQueryable() 方法获取 IQueryable<TEntity> 以尽可能执行查询use the GetMongoQueryable() method to obtain an IQueryable<TEntity> to perform queries wherever possible. 因为;
    • GetMongoQueryable() 方法在内部使用 ApplyDataFilters 方法根据当前的过滤器 (如 软删除与多租户)过滤数据.
    • 使用IQueryable<TEntity>让代码与EF Core仓储实现类似, 易于使用.
  • 推荐 如果无法使用 GetMongoQueryable() 方法, 则应自行实现数据过滤.

模块类

  • 推荐 为MongoDB集成包定义一个模块类.
  • 推荐 使用 AddMongoDbContext<TMongoDbContext> 方法将 MongoDbContext 添加到 IServiceCollection.
  • 推荐 将已实现的仓储添加到 AddMongoDbContext<TMongoDbContext> 方法options中. 示例:
  1. [DependsOn(
  2. typeof(AbpIdentityDomainModule),
  3. typeof(AbpUsersMongoDbModule)
  4. )]
  5. public class AbpIdentityMongoDbModule : AbpModule
  6. {
  7. public override void ConfigureServices(ServiceConfigurationContext context)
  8. {
  9. AbpIdentityBsonClassMap.Configure();
  10. context.Services.AddMongoDbContext<AbpIdentityMongoDbContext>(options =>
  11. {
  12. options.AddRepository<IdentityUser, MongoIdentityUserRepository>();
  13. options.AddRepository<IdentityRole, MongoIdentityRoleRepository>();
  14. });
  15. }
  16. }

需要注意的是, 模块类还调用上面定义的静态 BsonClassMap 配置方法.