DepartmentProject/DepartmentPortal/Security/SecurityDatabaseImplementation/Implementations/BackupJsonContractService.cs

201 lines
6.9 KiB
C#
Raw Normal View History

2022-03-18 22:55:48 +04:00
using CoreDatabase;
using SecurityContract.BindingModels;
using SecurityContract.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;
using ToolsModule.Attributes;
using ToolsModule.Extensions;
using ToolsModule.Interfaces;
using ToolsModule.Models;
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();
}
}
}
}