using DatabaseCore; using ModuleTools.Attributes; using ModuleTools.Extensions; using ModuleTools.Interfaces; using ModuleTools.Models; using SecurityBusinessLogic.BindingModels; using SecurityBusinessLogic.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.Serialization.Json; namespace SecurityDatabaseImplementation.Implementations { /// <summary> /// Реализация IBackupService для сохранения в JSON через JsonContract /// </summary> public class BackupJsonContractService : IBackupService { public OperationResultModel CreateBackUp(BackupBindingModel model) { try { var asm = typeof(DatabaseManager).Assembly; MethodInfo method = GetType().GetTypeInfo().GetDeclaredMethod("SaveToFile"); foreach (var t in asm.GetExportedTypes()) { if (t.IsClass && t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEntitySecurityExtenstion<>)) && t.GetCustomAttribute<EntityDescriptionAttribute>() != null) { MethodInfo generic = method.MakeGenericMethod(t); generic.Invoke(this, new object[] { model.FolderName, model.FullData, t }); } } if (model.CreateArchive) { var fileName = $"{model.FolderName}.zip"; if (File.Exists(fileName)) { File.Delete($"{model.FolderName}.zip"); } ZipFile.CreateFromDirectory(model.FolderName, fileName); } } catch (Exception ex) { return OperationResultModel.Error(ex); } return OperationResultModel.Success(null); } public OperationResultModel RestoreBackUp(BackupBindingModel model) { try { if (model.ArchiveFileName.IsNotEmpty()) { if(model.FolderName.IsEmpty()) { model.FolderName = $"{Path.GetDirectoryName(model.ArchiveFileName)}{Path.GetFileNameWithoutExtension(model.ArchiveFileName)}"; } ZipFile.ExtractToDirectory(model.ArchiveFileName, model.FolderName); } var asm = typeof(DatabaseManager).Assembly; #region вытаскиваем все типы-сущности (они должны быть унаследованы от IEntitySecurityExtenstion и иметь атрибут EntityDescription) List<Type> types = new(); foreach (var t in asm.GetExportedTypes()) { if (t.IsClass && t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEntitySecurityExtenstion<>)) && t.GetCustomAttribute<EntityDescriptionAttribute>() != null) { types.Add(t); } } #endregion #region формиурем зависимости (в каком порядке загружать данные) Dictionary<Type, int> typesWithLevel = new(); while (types.Count > 0) { for (int i = 0; i < types.Count; ++i) { var depends = types[i].GetCustomAttributes<EntityDependencyAttribute>(); if ((depends == null || !depends.Any()) && !typesWithLevel.ContainsKey(types[i])) { typesWithLevel.Add(types[i], 0); types.RemoveAt(i--); continue; } int? level = null; foreach (var depend in depends) { var type = typesWithLevel.Keys.FirstOrDefault(x => x.Name == depend.ClassName); if (type != null) { if (!level.HasValue) { level = typesWithLevel[type]; } else if (level < typesWithLevel[type]) { level = typesWithLevel[type]; } } else { level = null; break; } } if (level.HasValue) { typesWithLevel.Add(types[i], level.Value + 1); types.RemoveAt(i--); continue; } } } #endregion #region Удаляем записи сначала из тех, на которые никкто не ссылается и в последнюю оередь, те, на которые все ссылаются var deleteOrder = typesWithLevel.OrderByDescending(x => x.Value); MethodInfo delMethod = GetType().GetTypeInfo().GetDeclaredMethod("DeleteFromDB"); foreach (var delElem in deleteOrder) { if (File.Exists(string.Format("{0}/{1}.json", model.FolderName, delElem.Key.Name))) { MethodInfo generic = delMethod.MakeGenericMethod(delElem.Key); generic.Invoke(this, null); } } #endregion #region Заполняем в порядке - сначала те, у которых нет родителей, потом их потомство MethodInfo method = GetType().GetTypeInfo().GetDeclaredMethod("LoadFromFile"); foreach (var delElem in typesWithLevel.OrderBy(x => x.Value)) { MethodInfo generic = method.MakeGenericMethod(delElem.Key); generic.Invoke(this, new object[] { model.FolderName, delElem.Key }); } #endregion } catch (Exception ex) { return OperationResultModel.Error(ex); } return OperationResultModel.Success(null); } /// <summary> /// Сохранение списка сущности из БД в файл (вызывается через рефлексию) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="folderName"></param> /// <param name="allowFullData"></param> /// <param name="t"></param> private void SaveToFile<T>(string folderName, bool allowFullData, Type t) where T : class, IEntitySecurityExtenstion<T>, new() { using var context = DatabaseManager.GetContext; var records = context.Set<T>().Select(x => x.SecurityCheck(x, allowFullData)); DataContractJsonSerializer jsonFormatter = new(typeof(List<T>)); using FileStream fs = new(string.Format("{0}/{1}.json", folderName, t.Name), FileMode.OpenOrCreate); jsonFormatter.WriteObject(fs, records); } /// <summary> /// Отчистка записей сущности в БД (вызывается через рефлексию) /// </summary> /// <typeparam name="T"></typeparam> private void DeleteFromDB<T>() where T : class, new() { using var context = DatabaseManager.GetContext; context.Set<T>().RemoveRange(context.Set<T>()); context.SaveChanges(); } /// <summary> /// Загрузка списка сущности из файла в БД (вызывается через рефлексию) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="folderName"></param> /// <param name="t"></param> private void LoadFromFile<T>(string folderName, Type t) where T : class, new() { using var context = DatabaseManager.GetContext; if (File.Exists(string.Format("{0}/{1}.json", folderName, t.Name))) { DataContractJsonSerializer jsonFormatter = new(typeof(List<T>)); using FileStream fs = new(string.Format("{0}/{1}.json", folderName, t.Name), FileMode.Open); List<T> records = (List<T>)jsonFormatter.ReadObject(fs); context.Set<T>().AddRange(records); context.SaveChanges(); } } } }