From 2c5bb6fe5d40c2ddd463d5b9abe7dce48a53f9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 11 Dec 2016 20:36:11 +0300 Subject: [PATCH] Add default repositories for EF core. --- .../ConsoleDemo/AbpDeskConsoleDemoModule.cs | 6 +++ .../EntityFrameworkCore/AbpDeskDbContext.cs | 2 + .../AbpDeskDefaultDbContextFactory.cs | 17 +++++++++ .../AbpDeskEntityFrameworkCoreModule.cs | 14 +------ src/AbpDesk/AbpDesk.UI/AppModule.cs | 8 +++- .../AbpEfCoreServiceCollectionExtensions.cs | 30 +++++++++++++++ .../Abp/EntityFrameworkCore/AbpDbContext.cs | 4 +- .../EntityFrameworkCore/DbContextHelper.cs | 23 +++++++++++ .../Volo/Abp/Domain/Entities/EntityHelper.cs | 38 +++++++++++++++++++ .../Abp/Domain/Repositories/IRepository.cs | 3 +- src/Volo.Abp/Volo/Abp/Modularity/AbpModule.cs | 2 + .../Volo/Abp/Reflection/ReflectionHelper.cs | 38 +++++++++++++++++++ ...ServiceCollectionRegistrationExtensions.cs | 1 + .../AbpDesk/AbpDeskApplicationTestModule.cs | 1 + .../AbpDesk/Tickets/TicketAppService_Tests.cs | 2 +- 15 files changed, 172 insertions(+), 17 deletions(-) create mode 100644 src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskDefaultDbContextFactory.cs create mode 100644 src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs create mode 100644 src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DbContextHelper.cs create mode 100644 src/Volo.Abp/Volo/Abp/Domain/Entities/EntityHelper.cs create mode 100644 src/Volo.Abp/Volo/Abp/Reflection/ReflectionHelper.cs diff --git a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/AbpDeskConsoleDemoModule.cs b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/AbpDeskConsoleDemoModule.cs index 757c32070b..320499c609 100644 --- a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/AbpDeskConsoleDemoModule.cs +++ b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/AbpDeskConsoleDemoModule.cs @@ -1,4 +1,5 @@ using AbpDesk.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity; @@ -9,6 +10,11 @@ namespace AbpDesk.ConsoleDemo { public override void ConfigureServices(IServiceCollection services) { + services.AddDbContext(options => + { + options.UseSqlServer("Server=localhost;Database=AbpDesk;Trusted_Connection=True;"); + }); + services.AddAssemblyOf(); } } diff --git a/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskDbContext.cs b/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskDbContext.cs index b8c1fb030a..d1b569a17d 100644 --- a/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskDbContext.cs +++ b/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskDbContext.cs @@ -6,6 +6,8 @@ namespace AbpDesk.EntityFrameworkCore { public class AbpDeskDbContext : AbpDbContext { + public DbSet Tickets { get; set; } + public AbpDeskDbContext(DbContextOptions options) : base(options) { diff --git a/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskDefaultDbContextFactory.cs b/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskDefaultDbContextFactory.cs new file mode 100644 index 0000000000..128d106209 --- /dev/null +++ b/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskDefaultDbContextFactory.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace AbpDesk.EntityFrameworkCore +{ + /* This class is needed for EF Core command line tooling */ + + public class AbpDeskDefaultDbContextFactory : IDbContextFactory + { + public AbpDeskDbContext Create(DbContextFactoryOptions options) + { + var builder = new DbContextOptionsBuilder(); + builder.UseSqlServer("Server=localhost;Database=AbpDesk;Trusted_Connection=True;"); + return new AbpDeskDbContext(builder.Options); + } + } +} diff --git a/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskEntityFrameworkCoreModule.cs b/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskEntityFrameworkCoreModule.cs index cc78785467..7bb6f00b3f 100644 --- a/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskEntityFrameworkCoreModule.cs +++ b/src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskEntityFrameworkCoreModule.cs @@ -1,9 +1,5 @@ -using AbpDesk.Tickets; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Domain.Repositories; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity; -using Volo.Abp.Repositories.EntityFrameworkCore; namespace AbpDesk.EntityFrameworkCore { @@ -13,13 +9,7 @@ namespace AbpDesk.EntityFrameworkCore public override void ConfigureServices(IServiceCollection services) { services.AddAssemblyOf(); - - services.AddTransient, EfCoreRepository>(); - - //services.AddDbContext(options => - //{ - // options.UseSqlServer("Server=localhost;Database=AbpDesk;Trusted_Connection=True;"); - //}); + services.AddDefaultEfCoreRepositories(); } } } diff --git a/src/AbpDesk/AbpDesk.UI/AppModule.cs b/src/AbpDesk/AbpDesk.UI/AppModule.cs index bae32c2290..59ea207782 100644 --- a/src/AbpDesk/AbpDesk.UI/AppModule.cs +++ b/src/AbpDesk/AbpDesk.UI/AppModule.cs @@ -1,6 +1,7 @@ using AbpDesk.EntityFrameworkCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Volo.Abp.AspNetCore.Modularity; @@ -9,13 +10,18 @@ using Volo.Abp.Modularity; namespace AbpDesk { - //TODO: Rename project to AbpDesk.Web.Mvc + //TODO: Rename project to AbpDesk.Web.Mvc & rename to AbpDeskWebAppModule [DependsOn(typeof(AbpAspNetCoreMvcModule), typeof(AbpDeskApplicationModule), typeof(AbpDeskEntityFrameworkCoreModule))] public class AppModule : AbpModule { public override void ConfigureServices(IServiceCollection services) { + services.AddDbContext(options => + { + options.UseSqlServer("Server=localhost;Database=AbpDesk;Trusted_Connection=True;"); + }); + services.AddMvc(); } diff --git a/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs b/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs new file mode 100644 index 0000000000..ad759e16fe --- /dev/null +++ b/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.DependencyInjection.Extensions; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Repositories.EntityFrameworkCore; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class AbpEfCoreServiceCollectionExtensions + { + public static IServiceCollection AddDefaultEfCoreRepositories(this IServiceCollection services) + where TDbContext : AbpDbContext + { + //TODO: Add options to use a provided type as default repository. + + var dbContextType = typeof(TDbContext); + + foreach (var entityType in DbContextHelper.GetEntityTypes(dbContextType)) + { + var primaryKeyType = EntityHelper.GetPrimaryKeyType(entityType); + var repositoryInterfaceType = typeof(IRepository<,>).MakeGenericType(entityType, primaryKeyType); + var repositoryImplementationType = typeof(EfCoreRepository<,,>).MakeGenericType(dbContextType, entityType, primaryKeyType); + + services.TryAddTransient(repositoryInterfaceType, repositoryImplementationType); + } + + return services; + } + } +} diff --git a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index a918f93925..ec65fecf08 100644 --- a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -2,10 +2,10 @@ namespace Volo.Abp.EntityFrameworkCore { - public class AbpDbContext : DbContext + public abstract class AbpDbContext : DbContext where TDbContext : DbContext { - public AbpDbContext(DbContextOptions options) + protected AbpDbContext(DbContextOptions options) : base(options) { diff --git a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DbContextHelper.cs b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DbContextHelper.cs new file mode 100644 index 0000000000..06887c83c8 --- /dev/null +++ b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DbContextHelper.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Reflection; + +namespace Volo.Abp.EntityFrameworkCore +{ + internal static class DbContextHelper + { + public static IEnumerable GetEntityTypes(Type dbContextType) + { + return + from property in dbContextType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance) + where + ReflectionHelper.IsAssignableToGenericType(property.PropertyType, typeof(DbSet<>)) && + ReflectionHelper.IsAssignableToGenericType(property.PropertyType.GenericTypeArguments[0], typeof(IEntity<>)) + select property.PropertyType.GenericTypeArguments[0]; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Domain/Entities/EntityHelper.cs b/src/Volo.Abp/Volo/Abp/Domain/Entities/EntityHelper.cs new file mode 100644 index 0000000000..b1140df9b3 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Domain/Entities/EntityHelper.cs @@ -0,0 +1,38 @@ +using System; +using System.Reflection; +using Volo.Abp.Reflection; + +namespace Volo.Abp.Domain.Entities +{ + /// + /// Some helper methods for entities. + /// + public static class EntityHelper + { + public static bool IsEntity(Type type) + { + return ReflectionHelper.IsAssignableToGenericType(type, typeof (IEntity<>)); + } + + public static Type GetPrimaryKeyType() + { + return GetPrimaryKeyType(typeof (TEntity)); + } + + /// + /// Gets primary key type of given entity type + /// + public static Type GetPrimaryKeyType(Type entityType) + { + foreach (var interfaceType in entityType.GetTypeInfo().GetInterfaces()) + { + if (interfaceType.GetTypeInfo().IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof (IEntity<>)) + { + return interfaceType.GenericTypeArguments[0]; + } + } + + throw new AbpException("Can not find primary key type of given entity type: " + entityType + ". Be sure that this entity type implements " + typeof(IEntity<>).AssemblyQualifiedName); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Domain/Repositories/IRepository.cs b/src/Volo.Abp/Volo/Abp/Domain/Repositories/IRepository.cs index be5b00aed9..8d2b31876e 100644 --- a/src/Volo.Abp/Volo/Abp/Domain/Repositories/IRepository.cs +++ b/src/Volo.Abp/Volo/Abp/Domain/Repositories/IRepository.cs @@ -1,12 +1,13 @@ using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Domain.Entities; +using Volo.DependencyInjection; namespace Volo.Abp.Domain.Repositories { //TODO: Didn't get all members from ABP 1.x - public interface IRepository : IRepositoryMarker + public interface IRepository : IRepositoryMarker, ITransientDependency where TEntity : class, IEntity { /// diff --git a/src/Volo.Abp/Volo/Abp/Modularity/AbpModule.cs b/src/Volo.Abp/Volo/Abp/Modularity/AbpModule.cs index f196ae63f4..8069ba28a8 100644 --- a/src/Volo.Abp/Volo/Abp/Modularity/AbpModule.cs +++ b/src/Volo.Abp/Volo/Abp/Modularity/AbpModule.cs @@ -4,6 +4,8 @@ namespace Volo.Abp.Modularity { public abstract class AbpModule : IAbpModule, IOnApplicationInitialization { + //TODO: Add a OnBeforeConfigureServices method. + public virtual void ConfigureServices(IServiceCollection services) { diff --git a/src/Volo.Abp/Volo/Abp/Reflection/ReflectionHelper.cs b/src/Volo.Abp/Volo/Abp/Reflection/ReflectionHelper.cs new file mode 100644 index 0000000000..c8c47a3534 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Reflection/ReflectionHelper.cs @@ -0,0 +1,38 @@ +using System; +using System.Reflection; + +namespace Volo.Abp.Reflection +{ + public static class ReflectionHelper + { + /// + /// Checks whether implements/inherits . + /// + /// Type to check + /// Generic type + public static bool IsAssignableToGenericType(Type givenType, Type genericType) + { + var givenTypeInfo = givenType.GetTypeInfo(); + + if (givenTypeInfo.IsGenericType && givenType.GetGenericTypeDefinition() == genericType) + { + return true; + } + + foreach (var interfaceType in givenTypeInfo.GetInterfaces()) + { + if (interfaceType.GetTypeInfo().IsGenericType && interfaceType.GetGenericTypeDefinition() == genericType) + { + return true; + } + } + + if (givenTypeInfo.BaseType == null) + { + return false; + } + + return IsAssignableToGenericType(givenTypeInfo.BaseType, genericType); + } + } +} diff --git a/src/Volo.DependencyInjection/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationExtensions.cs b/src/Volo.DependencyInjection/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationExtensions.cs index f66651a1f0..2a9503823a 100644 --- a/src/Volo.DependencyInjection/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationExtensions.cs +++ b/src/Volo.DependencyInjection/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationExtensions.cs @@ -11,6 +11,7 @@ namespace Microsoft.Extensions.DependencyInjection public static class ServiceCollectionRegistrationExtensions { //TODO: Check if assembly/type is added before or add TryAdd versions of them? + //TODO: Return IServiceCollection from all methods public static void AddAssemblyOf(this IServiceCollection services) { diff --git a/test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/AbpDeskApplicationTestModule.cs b/test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/AbpDeskApplicationTestModule.cs index 54577e5c3a..31f153b315 100644 --- a/test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/AbpDeskApplicationTestModule.cs +++ b/test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/AbpDeskApplicationTestModule.cs @@ -11,6 +11,7 @@ namespace AbpDesk public override void ConfigureServices(IServiceCollection services) { services.AddEntityFrameworkInMemoryDatabase(); + services.AddDbContext(options => { options.UseInMemoryDatabase(); diff --git a/test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/Tickets/TicketAppService_Tests.cs b/test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/Tickets/TicketAppService_Tests.cs index 2f0b511529..b2c2896128 100644 --- a/test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/Tickets/TicketAppService_Tests.cs +++ b/test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/Tickets/TicketAppService_Tests.cs @@ -22,7 +22,7 @@ namespace AbpDesk.Tickets //Assert - result.Items.Count.ShouldBeGreaterThan(0); + result.Items.Count.ShouldBe(1); } } }