diff --git a/docs/Module-Development-Best-Practices.md b/docs/Module-Development-Best-Practices.md index 7116b31cad..b1e3414e7d 100644 --- a/docs/Module-Development-Best-Practices.md +++ b/docs/Module-Development-Best-Practices.md @@ -160,7 +160,7 @@ public static string Schema { get; set; } = AbpIdentityConsts.DefaultDbSchema; ###### Model Mapping -- **Do** explicitly configure all entities by overriding the `OnModelCreating` method of the `DbContext`. Example: +- **Do** explicitly **configure all entities** by overriding the `OnModelCreating` method of the `DbContext`. Example: ````C# protected override void OnModelCreating(ModelBuilder builder) @@ -175,7 +175,7 @@ protected override void OnModelCreating(ModelBuilder builder) } ```` -- **Do not** configure model directly in the `OnModelCreating` method. Instead, create an extension method for `ModelBuilder`. Use Configure*ModuleName* as the method name. Example: +- **Do not** configure model directly in the `OnModelCreating` method. Instead, create an **extension method** for `ModelBuilder`. Use Configure*ModuleName* as the method name. Example: ````C# public static class IdentityDbContextModelBuilderExtensions @@ -206,7 +206,7 @@ public static class IdentityDbContextModelBuilderExtensions } ```` -* Do create a configuration options class by inheriting from the `ModelBuilderConfigurationOptions`. Example: +* **Do** create a **configuration options** class by inheriting from the `ModelBuilderConfigurationOptions`. Example: ````C# public class IdentityModelBuilderConfigurationOptions : ModelBuilderConfigurationOptions @@ -219,12 +219,93 @@ public class IdentityModelBuilderConfigurationOptions : ModelBuilderConfiguratio } ```` +###### Repository Implementation + +- **Do** **inherit** the repository from the `EfCoreRepository` class and implement the corresponding repository interface. Example: + +````C# +public class EfCoreIdentityUserRepository + : EfCoreRepository, IIdentityUserRepository +{ + public EfCoreIdentityUserRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } +} +```` + +* **Do** pass the `cancellationToken` to EF Core using the `GetCancellationToken` helper method. Example: + +````C# +public virtual async Task FindByNormalizedUserNameAsync( + string normalizedUserName, + bool includeDetails = true, + CancellationToken cancellationToken = default) +{ + return await DbSet + .IncludeDetails(includeDetails) + .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** create a `IncludeDetails` **extension method** for the `IQueryable` for each aggregate root which has **sub collections**. Example: + +````C# +public static IQueryable IncludeDetails( + this IQueryable queryable, + bool include = true) +{ + if (!include) + { + return queryable; + } + + return queryable + .Include(x => x.Roles) + .Include(x => x.Logins) + .Include(x => x.Claims) + .Include(x => x.Tokens); +} +```` + +* **Do** use the `IncludeDetails` extension method in the repository methods just like used in the example code above (see FindByNormalizedUserNameAsync). + +- **Do** override `IncludeDetails` method of the repository for aggregates root which have **sub collections**. Example: + +````C# +protected override IQueryable IncludeDetails(IQueryable queryable) +{ + return queryable.IncludeDetails(); //uses the extension method defined above +} +```` + ###### Module Class -Define +- **Do** define a module class for the Entity Framework Core integration package. +- **Do** add `DbContext` to the `IServiceCollection` using the `AddAbpDbContext` method. +- **Do** add implemented repositories to the options for the `AddAbpDbContext` method. Example: -###### Repository Implementation +````C# +[DependsOn(typeof(AbpIdentityDomainModule))] +public class AbpIdentityEntityFrameworkCoreModule : AbpModule +{ + public override void ConfigureServices(IServiceCollection services) + { + services.AddAbpDbContext(options => + { + options.AddRepository(); + options.AddRepository(); + }); -- ​ + services.AddAssemblyOf(); + } +} +```` ##### MongoDB \ No newline at end of file diff --git a/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryRegistrarBase.cs b/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryRegistrarBase.cs index f060969f69..9c7cda7f71 100644 --- a/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryRegistrarBase.cs +++ b/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryRegistrarBase.cs @@ -31,7 +31,7 @@ namespace Volo.Abp.Domain.Repositories protected virtual void RegisterDefaultRepositories(IServiceCollection services) { - foreach (var entityType in GetEntityTypes(Options.DefaultRepositoryDbContextType)) + foreach (var entityType in GetEntityTypes(Options.OriginalDbContextType)) { if (!ShouldRegisterDefaultRepositoryFor(entityType)) { diff --git a/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs b/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs index 1236f17b2f..80cb4e22a6 100644 --- a/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs +++ b/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs @@ -17,14 +17,8 @@ namespace Microsoft.Extensions.DependencyInjection var options = new AbpDbContextRegistrationOptions(typeof(TDbContext)); optionsBuilder?.Invoke(options); - services.TryAddTransient(); services.TryAddTransient(DbContextOptionsFactory.Create); - if (options.DefaultRepositoryDbContextType != typeof(TDbContext)) - { - services.TryAddTransient(options.DefaultRepositoryDbContextType, typeof(TDbContext)); - } - foreach (var dbContextType in options.ReplacedDbContextTypes) { services.Replace(ServiceDescriptor.Transient(dbContextType, typeof(TDbContext))); diff --git a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 80e4ede0d9..cb7bbdf796 100644 --- a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Metadata; using Volo.Abp.Auditing; using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities.Events; using Volo.Abp.Guids; @@ -19,7 +20,7 @@ using Volo.Abp.Reflection; namespace Volo.Abp.EntityFrameworkCore { - public abstract class AbpDbContext : DbContext, IEfCoreDbContext + public abstract class AbpDbContext : DbContext, IEfCoreDbContext, ITransientDependency where TDbContext : DbContext { protected virtual Guid? CurrentTenantId => CurrentTenant?.Id; diff --git a/src/Volo.Abp.MemoryDb/Microsoft/Extensions/DependencyInjection/AbpMemoryDbServiceCollectionExtensions.cs b/src/Volo.Abp.MemoryDb/Microsoft/Extensions/DependencyInjection/AbpMemoryDbServiceCollectionExtensions.cs index cf6f9c7842..2753a0bddd 100644 --- a/src/Volo.Abp.MemoryDb/Microsoft/Extensions/DependencyInjection/AbpMemoryDbServiceCollectionExtensions.cs +++ b/src/Volo.Abp.MemoryDb/Microsoft/Extensions/DependencyInjection/AbpMemoryDbServiceCollectionExtensions.cs @@ -13,8 +13,6 @@ namespace Microsoft.Extensions.DependencyInjection var options = new MemoryDbContextRegistrationOptions(typeof(TMemoryDbContext)); optionsBuilder?.Invoke(options); - services.AddSingleton(); - if (options.DefaultRepositoryDbContextType != typeof(TMemoryDbContext)) { services.TryAddSingleton(options.DefaultRepositoryDbContextType, sp => sp.GetRequiredService()); diff --git a/src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/MemoryDbContext.cs b/src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/MemoryDbContext.cs index 8b7093d41b..c1383b3d70 100644 --- a/src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/MemoryDbContext.cs +++ b/src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/MemoryDbContext.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; +using Volo.Abp.DependencyInjection; namespace Volo.Abp.MemoryDb { - public abstract class MemoryDbContext + public abstract class MemoryDbContext : ISingletonDependency { private static readonly Type[] EmptyTypeList = new Type[0]; diff --git a/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs b/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs index c247e8374d..101b41af3c 100644 --- a/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs +++ b/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs @@ -13,16 +13,9 @@ namespace Microsoft.Extensions.DependencyInjection var options = new MongoDbContextRegistrationOptions(typeof(TMongoDbContext)); optionsBuilder?.Invoke(options); - services.TryAddSingleton(); - - if (options.DefaultRepositoryDbContextType != typeof(TMongoDbContext)) - { - services.TryAddSingleton(options.DefaultRepositoryDbContextType, sp => sp.GetRequiredService()); - } - foreach (var dbContextType in options.ReplacedDbContextTypes) { - services.Replace(ServiceDescriptor.Singleton(dbContextType, sp => sp.GetRequiredService())); + services.Replace(ServiceDescriptor.Transient(dbContextType, typeof(TMongoDbContext))); } new MongoDbRepositoryRegistrar(options) diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs index e3adb5ce95..bfa4b87da1 100644 --- a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; using MongoDB.Driver; +using Volo.Abp.DependencyInjection; namespace Volo.Abp.MongoDB { - public abstract class AbpMongoDbContext : IAbpMongoDbContext + public abstract class AbpMongoDbContext : IAbpMongoDbContext, ITransientDependency { public IMongoModelSource ModelSource { get; set; } diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs b/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs index faf0c89820..e36640de27 100644 --- a/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs +++ b/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs @@ -51,8 +51,7 @@ namespace Volo.Abp.Uow.MongoDB var dbContext = unitOfWork.ServiceProvider.GetRequiredService(); - var abpDbContext = unitOfWork.ServiceProvider.GetRequiredService().ToAbpMongoDbContext(); - abpDbContext.InitializeDatabase(database); + dbContext.ToAbpMongoDbContext().InitializeDatabase(database); return new MongoDbDatabaseApi(dbContext); });