Dynamic dependency loading

This commit is contained in:
ShabOl 2024-04-20 22:45:14 +04:00
parent d509fd016f
commit 54f300251d
17 changed files with 407 additions and 34 deletions

View File

@ -0,0 +1,105 @@
using AutoWorkshopContracts.BindingModels;
using AutoWorkshopContracts.BusinessLogicContracts;
using AutoWorkshopContracts.StoragesContracts;
using AutoWorkshopDataModels.Models;
using Microsoft.Extensions.Logging;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.Serialization.Json;
namespace AutoWorkshopBusinessLogic.BusinessLogics
{
public class BackUpLogic : IBackUpLogic
{
private readonly ILogger _logger;
private readonly IBackUpInfo _backUpInfo;
public BackUpLogic(ILogger<BackUpLogic> Logger, IBackUpInfo BackUpInfo)
{
_logger = Logger;
_backUpInfo = BackUpInfo;
}
public void CreateBackUp(BackUpSaveBinidngModel Model)
{
if (_backUpInfo == null)
return;
try
{
_logger.LogDebug("Clear folder");
var DirInfo = new DirectoryInfo(Model.FolderName);
if (DirInfo.Exists)
{
foreach (var File in DirInfo.GetFiles())
{
File.Delete();
}
}
_logger.LogDebug("Delete archive");
string FileName = $"{Model.FolderName}.zip";
if (File.Exists(FileName))
File.Delete(FileName);
_logger.LogDebug("Get assembly");
var TypeIId = typeof(IId);
var Assembly = TypeIId.Assembly;
if (Assembly == null)
{
throw new ArgumentNullException("Сборка не найдена", nameof(Assembly));
}
var Types = Assembly.GetTypes();
var Method = GetType().GetMethod("SaveToFile", BindingFlags.NonPublic | BindingFlags.Instance);
_logger.LogDebug("Found {count} types", Types.Length);
foreach (var Type in Types)
{
if (Type.IsInterface && Type.GetInterface(TypeIId.Name) != null)
{
var ModelType = _backUpInfo.GetTypeByModelInterface(Type.Name);
if (ModelType == null)
{
throw new InvalidOperationException($"Не найден класс - модель для { Type.Name }");
}
_logger.LogDebug("Call SaveToFile method for {name} Type", Type.Name);
Method?.MakeGenericMethod(ModelType).Invoke(this, new object[] { Model.FolderName });
}
}
_logger.LogDebug("Create zip and remove folder");
ZipFile.CreateFromDirectory(Model.FolderName, FileName);
DirInfo.Delete(true);
}
catch (Exception)
{
throw;
}
}
private void SaveToFile<T>(string FolderName) where T : class, new()
{
var Records = _backUpInfo.GetList<T>();
if (Records == null)
{
_logger.LogWarning("{type} type get null list", typeof(T).Name);
return;
}
var JsonFormatter = new DataContractJsonSerializer(typeof(List<T>));
using var fs = new FileStream(string.Format("{0}/{1}.json", FolderName, typeof(T).Name), FileMode.OpenOrCreate);
JsonFormatter.WriteObject(fs, Records);
}
}
}

View File

@ -6,6 +6,11 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AutoWorkshopDataModels\AutoWorkshopDataModels.csproj" />
</ItemGroup>

View File

@ -4,6 +4,8 @@ namespace AutoWorkshopContracts.BindingModels
{
public class MessageInfoBindingModel : IMessageInfoModel
{
public int Id { get; set; }
public string MessageId { get; set; } = string.Empty;
public int? ClientId { get; set; }

View File

@ -0,0 +1,39 @@
using Microsoft.Extensions.Logging;
namespace AutoWorkshopContracts.DI
{
public class DependencyManager
{
private readonly IDependencyContainer _dependencyManager;
private static DependencyManager? _manager;
private static readonly object _locjObject = new();
private DependencyManager()
{
_dependencyManager = new ServiceDependencyContainer();
}
public static DependencyManager Instance { get { if (_manager == null) { lock (_locjObject) { _manager = new DependencyManager(); } } return _manager; } }
public static void InitDependency()
{
var Ext = ServiceProviderLoader.GetImplementationExtensions();
if (Ext == null)
{
throw new ArgumentNullException("Отсутствуют компоненты для загрузки зависимостей по модулям");
}
Ext.RegisterServices();
}
public void AddLogging(Action<ILoggingBuilder> Configure) => _dependencyManager.AddLogging(Configure);
public void RegisterType<T, U>(bool IsSingle = false) where U : class, T where T : class => _dependencyManager.RegisterType<T, U>(IsSingle);
public void RegisterType<T>(bool IsSingle = false) where T : class => _dependencyManager.RegisterType<T>(IsSingle);
public T Resolve<T>() => _dependencyManager.Resolve<T>();
}
}

View File

@ -0,0 +1,35 @@
using Microsoft.Extensions.Logging;
namespace AutoWorkshopContracts.DI
{
public interface IDependencyContainer
{
/// <summary>
/// Регистрация логгера
/// </summary>
/// <param name="Configure"></param>
void AddLogging(Action<ILoggingBuilder> Configure);
/// <summary>
/// Добавление зависимости
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="U"></typeparam>
/// <param name="IsSingle"></param>
void RegisterType<T, U>(bool IsSingle) where U : class, T where T : class;
/// <summary>
/// Добавление зависимости
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="IsSingle"></param>
void RegisterType<T>(bool IsSingle) where T : class;
/// <summary>
/// Получение класса со всеми зависмостями
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
T Resolve<T>();
}
}

View File

@ -0,0 +1,9 @@
namespace AutoWorkshopContracts.DI
{
public interface IImplementationExtension
{
public int Priority { get; }
public void RegisterServices();
}
}

View File

@ -0,0 +1,60 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace AutoWorkshopContracts.DI
{
public class ServiceDependencyContainer : IDependencyContainer
{
private ServiceProvider? _serviceProvider;
private readonly ServiceCollection _serviceCollection;
public ServiceDependencyContainer()
{
_serviceCollection = new ServiceCollection();
}
public void AddLogging(Action<ILoggingBuilder> Configure)
{
_serviceCollection.AddLogging(Configure);
}
public void RegisterType<T, U>(bool IsSingle) where U : class, T where T : class
{
if (IsSingle)
{
_serviceCollection.AddSingleton<T, U>();
}
else
{
_serviceCollection.AddTransient<T, U>();
}
_serviceProvider = null;
}
public void RegisterType<T>(bool IsSingle) where T : class
{
if (IsSingle)
{
_serviceCollection.AddSingleton<T>();
}
else
{
_serviceCollection.AddTransient<T>();
}
_serviceProvider = null;
}
public T Resolve<T>()
{
if (_serviceProvider == null)
{
_serviceProvider = _serviceCollection.BuildServiceProvider();
}
return _serviceProvider.GetService<T>()!;
}
}
}

View File

@ -0,0 +1,48 @@
using System.Reflection;
namespace AutoWorkshopContracts.DI
{
public static class ServiceProviderLoader
{
public static IImplementationExtension? GetImplementationExtensions()
{
IImplementationExtension? Source = null;
var Files = Directory.GetFiles(TryGetImplementationExtensionsFolder(), "*.dll", SearchOption.AllDirectories);
foreach (var File in Files.Distinct())
{
Assembly Asm = Assembly.LoadFrom(File);
foreach (var Type in Asm.GetExportedTypes())
{
if (Type.IsClass && typeof(IImplementationExtension).IsAssignableFrom(Type))
{
if (Source == null)
{
Source = (IImplementationExtension)Activator.CreateInstance(Type)!;
}
else
{
var NewSource = (IImplementationExtension)Activator.CreateInstance(Type)!;
if (NewSource.Priority > Source.Priority)
{
Source = NewSource;
}
}
}
}
}
return Source;
}
private static string TryGetImplementationExtensionsFolder()
{
var Directory = new DirectoryInfo(System.IO.Directory.GetCurrentDirectory());
while (Directory != null && !Directory.GetDirectories("ImplementationExtensions", SearchOption.AllDirectories).Any(x => x.Name == "ImplementationExtensions"))
{
Directory = Directory.Parent;
}
return $"{Directory?.FullName}\\ImplementationExtensions";
}
}
}

View File

@ -5,6 +5,9 @@ namespace AutoWorkshopContracts.ViewModels
{
public class MessageInfoViewModel : IMessageInfoModel
{
[Column(Visible: false)]
public int Id { get; set; }
[Column(Visible: false)]
public string MessageId { get; set; } = string.Empty;

View File

@ -1,6 +1,6 @@
namespace AutoWorkshopDataModels.Models
{
public interface IMessageInfoModel
public interface IMessageInfoModel : IId
{
string MessageId { get; }

View File

@ -0,0 +1,21 @@
using AutoWorkshopContracts.DI;
using AutoWorkshopContracts.StoragesContracts;
namespace AutoWorkshopDatabaseImplement.Implements
{
public class ImplementationExtension : IImplementationExtension
{
public int Priority => 3;
public void RegisterServices()
{
DependencyManager.Instance.RegisterType<IClientStorage, ClientStorage>();
DependencyManager.Instance.RegisterType<IComponentStorage, ComponentStorage>();
DependencyManager.Instance.RegisterType<IImplementerStorage, ImplementerStorage>();
DependencyManager.Instance.RegisterType<IMessageInfoStorage, MessageInfoStorage>();
DependencyManager.Instance.RegisterType<IOrderStorage, OrderStorage>();
DependencyManager.Instance.RegisterType<IRepairStorage, RepairStorage>();
DependencyManager.Instance.RegisterType<IBackUpInfo, BackUpInfo>();
}
}
}

View File

@ -3,20 +3,26 @@ using AutoWorkshopContracts.ViewModels;
using AutoWorkshopDataModels.Models;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Runtime.Serialization;
namespace AutoWorkshopDatabaseImplement.Models
{
[DataContract]
public class Client : IClientModel
{
[DataMember]
public int Id { get; private set; }
[Required]
[DataMember]
[Required]
public string ClientFIO { get; private set; } = string.Empty;
[Required]
[DataMember]
[Required]
public string Email { get; set; } = string.Empty;
[Required]
[DataMember]
[Required]
public string Password { get; set; } = string.Empty;
[ForeignKey("ClientId")]

View File

@ -3,17 +3,22 @@ using AutoWorkshopContracts.ViewModels;
using AutoWorkshopDataModels.Models;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Runtime.Serialization;
namespace AutoWorkshopDatabaseImplement.Models
{
[DataContract]
public class Component : IComponentModel
{
[DataMember]
public int Id { get; private set; }
[Required]
[DataMember]
[Required]
public string ComponentName { get; private set; } = string.Empty;
[Required]
[DataMember]
[Required]
public double Cost { get; set; }
[ForeignKey("ComponentId")]

View File

@ -3,23 +3,30 @@ using AutoWorkshopContracts.ViewModels;
using AutoWorkshopDataModels.Models;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Runtime.Serialization;
namespace AutoWorkshopDatabaseImplement.Models
{
[DataContract]
public class Implementer : IImplementerModel
{
[DataMember]
public int Id { get; set; }
[Required]
[DataMember]
[Required]
public string ImplementerFIO { get; set; } = string.Empty;
[Required]
[DataMember]
[Required]
public string Password { get; set; } = string.Empty;
[Required]
[DataMember]
[Required]
public int WorkExperience { get; set; }
[Required]
[DataMember]
[Required]
public int Qualification { get; set; }
[ForeignKey("ImplementerId")]

View File

@ -2,22 +2,33 @@
using AutoWorkshopContracts.ViewModels;
using AutoWorkshopDataModels.Models;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
namespace AutoWorkshopDatabaseImplement.Models
{
[DataContract]
public class MessageInfo : IMessageInfoModel
{
[DataMember]
public int Id { get; set; }
[DataMember]
[Key]
public string MessageId { get; set; } = string.Empty;
[DataMember]
public int? ClientId { get; set; }
[DataMember]
public string SenderName { get; set; } = string.Empty;
[DataMember]
public DateTime DateDelivery { get; set; }
[DataMember]
public string Subject { get; set; } = string.Empty;
[DataMember]
public string Body { get; set; } = string.Empty;
public static MessageInfo? Create(MessageInfoBindingModel? Model)

View File

@ -3,40 +3,51 @@ using AutoWorkshopContracts.ViewModels;
using AutoWorkshopDataModels.Enums;
using AutoWorkshopDataModels.Models;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
namespace AutoWorkshopDatabaseImplement.Models
{
[DataContract]
public class Order : IOrderModel
{
[DataMember]
public int Id { get; private set; }
[Required]
[DataMember]
[Required]
public int RepairId { get; private set; }
public virtual Repair Repair { get; set; }
[Required]
[DataMember]
[Required]
public int ClientId { get; private set; }
public virtual Client Client { get; set; }
public int? ImplementerId { get; private set; }
[DataMember]
public int? ImplementerId { get; private set; }
public virtual Implementer? Implementer { get; set; }
[Required]
[DataMember]
[Required]
public int Count { get; private set; }
[Required]
[DataMember]
[Required]
public double Sum { get; private set; }
[Required]
[DataMember]
[Required]
public OrderStatus Status { get; private set; } = OrderStatus.Undefined;
[Required]
[DataMember]
[Required]
public DateTime DateCreate { get; private set; } = DateTime.Now;
public DateTime? DateImplement { get; private set; }
[DataMember]
public DateTime? DateImplement { get; private set; }
public static Order? Create(OrderBindingModel Model)
{

View File

@ -3,22 +3,28 @@ using AutoWorkshopContracts.ViewModels;
using AutoWorkshopDataModels.Models;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Runtime.Serialization;
namespace AutoWorkshopDatabaseImplement.Models
{
[DataContract]
public class Repair : IRepairModel
{
[DataMember]
public int Id { get; set; }
[Required]
[DataMember]
[Required]
public string RepairName { get; set; } = string.Empty;
[Required]
[DataMember]
[Required]
public double Price { get; set; }
private Dictionary<int, (IComponentModel, int)>? _repairComponents = null;
[NotMapped]
[DataMember]
[NotMapped]
public Dictionary<int, (IComponentModel, int)> RepairComponents
{
get