You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
abp/docs/en/Best-Practices/MongoDB-Integration.md

171 lines
6.1 KiB

## 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:
````C#
[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:
```c#
[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:
```c#
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:
```c#
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
base.CreateModel(modelBuilder);
modelBuilder.ConfigureIdentity(options =>
{
options.CollectionPrefix = CollectionPrefix;
});
}
```
- **Do not** configure model directly in the `CreateModel` method. Instead, create an **extension method** for the `IMongoModelBuilder`. Use Configure*ModuleName* as the method name. Example:
```c#
public static class AbpIdentityMongoDbContextExtensions
{
public static void ConfigureIdentity(
this IMongoModelBuilder builder,
Action<IdentityMongoModelBuilderConfigurationOptions> optionsAction = null)
{
Check.NotNull(builder, nameof(builder));
var options = new IdentityMongoModelBuilderConfigurationOptions();
optionsAction?.Invoke(options);
builder.Entity<IdentityUser>(b =>
{
b.CollectionName = options.CollectionPrefix + "Users";
});
builder.Entity<IdentityRole>(b =>
{
b.CollectionName = options.CollectionPrefix + "Roles";
});
}
}
```
- **Do** create a **configuration options** class by inheriting from the `AbpMongoModelBuilderConfigurationOptions`. Example:
```c#
public class IdentityMongoModelBuilderConfigurationOptions
: AbpMongoModelBuilderConfigurationOptions
{
public IdentityMongoModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix)
{
}
}
```
### Repository Implementation
- **Do** **inherit** the repository from the `MongoDbRepository<TMongoDbContext, TEntity, TKey>` class and implement the corresponding repository interface. Example:
```c#
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:
```c#
public async Task<IdentityUser> FindByNormalizedUserNameAsync(
string normalizedUserName,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await GetMongoQueryable()
.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 `GetMongoQueryable()` method to obtain an `IQueryable<TEntity>` to perform queries wherever possible. Because;
* `GetMongoQueryable()` 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:
```c#
[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.