feat(dal): заготовка для хранения истории
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user