From 54f300251d12a6df2a1bfbb70c99417c4cc4a1c7 Mon Sep 17 00:00:00 2001 From: Oleg Shabunov Date: Sat, 20 Apr 2024 22:45:14 +0400 Subject: [PATCH] Dynamic dependency loading --- .../BusinessLogics/BackUpLogic.cs | 105 ++++++++++++++++++ .../AutoWorkshopContracts.csproj | 5 + .../BindingModels/MessageInfoBindingModel.cs | 2 + AutoWorkshopContracts/DI/DependencyManager.cs | 39 +++++++ .../DI/IDependencyContainer.cs | 35 ++++++ .../DI/IImplementationExtension.cs | 9 ++ .../DI/ServiceDependencyContainer.cs | 60 ++++++++++ .../DI/ServiceProviderLoader.cs | 48 ++++++++ .../ViewModels/MessageInfoViewModel.cs | 3 + .../Models/IMessageInfoModel.cs | 2 +- .../Implements/ImplementationExtension.cs | 21 ++++ .../Models/Client.cs | 18 ++- .../Models/Component.cs | 13 ++- .../Models/Implementer.cs | 15 ++- .../Models/MessageInfo.cs | 11 ++ AutoWorkshopDatabaseImplement/Models/Order.cs | 37 +++--- .../Models/Repair.cs | 18 ++- 17 files changed, 407 insertions(+), 34 deletions(-) create mode 100644 AutoWorkshopBusinessLogic/BusinessLogics/BackUpLogic.cs create mode 100644 AutoWorkshopContracts/DI/DependencyManager.cs create mode 100644 AutoWorkshopContracts/DI/IDependencyContainer.cs create mode 100644 AutoWorkshopContracts/DI/IImplementationExtension.cs create mode 100644 AutoWorkshopContracts/DI/ServiceDependencyContainer.cs create mode 100644 AutoWorkshopContracts/DI/ServiceProviderLoader.cs create mode 100644 AutoWorkshopDatabaseImplement/Implements/ImplementationExtension.cs diff --git a/AutoWorkshopBusinessLogic/BusinessLogics/BackUpLogic.cs b/AutoWorkshopBusinessLogic/BusinessLogics/BackUpLogic.cs new file mode 100644 index 0000000..5157ad0 --- /dev/null +++ b/AutoWorkshopBusinessLogic/BusinessLogics/BackUpLogic.cs @@ -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 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(string FolderName) where T : class, new() + { + var Records = _backUpInfo.GetList(); + if (Records == null) + { + _logger.LogWarning("{type} type get null list", typeof(T).Name); + return; + } + + var JsonFormatter = new DataContractJsonSerializer(typeof(List)); + using var fs = new FileStream(string.Format("{0}/{1}.json", FolderName, typeof(T).Name), FileMode.OpenOrCreate); + + JsonFormatter.WriteObject(fs, Records); + } + } +} diff --git a/AutoWorkshopContracts/AutoWorkshopContracts.csproj b/AutoWorkshopContracts/AutoWorkshopContracts.csproj index 7c2e15c..ce24ff2 100644 --- a/AutoWorkshopContracts/AutoWorkshopContracts.csproj +++ b/AutoWorkshopContracts/AutoWorkshopContracts.csproj @@ -6,6 +6,11 @@ enable + + + + + diff --git a/AutoWorkshopContracts/BindingModels/MessageInfoBindingModel.cs b/AutoWorkshopContracts/BindingModels/MessageInfoBindingModel.cs index b1220be..ee2021a 100644 --- a/AutoWorkshopContracts/BindingModels/MessageInfoBindingModel.cs +++ b/AutoWorkshopContracts/BindingModels/MessageInfoBindingModel.cs @@ -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; } diff --git a/AutoWorkshopContracts/DI/DependencyManager.cs b/AutoWorkshopContracts/DI/DependencyManager.cs new file mode 100644 index 0000000..69e702a --- /dev/null +++ b/AutoWorkshopContracts/DI/DependencyManager.cs @@ -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 Configure) => _dependencyManager.AddLogging(Configure); + + public void RegisterType(bool IsSingle = false) where U : class, T where T : class => _dependencyManager.RegisterType(IsSingle); + + public void RegisterType(bool IsSingle = false) where T : class => _dependencyManager.RegisterType(IsSingle); + + public T Resolve() => _dependencyManager.Resolve(); + } +} diff --git a/AutoWorkshopContracts/DI/IDependencyContainer.cs b/AutoWorkshopContracts/DI/IDependencyContainer.cs new file mode 100644 index 0000000..34d4b7d --- /dev/null +++ b/AutoWorkshopContracts/DI/IDependencyContainer.cs @@ -0,0 +1,35 @@ +using Microsoft.Extensions.Logging; + +namespace AutoWorkshopContracts.DI +{ + public interface IDependencyContainer + { + /// + /// Регистрация логгера + /// + /// + void AddLogging(Action Configure); + + /// + /// Добавление зависимости + /// + /// + /// + /// + void RegisterType(bool IsSingle) where U : class, T where T : class; + + /// + /// Добавление зависимости + /// + /// + /// + void RegisterType(bool IsSingle) where T : class; + + /// + /// Получение класса со всеми зависмостями + /// + /// + /// + T Resolve(); + } +} diff --git a/AutoWorkshopContracts/DI/IImplementationExtension.cs b/AutoWorkshopContracts/DI/IImplementationExtension.cs new file mode 100644 index 0000000..a62fe9c --- /dev/null +++ b/AutoWorkshopContracts/DI/IImplementationExtension.cs @@ -0,0 +1,9 @@ +namespace AutoWorkshopContracts.DI +{ + public interface IImplementationExtension + { + public int Priority { get; } + + public void RegisterServices(); + } +} diff --git a/AutoWorkshopContracts/DI/ServiceDependencyContainer.cs b/AutoWorkshopContracts/DI/ServiceDependencyContainer.cs new file mode 100644 index 0000000..2adc2e7 --- /dev/null +++ b/AutoWorkshopContracts/DI/ServiceDependencyContainer.cs @@ -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 Configure) + { + _serviceCollection.AddLogging(Configure); + } + + public void RegisterType(bool IsSingle) where U : class, T where T : class + { + if (IsSingle) + { + _serviceCollection.AddSingleton(); + } + else + { + _serviceCollection.AddTransient(); + } + + _serviceProvider = null; + } + + public void RegisterType(bool IsSingle) where T : class + { + if (IsSingle) + { + _serviceCollection.AddSingleton(); + } + else + { + _serviceCollection.AddTransient(); + } + + _serviceProvider = null; + } + + public T Resolve() + { + if (_serviceProvider == null) + { + _serviceProvider = _serviceCollection.BuildServiceProvider(); + } + + return _serviceProvider.GetService()!; + } + } +} diff --git a/AutoWorkshopContracts/DI/ServiceProviderLoader.cs b/AutoWorkshopContracts/DI/ServiceProviderLoader.cs new file mode 100644 index 0000000..d0738f9 --- /dev/null +++ b/AutoWorkshopContracts/DI/ServiceProviderLoader.cs @@ -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"; + } + } +} diff --git a/AutoWorkshopContracts/ViewModels/MessageInfoViewModel.cs b/AutoWorkshopContracts/ViewModels/MessageInfoViewModel.cs index 2a1371a..5d2ae99 100644 --- a/AutoWorkshopContracts/ViewModels/MessageInfoViewModel.cs +++ b/AutoWorkshopContracts/ViewModels/MessageInfoViewModel.cs @@ -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; diff --git a/AutoWorkshopDataModels/Models/IMessageInfoModel.cs b/AutoWorkshopDataModels/Models/IMessageInfoModel.cs index d7a7c12..331069b 100644 --- a/AutoWorkshopDataModels/Models/IMessageInfoModel.cs +++ b/AutoWorkshopDataModels/Models/IMessageInfoModel.cs @@ -1,6 +1,6 @@ namespace AutoWorkshopDataModels.Models { - public interface IMessageInfoModel + public interface IMessageInfoModel : IId { string MessageId { get; } diff --git a/AutoWorkshopDatabaseImplement/Implements/ImplementationExtension.cs b/AutoWorkshopDatabaseImplement/Implements/ImplementationExtension.cs new file mode 100644 index 0000000..3f15b6d --- /dev/null +++ b/AutoWorkshopDatabaseImplement/Implements/ImplementationExtension.cs @@ -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(); + DependencyManager.Instance.RegisterType(); + DependencyManager.Instance.RegisterType(); + DependencyManager.Instance.RegisterType(); + DependencyManager.Instance.RegisterType(); + DependencyManager.Instance.RegisterType(); + DependencyManager.Instance.RegisterType(); + } + } +} diff --git a/AutoWorkshopDatabaseImplement/Models/Client.cs b/AutoWorkshopDatabaseImplement/Models/Client.cs index 98b44e1..f950e8c 100644 --- a/AutoWorkshopDatabaseImplement/Models/Client.cs +++ b/AutoWorkshopDatabaseImplement/Models/Client.cs @@ -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")] diff --git a/AutoWorkshopDatabaseImplement/Models/Component.cs b/AutoWorkshopDatabaseImplement/Models/Component.cs index bc6ff06..fb8db4b 100644 --- a/AutoWorkshopDatabaseImplement/Models/Component.cs +++ b/AutoWorkshopDatabaseImplement/Models/Component.cs @@ -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")] diff --git a/AutoWorkshopDatabaseImplement/Models/Implementer.cs b/AutoWorkshopDatabaseImplement/Models/Implementer.cs index 1c274e9..63f9779 100644 --- a/AutoWorkshopDatabaseImplement/Models/Implementer.cs +++ b/AutoWorkshopDatabaseImplement/Models/Implementer.cs @@ -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")] diff --git a/AutoWorkshopDatabaseImplement/Models/MessageInfo.cs b/AutoWorkshopDatabaseImplement/Models/MessageInfo.cs index c05855d..5e5f0fe 100644 --- a/AutoWorkshopDatabaseImplement/Models/MessageInfo.cs +++ b/AutoWorkshopDatabaseImplement/Models/MessageInfo.cs @@ -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) diff --git a/AutoWorkshopDatabaseImplement/Models/Order.cs b/AutoWorkshopDatabaseImplement/Models/Order.cs index 7a0b071..c8c7f59 100644 --- a/AutoWorkshopDatabaseImplement/Models/Order.cs +++ b/AutoWorkshopDatabaseImplement/Models/Order.cs @@ -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) { diff --git a/AutoWorkshopDatabaseImplement/Models/Repair.cs b/AutoWorkshopDatabaseImplement/Models/Repair.cs index e4707ba..e5be3c9 100644 --- a/AutoWorkshopDatabaseImplement/Models/Repair.cs +++ b/AutoWorkshopDatabaseImplement/Models/Repair.cs @@ -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? _repairComponents = null; - - [NotMapped] + + [DataMember] + [NotMapped] public Dictionary RepairComponents { get