feat(dal): заготовка для хранения истории

This commit is contained in:
Mikhail Trubnikov
2024-10-07 15:08:46 +10:00
parent 5c03b6bc81
commit 4ec47b1dcb
15 changed files with 7144 additions and 84 deletions

View File

@@ -1,10 +1,14 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.Extensions.Logging;
using PARR.DAL.Context;
using PARR.DAL.DomainModels;
using PARR.DAL.Models;
using PARR.DAL.Models.Base;
using PARR.DAL.Models.Base.History;
using PARR.DAL.Models.Base.History.Base;
using PARR.DAL.Services.Interfaces.Base;
using System.Reflection;
namespace PARR.DAL.Services.Abstracts
{
@@ -38,39 +42,17 @@ namespace PARR.DAL.Services.Abstracts
public async Task<bool> CommitAsync()
{
#region test
////https://stackoverflow.com/questions/53064528/entityframework-core-get-changes-occured-in-entity-and-related-data
//var modifiedEntrities = EntitiContext.ChangeTracker.Entries()
// .Where(t => t.State == EntityState.Modified || t.State == EntityState.Deleted);
var modifiedEntrities = EntitiContext.ChangeTracker.Entries()
.Where(t => t.State == EntityState.Modified/* || t.State == EntityState.Deleted*/);
//foreach (var change in modifiedEntrities)
//{
// ////var type = change.Entity.GetType();
// ////var userType = typeof(User);
// if (change.Entity.GetType() == typeof(User))
// {
// // это пользователь
// var user = change.Entity as User;
// if (user!.Name == "string")
// {
// //user.LastLogon = DateTimeOffset.UtcNow;
// user.Description = "Новый description " + DateTime.Now.Microsecond;
// }
foreach (var obj in modifiedEntrities)
{
// Если нунжо, обновляем дату изменения
DateModifiedResolver(obj);
// }
// // интерфейс IBase?
// //if (change.Entity.GetType().IsAssignableFrom(typeof(IBase)))
// if (change.Entity.GetType().GetInterface(nameof(IBase)) != null)
// {
// (change.Entity as IBase)!.DateModified = DateTimeOffset.UtcNow;
// }
//}
#endregion
//Если нужно, пишем историю
TableHistoryResolver(obj);
}
try
{
@@ -84,6 +66,151 @@ namespace PARR.DAL.Services.Abstracts
}
}
/// <summary>
/// Обновления DateModified у таблиц с IBase
/// </summary>
/// <param name="obj"></param>
private void DateModifiedResolver(EntityEntry obj)
{
if (obj.Entity is IBase)
(obj.Entity as IBase)!.DateModified = DateTimeOffset.UtcNow;
}
/// <summary>
/// При необходимости, записывать историю таблиц
/// </summary>
/// <param name="obj"></param>
private void TableHistoryResolver(EntityEntry obj)
{
var myHistoryInterface = obj.Entity.GetType().GetInterfaces()
.Where(t => t.IsGenericType)
.Where(t => t.GetGenericTypeDefinition() == typeof(IMyHistory<>))
.FirstOrDefault();
// у этого объекта нет интерфейса IMyHistory<>. Не ведем историю
if (myHistoryInterface == null)
return;
// !!! Эта таблица хочет хранить историю !!!
// Получаем тип таблицы где хранится история
var historyType = myHistoryInterface.GetGenericArguments().First();
var historyProps = historyType.GetProperties(/*BindingFlags.DeclaredOnly | */ /*BindingFlags.Public*/).ToList();
var historyInstance = Activator.CreateInstance(historyType);
if (historyInstance == null)
{
logger.LogError($"Не смог создать инстанс для ведения истории {historyType.Name}");
return;
}
// Заполняем поля истории, полями которые есть в исходной таблице
FillHistoryProps(obj, ref historyInstance, historyProps);
// Заполняем поля интерфейса IHistoryTable
// Заполняем Id
// EntitiContext.Add(historyInstance);
}
/// <summary>
/// Заполнить объект истории
/// </summary>
/// <param name="originalObj"></param>
/// <param name="historyInstance"></param>
/// <param name="propsList"></param>
private void FillHistoryProps(EntityEntry originalObj, ref object historyInstance, List<PropertyInfo> propsList)
{
foreach (var prop in propsList)
{
if (originalObj.OriginalValues.Properties.FirstOrDefault(t => t.Name == prop.Name) == null)
continue;
var sourceObjProp = originalObj.Property(prop.Name);
if (sourceObjProp == null)
return;
//Смотрим, если это Id, записываем его в ParentId
var histPropName = prop.Name == nameof(IBase.Id) ? nameof(IHistoryTable.ParentId) : prop.Name;
var histProp = historyInstance.GetType().GetProperty(histPropName);
if (histProp == null) continue;
//сравним типы
if (sourceObjProp.Metadata.PropertyInfo?.PropertyType != histProp.PropertyType)
continue;
var origValues = originalObj.Property(prop.Name).OriginalValue;
histProp.SetValue(historyInstance, origValues);
}
}
//private void SaveHistory(EntityEntry obj)
//{
// // пока просто попробуем для шаблонов
// if (obj.Entity is Template == false)
// return;
// var template = (Template)obj.Entity;
// // https://stackoverflow.com/questions/15012621/how-to-get-original-entity-from-changetracker
// var history = new TemplateHistory();
// // {
// history.Id = Guid.NewGuid();
// history.ParentId = template.Id;
// history.DateAddedToHistory = DateTimeOffset.UtcNow;
// history.InitiatorIp = null;
// history.InitiatorParrComponentId = null;
// history.InitiatorComment = null;
// //---
// //DateModified="",
// //Name ="",
// //IsActiveTemplate = null,
// //IsActiveSchedule = null,
// //LastRun = null,
// //NextRun = null
// // DateModified = template.DateModified,
// // Name = template.Name,
// // IsActiveTemplate = template.IsActiveTemplate,
// // IsActiveSchedule = template.IsActiveSchedule,
// // LastRun = template.LastRun,
// // NextRun = template.NextRun
// //};
// // obj.OriginalValues.GetValue
// //ITemplateBase хочу получить все поля из интерфейса, потом заполнить по ним в хистори
// var interfaceType = typeof(ITemplateGeneralProps);
// foreach (var prop in interfaceType.GetProperties())
// {
// var propName = prop.Name;
// //var propType = prop.PropertyType;
// var originalPropValue = obj.OriginalValues.GetValue<object>(propName);
// var histProp = history.GetType().GetProperty(propName);
// histProp.SetValue(histProp, originalPropValue);
// }
// EntitiContext.Add(history);
//}
public virtual async Task<bool> CreateAsync(T obj)
{
obj.DateCreated = DateTimeOffset.UtcNow;