287 lines
9.7 KiB
C#
287 lines
9.7 KiB
C#
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
|
||
{
|
||
internal abstract class BaseService<T> : IBaseService<T> where T : class, IBase
|
||
{
|
||
private readonly ILogger<BaseService<T>> logger;
|
||
|
||
protected abstract DbSet<T> EntitySet { get; }
|
||
protected abstract DataContext EntitiContext { get; }
|
||
|
||
public BaseService(ILogger<BaseService<T>> logger)
|
||
{
|
||
this.logger = logger;
|
||
}
|
||
|
||
public virtual async Task<bool> AddRangeAsync(List<T> objs)
|
||
{
|
||
objs.ForEach(item => item.DateCreated = DateTimeOffset.UtcNow);
|
||
|
||
try
|
||
{
|
||
await EntitySet.AddRangeAsync(objs);
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.LogError(ex, "Ошибка при добавлении диапазона в БД.");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public async Task<bool> CommitAsync()
|
||
{
|
||
var modifiedEntrities = EntitiContext.ChangeTracker.Entries()
|
||
.Where(t => t.State == EntityState.Modified/* || t.State == EntityState.Deleted*/);
|
||
|
||
foreach (var obj in modifiedEntrities)
|
||
{
|
||
// Если нунжо, обновляем дату изменения
|
||
DateModifiedResolver(obj);
|
||
|
||
//Если нужно, пишем историю
|
||
TableHistoryResolver(obj);
|
||
}
|
||
|
||
try
|
||
{
|
||
await EntitiContext.SaveChangesAsync();
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.LogError(ex, "Ошибка при Commit");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <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;
|
||
|
||
try
|
||
{
|
||
await EntitySet.AddAsync(obj);
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.LogError(ex, "Ошибка при добавлении в БД.");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public virtual bool Delete(T obj)
|
||
{
|
||
try
|
||
{
|
||
EntitySet.Remove(obj);
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.LogError(ex, "Ошибка при удалении из БД.");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public virtual async Task<bool> DeleteAsync(Guid id)
|
||
{
|
||
try
|
||
{
|
||
var exist = await GetAsync(id);
|
||
if (exist == null)
|
||
{
|
||
logger.LogError($"Ошибка при удалении из БД. Не найдена запись в БД с id: {id}");
|
||
return false;
|
||
}
|
||
|
||
EntitySet.Remove(exist);
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.LogError(ex, "Ошибка при удалении из БД.");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public virtual IQueryable<T> Get()
|
||
{
|
||
return EntitySet;
|
||
}
|
||
|
||
public virtual async Task<T?> GetAsync(Guid id)
|
||
{
|
||
return await EntitySet.FirstOrDefaultAsync(t => t.Id == id);
|
||
}
|
||
|
||
public virtual IQueryable<T> GetPage(IQueryable<T> query, PaginationFilter paginationFilter)
|
||
{
|
||
//if (paginationFilter.IsDisabledPagination)
|
||
// return query;
|
||
|
||
|
||
int skip = (paginationFilter.PageNumber - 1) * paginationFilter.PageSize;
|
||
|
||
return query.Skip(skip).Take(paginationFilter.PageSize);
|
||
}
|
||
}
|
||
}
|