Removed `IDocReceiverElementWriter` interface and its implementation, replacing it with repository-based operations (`IRepository<DocReceiverElement>`). Updated `CreateEnvelopeCommandHandler` to use repository methods for deleting and creating `DocReceiverElement` records. Cleaned up dependency injection by removing `IDocReceiverElementWriter` registration. Added new database triggers for `DocReceiverElement` in `appsettings.json` and `appsettings.Database.json`. Introduced `SENDER_SAVE_EDIT_MIGRATION_PLAN.md` to outline the migration of the sender save/edit workflow to Blazor Server. This includes splitting save and send actions, supporting draft persistence, and enabling draft editing from the dashboard. Removed unused namespaces and performed code cleanup. Updated testing strategies to ensure correctness and prevent regressions.
218 lines
9.4 KiB
C#
218 lines
9.4 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using DigitalData.Core.Infrastructure;
|
|
using EnvelopeGenerator.Domain.Entities;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Dapper;
|
|
using System.ComponentModel.DataAnnotations.Schema;
|
|
using System.Reflection;
|
|
using DigitalData.UserManager.Domain.Entities;
|
|
#if NET
|
|
using CommandDotNet.Execution;
|
|
using EnvelopeGenerator.Infrastructure.Repositories;
|
|
using EnvelopeGenerator.Infrastructure.Executor;
|
|
using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
|
#elif NETFRAMEWORK
|
|
using System;
|
|
#endif
|
|
|
|
namespace EnvelopeGenerator.Infrastructure
|
|
{
|
|
public static class DependencyInjection
|
|
{
|
|
/// <summary>
|
|
/// Registers the required repositories for the Envelope Generator service to the given <see cref="IServiceCollection"/>.
|
|
/// 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 <see cref="EGDbContext"/>
|
|
/// with the provided database context options if specified.
|
|
/// </summary>
|
|
/// <param name="services">The <see cref="IServiceCollection"/> to which the services are added.</param>
|
|
/// <param name="dbContextOptions">An optional action to configure the <see cref="DbContextOptionsBuilder"/> for the <see cref="EGDbContext"/>.
|
|
/// If not provided, the <see cref="EGDbContext"/> will not be configured.</param>
|
|
/// <returns>The updated <see cref="IServiceCollection"/> with the added repository services.</returns>
|
|
/// <remarks>
|
|
/// 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.
|
|
/// </remarks>
|
|
[Obsolete("Use IRepository")]
|
|
public static IServiceCollection AddEnvelopeGeneratorInfrastructureServices(this IServiceCollection services, Action<Config> options)
|
|
{
|
|
// configure custom options
|
|
options(new Config(services));
|
|
#if NET
|
|
services.TryAddScoped<IConfigRepository, ConfigRepository>();
|
|
services.TryAddScoped<IDocumentReceiverElementRepository, DocumentReceiverElementRepository>();
|
|
services.TryAddScoped<IEnvelopeDocumentRepository, EnvelopeDocumentRepository>();
|
|
services.TryAddScoped<IDocumentReceiverElementRepository, DocumentReceiverElementRepository>();
|
|
services.TryAddScoped<IDocumentStatusRepository, DocumentStatusRepository>();
|
|
services.TryAddScoped<IEmailTemplateRepository, EmailTemplateRepository>();
|
|
services.TryAddScoped<IEnvelopeRepository, EnvelopeRepository>();
|
|
services.TryAddScoped<IEnvelopeHistoryRepository, EnvelopeHistoryRepository>();
|
|
services.TryAddScoped<IEnvelopeReceiverRepository, EnvelopeReceiverRepository>();
|
|
services.TryAddScoped<IEnvelopeTypeRepository, EnvelopeTypeRepository>();
|
|
services.TryAddScoped<IReceiverRepository, ReceiverRepository>();
|
|
services.TryAddScoped<IEnvelopeReceiverReadOnlyRepository, EnvelopeReceiverReadOnlyRepository>();
|
|
|
|
// User Preferences — distributed cache (dbo.TBDD_CACHE)
|
|
services.TryAddScoped<IUserPreferencesService, UserPreferencesService>();
|
|
#endif
|
|
|
|
services.AddDbRepository(opt =>
|
|
{
|
|
// scan EnvelopeGenerator
|
|
opt.RegisterFromAssembly<EGDbContext>(typeof(EnvelopeReceiver).Assembly);
|
|
|
|
// scan UserManager
|
|
opt.RegisterFromAssembly<EGDbContext>(typeof(User).Assembly);
|
|
#if NET
|
|
// scan EmailProfilerDispatcher
|
|
opt.RegisterFromAssembly<EGDbContext>(typeof(EmailOut).Assembly);
|
|
#endif
|
|
});
|
|
|
|
#if NET
|
|
services.AddSQLExecutor<Envelope>();
|
|
services.AddSQLExecutor<Receiver>();
|
|
services.AddSQLExecutor<Document>();
|
|
services.AddSQLExecutor<DocReceiverElement>();
|
|
services.AddSQLExecutor<DocumentStatus>();
|
|
|
|
SetDapperTypeMap<Envelope>();
|
|
SetDapperTypeMap<User>();
|
|
SetDapperTypeMap<Receiver>();
|
|
SetDapperTypeMap<Document>();
|
|
SetDapperTypeMap<DocReceiverElement>();
|
|
SetDapperTypeMap<DocumentStatus>();
|
|
|
|
services.AddScoped<IEnvelopeExecutor, EnvelopeExecutor>();
|
|
services.AddScoped<IEnvelopeReceiverExecutor, EnvelopeReceiverExecutor>();
|
|
services.AddScoped<IDocumentExecutor, DocumentExecutor>();
|
|
#endif
|
|
|
|
return services;
|
|
}
|
|
|
|
#if NET
|
|
public static IServiceCollection AddSQLExecutor(this IServiceCollection services, IConfiguration? configuration = null, Action<SQLExecutorParams>? 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<SQLExecutorParams>(configuration);
|
|
|
|
if (configureOptions is not null)
|
|
services.Configure(configureOptions);
|
|
|
|
return services.AddSingleton<ISQLExecutor, SQLExecutor>();
|
|
}
|
|
|
|
private static void SetDapperTypeMap<TModel>()
|
|
{
|
|
Dapper.SqlMapper.SetTypeMap(typeof(TModel), new CustomPropertyTypeMap(
|
|
typeof(TModel),
|
|
(type, columnName) =>
|
|
{
|
|
return type.GetProperties().FirstOrDefault(prop =>
|
|
{
|
|
var attr = prop.GetCustomAttribute<ColumnAttribute>();
|
|
return attr != null && string.Equals(attr.Name, columnName, StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(prop.Name, columnName, StringComparison.OrdinalIgnoreCase);
|
|
})!;
|
|
}
|
|
));
|
|
}
|
|
|
|
public static IServiceCollection AddSQLExecutor<T>(this IServiceCollection services, IConfiguration? configuration = null, Action<SQLExecutorParams>? 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<SQLExecutorParams>(configuration);
|
|
|
|
services.AddScoped<ISQLExecutor<T>, SQLExecutor<T>>();
|
|
|
|
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<Type>(); }
|
|
})
|
|
.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;
|
|
}
|
|
#endif
|
|
|
|
public class Config
|
|
{
|
|
private readonly IServiceCollection _services;
|
|
|
|
internal Config(IServiceCollection services)
|
|
{
|
|
_services = services;
|
|
}
|
|
|
|
public void AddDbTriggerParams(IConfiguration configuration)
|
|
{
|
|
_services.Configure<DbTriggerParams>(configuration.GetSection(nameof(DbTriggerParams)));
|
|
}
|
|
|
|
public void AddDbTriggerParams(Action<DbTriggerParams> configureOptions)
|
|
{
|
|
_services.Configure(configureOptions);
|
|
}
|
|
|
|
public void AddDbContext(Action<DbContextOptionsBuilder> dbContextOptions)
|
|
{
|
|
_services.AddDbContext<EGDbContext>(dbContextOptions);
|
|
}
|
|
|
|
public void AddDbContext(Action<IServiceProvider, DbContextOptionsBuilder> dbContextOptions)
|
|
{
|
|
_services.AddDbContext<EGDbContext>(dbContextOptions);
|
|
}
|
|
|
|
#if NET
|
|
public void AddSQLExecutor(IConfiguration configuration)
|
|
{
|
|
_services.Configure<SQLExecutorParams>(configuration);
|
|
|
|
_services.AddSingleton<ISQLExecutor, SQLExecutor>();
|
|
|
|
_services.AddSQLExecutor(configuration, null);
|
|
}
|
|
|
|
public void AddSQLExecutor(Action<SQLExecutorParams> options)
|
|
{
|
|
_services.Configure(options);
|
|
|
|
_services.AddSingleton<ISQLExecutor, SQLExecutor>();
|
|
|
|
_services.AddSQLExecutor(null, options);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|