using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Luticate2.Auth.Utils.Business.Converters; using Luticate2.Auth.Utils.Business.Converters.ExpressionConverter; using Luticate2.Auth.Utils.Dbo; using Luticate2.Auth.Utils.Dbo.Fields; using Luticate2.Auth.Utils.Dbo.Pagination; using Luticate2.Auth.Utils.Dbo.Result; using Luticate2.Auth.Utils.Interfaces; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; namespace Luticate2.Auth.Utils.DataAccess.Crud { public class LuEfCrudDataAccess : ILuCrud where TModel : class where TDbContext : DbContext where TDbo : class { protected IServiceProvider ServiceProvider { get; } protected ILuConvertersAllocator ConvertersAllocator { get; } protected ILuConvertersTypeConverter ConvertersTypeConverter { get; } public LuEfCrudDataAccess(IServiceProvider serviceProvider) { ServiceProvider = serviceProvider; ConvertersAllocator = ServiceProvider.GetRequiredService(); // TODO Make a DbContext or DataAccess specific ILuConvertersAllocator (in case of multiple DbContext) ConvertersTypeConverter = ServiceProvider.GetRequiredService(); // TODO same as above } protected virtual LuConvertersOptions GetOptions() { return new LuConvertersOptions { Allocator = ConvertersAllocator, Parameters = new Dictionary(), TypeConverter = ConvertersTypeConverter }; } protected virtual LuResult Convert(LuPartialFieldsDbo partialResponse, object dbo, LuConvertersOptions options) where TTypeTo : class { var typeTo = dbo.GetType(); var converterType = typeof(ILuObjectConverter<,>); var gConverterType = converterType.MakeGenericType(typeTo, typeof(TTypeTo)); var converter = (ILuObjectConverter) ServiceProvider.GetService(gConverterType); if (converter == null) { return LuResult.Error(LuStatus.InternalError.ToInt(), $"Could not get service: {nameof(ILuObjectConverter)}<{typeof(TDbo).Name}, {typeof(TTypeTo).Name}>"); } var dstObjResult = options.Allocator.GetInstance(typeof(TTypeTo)); if (!dstObjResult) { return dstObjResult.To(); } var dstObj = dstObjResult.Data; var result = converter.Convert(dbo, dstObj, new LuFieldDbo(), partialResponse, options); return result.Select(o => o as TTypeTo); } protected virtual LuResult> Convert(LuPartialFieldsDbo partialResponse, IEnumerable dbos, LuConvertersOptions options) where TTypeTo : class { var dboList = new List(); foreach (var model in dbos) { var dboResult = Convert(partialResponse, model, options); if (!dboResult) { return dboResult.To>(); } var dbo = dboResult.Data; dboList.Add(dbo); } return LuResult>.Ok(dboList); } protected virtual LuResult HandleError(Exception e) { return LuResult.Error(LuStatus.DbError.ToInt(), e); } protected LuResult Execute(Func, LuResult> action) { try { using (var db = ServiceProvider.GetService()) { return action(db, db.Set()); } } catch (Exception e) { var result = HandleError(e); return result; } } protected virtual LuResult> Include(LuPartialFieldsDbo partialResponse, IQueryable queryable) { return LuResult>.Ok(queryable); } protected virtual LuResult> Filter(LuFilterDbo filter, IQueryable queryable, LuConvertersOptions options) { if (filter.Expression == null) { return LuResult>.Ok(queryable); } var lambdaDbo = filter.Expression as Expression>; var converter = new LuExpressionConverterVisitor(options, ServiceProvider); var lambdaModel = converter.Visit(lambdaDbo) as Expression>;// TODO Handle errors return LuResult>.Ok(queryable.Where(lambdaModel)); } protected virtual LuResult> OrderByField(LuOrderByFieldDbo orderByField, IQueryable queryable, LuConvertersOptions options) { var lambdaDbo = orderByField.Expression as Expression>; var converter = new LuExpressionConverterVisitor(options, ServiceProvider); var lambdaModelNoConvert = converter.Visit(lambdaDbo) as LambdaExpression;// TODO Handle errors var lambdaModel = Expression.Lambda>(lambdaModelNoConvert.Body, lambdaModelNoConvert.Parameters); IOrderedQueryable ordered; if (queryable is IOrderedQueryable orderedQueryable) { if (orderByField.Asc) { ordered = orderedQueryable.ThenBy(lambdaModel); } else { ordered = orderedQueryable.ThenByDescending(lambdaModel); } } else { if (orderByField.Asc) { ordered = queryable.OrderBy(lambdaModel); } else { ordered = queryable.OrderByDescending(lambdaModel); } } return LuResult>.Ok(ordered); } protected virtual LuResult> OrderBy(LuOrderByDbo orderBy, IQueryable queryable, LuConvertersOptions options) { var ordered = queryable; foreach (var field in orderBy.OrderByFields) { var orderedResult = OrderByField(field, ordered, options); if (!orderedResult) { return orderedResult.To>(); } ordered = orderedResult.Data; } return LuResult>.Ok(ordered ?? queryable); } protected virtual LuResult> Paginate(int page, int perPage, IQueryable queryable) { var paginated = queryable.Skip(page * perPage).Take(perPage); return LuResult>.Ok(paginated); } protected virtual LuResult> Paginate(LuPaginatedParamsDbo paginationParams, LuPartialFieldsDbo partialResponse, IQueryable set, LuConvertersOptions options) { var includeResult = Include(partialResponse, set); if (!includeResult) { return includeResult.To>(); } var included = includeResult.Data; var orderByResult = OrderBy(paginationParams.OrderBy, included, options); if (!orderByResult) { return orderByResult.To>(); } var ordered = orderByResult.Data; var filteredResult = Filter(paginationParams.Filter, ordered, options); if (!filteredResult) { return filteredResult.To>(); } var filtered = filteredResult.Data; var count = filtered.Count(); var paginatedResult = Paginate(paginationParams.Page, paginationParams.PerPage, filtered); if (!paginatedResult) { return paginatedResult.To>(); } var paginated = paginatedResult.Data; var paginatedData = new LuPaginatedDbo { Data = paginated.ToList(), Count = count }; return LuResult>.Ok(paginatedData); } public virtual LuResult> Create(LuPartialFieldsDbo partialResponse, LuPartialFieldsDbo partialInput, IEnumerable dbos) { var options = GetOptions(); var addedDbos = new List(); var addedModels = new List(); var createResult = Execute((context, set) => { foreach (var dbo in dbos) { var convertResult = Convert(partialInput, dbo, options); if (!convertResult) { return convertResult.To(); } var model = convertResult.Data; set.Add(model); addedModels.Add(model); } context.SaveChanges(); return LuResult.Ok(true); }); if (!createResult) { return createResult.To>(); } foreach (var addedModel in addedModels) { var convertResult = Convert(partialResponse, addedModel, options); if (!convertResult) { return convertResult.To>(); } addedDbos.Add(convertResult.Data); } return LuResult>.Ok(addedDbos); } public virtual LuResult> Read(LuPartialFieldsDbo partialResponse, LuPaginatedParamsDbo paginationParams) { var result = Execute((context, set) => { var options = GetOptions(); var paginatedResult = Paginate(paginationParams, partialResponse, set, options); if (!paginatedResult) { return paginatedResult.To>(); } var paginated = paginatedResult.Data; var dboListResult = Convert(partialResponse, paginated.Data, options); if (!dboListResult) { return dboListResult.To>(); } var dboList = dboListResult.Data; var paginatedData = new LuPaginatedDbo { Count = paginated.Count, Data = dboList }; return LuResult>.Ok(paginatedData); }); return result; } public virtual LuResult> Update(LuPartialFieldsDbo partialResponse, LuPartialFieldsDbo partialInput, IEnumerable dbos) { throw new NotImplementedException(); } public virtual LuResult> Delete(LuPartialFieldsDbo partialResponse, LuPaginatedParamsDbo paginationParams) { var res = Execute((context, set) => { var options = GetOptions(); var paginatedResult = Paginate(paginationParams, partialResponse, set, options); if (!paginatedResult) { return paginatedResult.To>(); } var paginated = paginatedResult.Data; var dboListResult = Convert(partialResponse, paginated.Data, options); if (!dboListResult) { return dboListResult.To>(); } var dboList = dboListResult.Data; set.RemoveRange(paginated.Data); context.SaveChanges(); var paginatedData = new LuPaginatedDbo { Count = paginated.Count, Data = dboList }; return LuResult>.Ok(paginatedData); }); return res; } } }