Files
parr_api/PARR.DAL/Services/Abstracts/BaseService.cs

287 lines
9.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}
}