using EnvelopeGenerator.Application.Contracts.Repositories; using EnvelopeGenerator.Infrastructure.Repositories; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.EntityFrameworkCore; using DigitalData.Core.Infrastructure; using EnvelopeGenerator.Domain.Entities; using DigitalData.Core.Infrastructure.AutoMapper; using EnvelopeGenerator.Application.Contracts.SQLExecutor; using Microsoft.Extensions.Configuration; using EnvelopeGenerator.Infrastructure.Executor; using Dapper; using System.ComponentModel.DataAnnotations.Schema; using System.Reflection; using DigitalData.UserManager.Domain.Entities; namespace EnvelopeGenerator.Infrastructure; public static class DIExtensions { /// /// Registers the required repositories for the Envelope Generator service to the given . /// This method adds the repositories for various envelope-related entities, such as configuration, document receivers, envelopes, and users, /// as scoped services to the dependency injection container. Optionally, it can also configure the /// with the provided database context options if specified. /// /// The to which the services are added. /// An optional action to configure the for the . /// If not provided, the will not be configured. /// The updated with the added repository services. /// /// This method ensures that the repositories are registered as scoped services, meaning that a new instance of each repository /// will be created per HTTP request (or per scope) within the dependency injection container. /// public static IServiceCollection AddEnvelopeGeneratorInfrastructureServices(this IServiceCollection services, Action? dbContextOptions = null, IConfiguration? sqlExecutorConfiguration = null, Action? sqlExecutorConfigureOptions = null) { if(dbContextOptions is not null) services.AddDbContext(dbContextOptions); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.AddDbRepository(context => context.Configs).UseAutoMapper(); services.AddDbRepository(context => context.DocumentReceiverElements).UseAutoMapper(); services.AddDbRepository(context => context.EnvelopeDocument).UseAutoMapper(); services.AddDbRepository(context => context.DocumentStatus).UseAutoMapper(); services.AddDbRepository(context => context.EmailTemplate).UseAutoMapper(); services.AddDbRepository(context => context.Envelopes).UseAutoMapper(); services.AddDbRepository(context => context.EnvelopeCertificates).UseAutoMapper(); services.AddDbRepository(context => context.EnvelopeHistories).UseAutoMapper(); services.AddDbRepository(context => context.EnvelopeReceivers).UseAutoMapper(); services.AddDbRepository(context => context.EnvelopeTypes).UseAutoMapper(); services.AddDbRepository(context => context.Receivers).UseAutoMapper(); services.AddDbRepository(context => context.UserReceivers).UseAutoMapper(); services.AddDbRepository(context => context.EnvelopeReceiverReadOnlys).UseAutoMapper(); services.AddSQLExecutor(); services.AddSQLExecutor(); services.AddSQLExecutor(); services.AddSQLExecutor(); services.AddSQLExecutor(); SetDapperTypeMap(); SetDapperTypeMap(); SetDapperTypeMap(); SetDapperTypeMap(); SetDapperTypeMap(); SetDapperTypeMap(); if (sqlExecutorConfiguration is not null || sqlExecutorConfigureOptions is not null) services.AddSQLExecutor(sqlExecutorConfiguration, sqlExecutorConfigureOptions); return services; } public static IServiceCollection AddSQLExecutor(this IServiceCollection services, IConfiguration? configuration = null, Action? configureOptions = null) { if(configuration is not null && configureOptions is not null) throw new InvalidOperationException("Cannot use both 'configuration' and 'configureOptions'. Only one should be provided."); if (configuration is not null) services.Configure(configuration); if(configureOptions is not null) services.Configure(configureOptions); return services.AddSingleton(); } private static void SetDapperTypeMap() { Dapper.SqlMapper.SetTypeMap(typeof(TModel), new CustomPropertyTypeMap( typeof(TModel), (type, columnName) => { return type.GetProperties().FirstOrDefault(prop => { var attr = prop.GetCustomAttribute(); return attr != null && string.Equals(attr.Name, columnName, StringComparison.OrdinalIgnoreCase) || string.Equals(prop.Name, columnName, StringComparison.OrdinalIgnoreCase); })!; } )); } public static IServiceCollection AddSQLExecutor(this IServiceCollection services, IConfiguration? configuration = null, Action? configureOptions = null) where T : class { if (configuration is not null && configureOptions is not null) throw new InvalidOperationException("Cannot use both 'configuration' and 'configureOptions'. Only one should be provided."); if (configuration is not null) services.Configure(configuration); services.AddScoped, SQLExecutor>(); var interfaceType = typeof(ISQL<>); var targetGenericType = interfaceType.MakeGenericType(typeof(T)); var implementations = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => { try { return a.GetTypes(); } catch { return Array.Empty(); } }) .Where(t => t is { IsClass: true, IsAbstract: false } && t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType && i.GenericTypeArguments[0] == typeof(T) ) ); foreach (var impl in implementations) { services.AddSingleton(impl); } return services; } }