## 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` **properties** to the `MongoDbContext` interface only for the aggregate roots. Example: ````C# [ConnectionStringName("AbpIdentity")] public interface IAbpIdentityMongoDbContext : IAbpMongoDbContext { IMongoCollection Users { get; } IMongoCollection 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 Users => Collection(); public IMongoCollection Roles => Collection(); //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(); } ``` - **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 optionsAction = null) { Check.NotNull(builder, nameof(builder)); builder.Entity(b => { b.CollectionName = AbpIdentityDbProperties.DbTablePrefix + "Users"; }); builder.Entity(b => { b.CollectionName = AbpIdentityDbProperties.DbTablePrefix + "Roles"; }); } } ``` ### Repository Implementation - **Do** **inherit** the repository from the `MongoDbRepository` class and implement the corresponding repository interface. Example: ```c# public class MongoIdentityUserRepository : MongoDbRepository, IIdentityUserRepository { public MongoIdentityUserRepository( IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) { } } ``` - **Do** pass the `cancellationToken` to the MongoDB Driver using the `GetCancellationToken` helper method. Example: ```c# public async Task 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` 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` 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` method. - **Do** add implemented repositories to the options for the `AddMongoDbContext` method. Example: ```c# [DependsOn( typeof(AbpIdentityDomainModule), typeof(AbpUsersMongoDbModule) )] public class AbpIdentityMongoDbModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddMongoDbContext(options => { options.AddRepository(); options.AddRepository(); }); } } ``` Notice that this module class also calls the static `BsonClassMap` configuration method defined above.