using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Luticate2.Utils.DataAccess.Npgsql; using Luticate2.Utils.Dbo.Basic; using Luticate2.Utils.Dbo.Filter; using Luticate2.Utils.Dbo.OrderBy; using Luticate2.Utils.Dbo.Result; using Luticate2.Utils.Interfaces; using Luticate2.Utils.Utils; using Microsoft.EntityFrameworkCore; namespace Luticate2.Utils.DataAccess { public abstract class LuEfCrudDataAccess<TModel, TDboCreate, TDboRead, TDboUpdate, TDbContext> : LuEfDataAccess<TModel, TDbContext>, ILuCrudInterface<TDboCreate, TDboRead, TDboUpdate> where TModel : class where TDboCreate : class where TDboRead : class where TDboUpdate : class where TDbContext : DbContext { protected LuEfCrudDataAccess(TDbContext db, DbSet<TModel> table) : base(db, table) { } protected abstract TModel GetModelFromTCreate(TDboCreate obj); protected abstract void EditModelFromTUpdate(TDboUpdate obj, TModel model); protected abstract TDboRead GetDboFromModel(TModel model); protected virtual Expression<Func<TModel, object>> GetOrderByFieldExpression(string fieldName) { fieldName = fieldName.ToSnakeCase(); if (!typeof(TModel).HasProperty(fieldName)) { return null; } var param = Expression.Parameter(typeof(TModel), "x"); var prop = Expression.Property(param, fieldName); var converted = Expression.Convert(prop, typeof(object)); var exp = Expression.Lambda<Func<TModel, object>>(converted, param); return exp; } protected virtual Expression<Func<TModel, bool>> GetFilterExpression(LuFilterDbo filter) { return model => true; } protected Func<TDboRead, T> GetIdFunc<T>() { return x => ((dynamic) x).Id; } protected TModel GetModelFromTUpdate(TDboUpdate obj, TModel model) { EditModelFromTUpdate(obj, model); return model; } protected Guid? GetGuid(string id) { Guid guid; if (Guid.TryParse(id, out guid)) { return guid; } return null; } protected LuResult<T> GetNotFoundResult<T>() { return LuResult<T>.Error(LuStatus.NotFound, typeof(TModel).Name + ": Value not found", ""); } protected IQueryable<TModel> GetFilterQuery(LuFilterDbo filter) { return Table.Where(GetFilterExpression(filter)); } public LuResult<T> Add<T>(IEnumerable<TDboCreate> objs, Func<IEnumerable<TDboRead>, T> returnFunc) { return Execute(() => { var models = objs.Select(GetModelFromTCreate).ToList(); Table.AddRange(models); Db.SaveChanges(); var dbos = models.Select(GetDboFromModel).ToList(); var res = returnFunc(dbos); return LuResult<T>.Ok(res); }); } public LuResult<T> Add<T>(TDboCreate obj, Func<TDboRead, T> returnFunc) { return Add(new List<TDboCreate> {obj}, list => returnFunc(list.First())); } public LuResult<IEnumerable<string>> AddGuid(IEnumerable<TDboCreate> objs) { var func = GetIdFunc<string>(); return Add(objs, list => list.Select(func)); } public LuResult<string> AddGuid(TDboCreate obj) { return AddGuid(new List<TDboCreate> {obj}).To(list => list.First()); } public LuResult<IEnumerable<long>> AddId(IEnumerable<TDboCreate> obj) { var func = GetIdFunc<long>(); return Add(obj, list => list.Select(func)); } public LuResult<long> AddId(TDboCreate obj) { return AddId(new List<TDboCreate> {obj}).To(list => list.First()); } public LuResult<IEnumerable<TDboRead>> AddDbo(IEnumerable<TDboCreate> obj) { return Add(obj, read => read); } public LuResult<TDboRead> AddDbo(TDboCreate obj) { return AddDbo(new List<TDboCreate> {obj}).To(list => list.First()); } public LuResult<TDboRead> GetSingle(Expression<Func<TModel, bool>> predicate) { return Execute(() => { var model = Table.FirstOrDefault(predicate); if (model == default(TModel)) { return GetNotFoundResult<TDboRead>(); } var dbo = GetDboFromModel(model); return LuResult<TDboRead>.Ok(dbo); }); } public LuResult<TDboRead> GetSingleByKeys(params KeyValuePair<string, object>[] keys) { return GetSingle(GetExpression(keys)); } public LuResult<TDboRead> GetSingleById(string id) { var guid = GetGuid(id); if (guid == null) { return GetNotFoundResult<TDboRead>(); } return GetSingleByKeys(new KeyValuePair<string, object>("id", guid)); } public LuResult<TDboRead> GetSingleById(long id) { return GetSingleByKeys(new KeyValuePair<string, object>("id", id)); } public LuResult<LuPaginatedDbo<TDboRead>> GetMultiple(Func<DbSet<TModel>, IOrderedQueryable<TModel>> orderBy, Expression<Func<TModel, bool>> predicate, int page = 0, int perPage = int.MaxValue, params Func<IOrderedQueryable<TModel>, IOrderedQueryable<TModel>>[] otherOrderBy) { return Execute(() => { var count = Table.Count(predicate); var ordered = orderBy(Table); foreach (var func in otherOrderBy) { ordered = func(ordered); } var data = ordered.Where(predicate).Skip(page * perPage).Take(perPage).Select(GetDboFromModel).ToList(); var result = new LuPaginatedDbo<TDboRead> { Count = count, Data = data }; return LuResult<LuPaginatedDbo<TDboRead>>.Ok(result); }); } public LuResult<LuPaginatedDbo<TDboRead>> GetMultiple(Expression<Func<TModel, object>> orderBy, Expression<Func<TModel, bool>> predicate, int page = 0, int perPage = int.MaxValue, params Expression<Func<TModel, object>>[] otherOrderBy) { return GetMultiple(table => table.OrderBy(orderBy), predicate, page, perPage, otherOrderBy.Select<Expression<Func<TModel, object>>, Func<IOrderedQueryable<TModel>, IOrderedQueryable<TModel>>>(expression => (t => t.ThenBy(expression))).ToArray()); } public LuResult<LuPaginatedDbo<TDboRead>> GetMultiple(Func<DbSet<TModel>, IOrderedQueryable<TModel>> orderBy, int page = 0, int perPage = int.MaxValue, params Func<IOrderedQueryable<TModel>, IOrderedQueryable<TModel>>[] otherOrderBy) { return GetMultiple(orderBy, x => true, page, perPage, otherOrderBy); } public LuResult<LuPaginatedDbo<TDboRead>> GetMultiple(Expression<Func<TModel, object>> orderBy, int page = 0, int perPage = int.MaxValue, params Expression<Func<TModel, object>>[] otherOrderBy) { return GetMultiple(orderBy, x => true, page, perPage, otherOrderBy); } public LuResult<LuPaginatedDbo<TDboRead>> GetMultiple(LuOrderByDbo orderBy, LuFilterDbo filter, int page = 0, int perPage = int.MaxValue) { return Execute(() => { var queryable = GetFilterQuery(filter); var count = queryable.Count(); IOrderedQueryable<TModel> ordered = null; foreach (var field in orderBy.Fields) { var exp = GetOrderByFieldExpression(field.Name); if (exp == null) { return LuResult<LuPaginatedDbo<TDboRead>>.Error(LuStatus.InputError, string.Format("LuEfCrudDataAccess: {0}", field.Name), "Invalid order by field"); } if (ordered != null) { ordered = field.Asc ? ordered.ThenBy(exp) : ordered.ThenByDescending(exp); } else { ordered = field.Asc ? queryable.OrderBy(exp) : queryable.OrderByDescending(exp); } } var data = ordered.Skip(page * perPage).Take(perPage).Select(GetDboFromModel).ToList(); var result = new LuPaginatedDbo<TDboRead> { Count = count, Data = data }; return LuResult<LuPaginatedDbo<TDboRead>>.Ok(result); }); } public LuResult<LuPaginatedDbo<TDboRead>> GetMultiple(LuOrderByDbo orderBy, int page = 0, int perPage = int.MaxValue) { var filter = new LuFilterDbo { Query = "" }; return GetMultiple(orderBy, filter, page, perPage); } public LuResult<T> Edit<T>(Expression<Func<TModel, bool>> predicate, Action<TModel> update, Func<IEnumerable<TDboRead>, T> returnFunc) { return Execute(() => { var models = Table.Where(predicate); var editedDbos = new List<TDboRead>(); foreach (var model in models) { update(model); editedDbos.Add(GetDboFromModel(model)); Db.Entry(model).State = EntityState.Modified; } Db.SaveChanges(); var res = returnFunc(editedDbos); return LuResult<T>.Ok(res); }); } public LuResult<IEnumerable<string>> EditGuid(Expression<Func<TModel, bool>> predicate, Action<TModel> update) { var func = GetIdFunc<string>(); return Edit(predicate, update, reads => reads.Select(func)); } public LuResult<IEnumerable<long>> EditId(Expression<Func<TModel, bool>> predicate, Action<TModel> update) { var func = GetIdFunc<long>(); return Edit(predicate, update, reads => reads.Select(func)); } public LuResult<IEnumerable<TDboRead>> EditDbo(Expression<Func<TModel, bool>> predicate, Action<TModel> update) { return Edit(predicate, update, read => read); } public LuResult<T> EditSingleById<T>(long id, Action<TModel> update, Func<TDboRead, T> returnFunc) { return Edit(GetExpression(new KeyValuePair<string, object>("id", id)), update, list => returnFunc(list.FirstOrDefault())); } public LuResult<long> EditSingleByIdId(long id, Action<TModel> update) { var func = GetIdFunc<long>(); return EditSingleById(id, update, func); } public LuResult<TDboRead> EditSingleByIdDbo(long id, Action<TModel> update) { return EditSingleById(id, update, read => read); } public LuResult<T> EditSingleById<T>(string id, Action<TModel> update, Func<TDboRead, T> returnFunc) { var guid = GetGuid(id); if (guid == null) { return GetNotFoundResult<T>(); } return Edit(GetExpression(new KeyValuePair<string, object>("id", guid)), update, list => returnFunc(list.FirstOrDefault())); } public LuResult<string> EditSingleByIdGuid(string id, Action<TModel> update) { return EditSingleById(id, update, GetIdFunc<string>()); } public LuResult<TDboRead> EditSingleByIdDbo(string id, Action<TModel> update) { return EditSingleById(id, update, read => read); } public LuResult<T> EditSingleById<T>(long id, TDboUpdate update, Func<TDboRead, T> returnFunc) { return Edit(GetExpression(new KeyValuePair<string, object>("id", id)), model => EditModelFromTUpdate(update, model), list => returnFunc(list.FirstOrDefault())); } public LuResult<long> EditSingleByIdId(long id, TDboUpdate update) { return EditSingleById(id, update, GetIdFunc<long>()); } public LuResult<TDboRead> EditSingleByIdDbo(long id, TDboUpdate update) { return EditSingleById(id, update, read => read); } public LuResult<T> EditSingleById<T>(string id, TDboUpdate update, Func<TDboRead, T> returnFunc) { var guid = GetGuid(id); if (guid == null) { return GetNotFoundResult<T>(); } return Edit(GetExpression(new KeyValuePair<string, object>("id", guid)), model => EditModelFromTUpdate(update, model), list => returnFunc(list.FirstOrDefault())); } public LuResult<string> EditSingleByIdGuid(string id, TDboUpdate update) { return EditSingleById(id, update, GetIdFunc<string>()); } public LuResult<TDboRead> EditSingleByIdDbo(string id, TDboUpdate update) { return EditSingleById(id, update, read => read); } public LuResult<T> Delete<T>(Expression<Func<TModel, bool>> predicate, Func<IEnumerable<TDboRead>, T> returnFunc) { return Execute(() => { var models = Table.Where(predicate).ToList(); Table.RemoveRange(models); Db.SaveChanges(); var dbos = models.Select(GetDboFromModel); return LuResult<T>.Ok(returnFunc(dbos)); }); } public LuResult<IEnumerable<string>> DeleteGuid(Expression<Func<TModel, bool>> predicate) { var func = GetIdFunc<string>(); return Delete(predicate, reads => reads.Select(func)); } public LuResult<IEnumerable<long>> DeleteId(Expression<Func<TModel, bool>> predicate) { var func = GetIdFunc<long>(); return Delete(predicate, reads => reads.Select(func)); } public LuResult<IEnumerable<TDboRead>> DeleteDbo(Expression<Func<TModel, bool>> predicate) { return Delete(predicate, read => read); } public LuResult<T> DeleteSingleById<T>(string id, Func<TDboRead, T> returnFunc) { var guid = GetGuid(id); if (guid == null) { return GetNotFoundResult<T>(); } return Delete(GetExpression(new KeyValuePair<string, object>("id", guid)), reads => returnFunc(reads.FirstOrDefault())); } public LuResult<string> DeleteSingleByIdGuid(string id) { var func = GetIdFunc<string>(); return DeleteSingleById(id, func); } public LuResult<TDboRead> DeleteSingleByIdDbo(string id) { return DeleteSingleById(id, read => read); } public LuResult<T> DeleteSingleById<T>(long id, Func<TDboRead, T> returnFunc) { return Delete(GetExpression(new KeyValuePair<string, object>("id", id)), reads => returnFunc(reads.First())); } public LuResult<long> DeleteSingleByIdId(long id) { var func = GetIdFunc<long>(); return DeleteSingleById(id, func); } public LuResult<TDboRead> DeleteSingleByIdDbo(long id) { return DeleteSingleById(id, read => read); } } }