using DigitalData.Core.Abstraction.Application.Repository; using DigitalData.Core.Infrastructure.Factory; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using System.ComponentModel.DataAnnotations.Schema; using System.Reflection; namespace DigitalData.Core.Infrastructure; public static class DependencyInjection { public static IServiceCollection AddDbRepository(this IServiceCollection services, Action options) { // register services from configuration var cfg = new RepositoryConfiguration(); options.Invoke(cfg); cfg.RegisterAllServices(services); // register db repository services.AddSingleton(); // register db repository factory services.AddSingleton(); return services; } public class RepositoryConfiguration { // 1. register from assembly private readonly Queue> RegsFromAssembly = new(); // 2. register entities (can overwrite) private readonly Queue> RegsEntity = new(); // 3. register db set factories (can overwrite) private readonly Queue> RegsDbSetFactory = new(); internal void RegisterAllServices(IServiceCollection services) { // 1. register from assembly RegsFromAssembly.InvokeAll(services); // 2. register entities (can overwrite) RegsEntity.InvokeAll(services); // 3. register db set factories (can overwrite) RegsDbSetFactory.InvokeAll(services); } internal RepositoryConfiguration() { } public void RegisterFromAssembly(Assembly assembly) where TDbContext : DbContext { void reg(IServiceCollection services) { // scan all types in the Assembly var entityTypes = assembly.GetTypes() .Where(t => t.IsClass && !t.IsAbstract && t.GetCustomAttribute() != null); foreach (var entityType in entityTypes) { #region Repository /// register repository // create generic DbRepository type var repositoryType = typeof(DbRepository<,>).MakeGenericType(typeof(TDbContext), entityType); var interfaceType = typeof(IRepository<>).MakeGenericType(entityType); // add into DI container as Scoped services.AddScoped(interfaceType, repositoryType); #endregion Repository #region DbSetFactory /// register DbSetFactory var addDbSetFactoryMethod = typeof(DependencyInjection) .GetMethod(nameof(AddDbSetFactory), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); var genericMethod = addDbSetFactoryMethod!.MakeGenericMethod(typeof(TDbContext), entityType); genericMethod.Invoke(null, new [] { services, null }); #endregion DbSetFactory } } RegsFromAssembly.Enqueue(reg); } public void RegisterEntity(Func>? dbSetFactory = null) where TDbContext : DbContext where TEntity : class { void reg(IServiceCollection services) => services .AddScoped, DbRepository>() .AddDbSetFactory(dbSetFactory); RegsEntity.Enqueue(reg); } public void RegisterDbSetFactory(Func> dbSetFactory) where TDbContext : DbContext where TEntity : class => RegsDbSetFactory.Enqueue(s => s.AddDbSetFactory(dbSetFactory)); } private static void InvokeAll(this Queue> queue, T services) { while (queue.Count > 0) queue.Dequeue().Invoke(services); } internal static IServiceCollection AddDbSetFactory(this IServiceCollection services, Func>? create = null) where TDbContext : DbContext where TEntity : class { create ??= ctx => ctx.Set(); services.AddSingleton(_ => new DbSetFactory(create)); return services; } }