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核心模块保留使用。
集合映射
- 务必通过重写
MongoDbContext的CreateModel方法显式配置所有聚合根。示例:
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配置方法。
抠丁客


