using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; 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 : LuEfDataAccess, ILuCrudInterface where TModel : class where TDboCreate : class where TDboRead : class where TDboUpdate : class where TDbContext : DbContext { protected LuEfCrudDataAccess(IServiceProvider serviceProvider) : base(serviceProvider) { } protected abstract TModel GetModelFromTCreate(TDboCreate obj); protected abstract void EditModelFromTUpdate(TDboUpdate obj, TModel model); protected abstract TDboRead GetDboFromModel(TModel model); protected virtual Expression> 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>(converted, param); return exp; } protected virtual Expression> GetFilterExpression(LuFilterDbo filter) { return model => true; } protected virtual object GetId(TId id) { var s = id as string; if (s != null) { Guid guid; if (Guid.TryParse(s, out guid)) { return guid; } return null; } return id; } protected virtual object GetModelId(object id) { var g = id as Guid?; if (g != null) { return g.ToString(); } return id; } protected virtual IQueryable GetGetQueryable(TDbContext db, DbSet table) { return table; } protected virtual IQueryable GetEditQueryable(TDbContext db, DbSet table) { return table; } protected virtual IQueryable GetDeleteQueryable(TDbContext db, DbSet table) { return table; } protected virtual Func GetIdFunc() { return x => ((dynamic) x).Id; } protected virtual TModel GetModelFromTUpdate(TDboUpdate obj, TModel model) { EditModelFromTUpdate(obj, model); return model; } protected virtual LuResult GetNotFoundResult() { return LuResult.Error(LuStatus.NotFound, typeof(TModel).Name + ": Value not found", ""); } protected virtual IQueryable GetFilterQuery(LuFilterDbo filter, TDbContext db, DbSet table) { return GetGetQueryable(db, table).Where(GetFilterExpression(filter)); } protected virtual LuResult _Add(TModel model, TDboCreate dbo, TDbContext db, DbSet table) { return LuResult.Ok(true); } public virtual LuResult Add(IEnumerable objs, Func, T> returnFunc) { var models = new List(); var addRes = Execute((db, table) => { var transact = BeginTransaction(db); foreach (var dbo in objs) { var model = GetModelFromTCreate(dbo); table.Add(model); models.Add(model); var res = _Add(model, dbo, db, table); if (!res) { RollbackTransaction(transact); return res.To(); } } db.SaveChanges(); CommitTransaction(transact); return LuResult.Ok(default(T)); }); if (!addRes) { return addRes; } return GetMultiple(models, returnFunc); } public virtual LuResult Add(TDboCreate obj, Func returnFunc) { return Add(new List {obj}, list => returnFunc(list.First())); } public virtual LuResult> AddId(IEnumerable objs) { var func = GetIdFunc(); return Add(objs, list => list.Select(func)); } public virtual LuResult AddId(TDboCreate obj) { return AddId(new List {obj}).To(list => list.First()); } public virtual LuResult> AddDbo(IEnumerable obj) { return Add(obj, read => read); } public virtual LuResult AddDbo(TDboCreate obj) { return AddDbo(new List {obj}).To(list => list.First()); } public virtual LuResult GetSingle(Expression> predicate) { return Execute((db, table) => { var model = GetGetQueryable(db, table).FirstOrDefault(predicate); if (model == default(TModel)) { return GetNotFoundResult(); } var dbo = GetDboFromModel(model); return LuResult.Ok(dbo); }); } public virtual LuResult GetSingleByKeys(params KeyValuePair[] keys) { return GetSingle(GetExpression(keys)); } public virtual LuResult GetSingleById(TId id) { var guid = GetId(id); if (guid == null) { return GetNotFoundResult(); } return GetSingleByKeys(new KeyValuePair("id", guid)); } protected virtual LuResult GetMultiple(IList models, Func, T> returnFunc) { var dbos = new List(); foreach (var model in models) { var getRes = GetSingleById((TId) GetModelId(((dynamic)model).id)); if (!getRes) { return getRes.To(); } dbos.Add(getRes.Data); } var res = returnFunc(dbos); return LuResult.Ok(res); } public virtual LuResult> GetMultiple(Func, IOrderedQueryable> orderBy, Expression> predicate, int page = 0, int perPage = int.MaxValue, params Func, IOrderedQueryable>[] otherOrderBy) { return Execute((db, table) => { var queryable = GetGetQueryable(db, table); var count = queryable.Count(predicate); var ordered = orderBy(queryable); 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 { Count = count, Data = data }; return LuResult>.Ok(result); }); } public virtual LuResult> GetMultiple(Expression> orderBy, Expression> predicate, int page = 0, int perPage = int.MaxValue, params Expression>[] otherOrderBy) { return GetMultiple(table => table.OrderBy(orderBy), predicate, page, perPage, otherOrderBy.Select>, Func, IOrderedQueryable>>(expression => (t => t.ThenBy(expression))).ToArray()); } public virtual LuResult> GetMultiple(Func, IOrderedQueryable> orderBy, int page = 0, int perPage = int.MaxValue, params Func, IOrderedQueryable>[] otherOrderBy) { return GetMultiple(orderBy, x => true, page, perPage, otherOrderBy); } public virtual LuResult> GetMultiple(Expression> orderBy, int page = 0, int perPage = int.MaxValue, params Expression>[] otherOrderBy) { return GetMultiple(orderBy, x => true, page, perPage, otherOrderBy); } public virtual LuResult> GetMultiple(LuOrderByDbo orderBy, Func, IQueryable> getQueryable, int page = 0, int perPage = int.MaxValue) { return Execute((db, table) => { var queryable = getQueryable(db, table); var count = queryable.Count(); IOrderedQueryable ordered = null; foreach (var field in orderBy.Fields) { var exp = GetOrderByFieldExpression(field.Name); if (exp == null) { return LuResult>.Error(LuStatus.InputError, $"LuEfCrudDataAccess: {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 { Count = count, Data = data }; return LuResult>.Ok(result); }); } public virtual LuResult> GetMultiple(LuOrderByDbo orderBy, LuFilterDbo filter, int page = 0, int perPage = int.MaxValue) { return GetMultiple(orderBy, (db, table) => GetFilterQuery(filter, db, table), page, perPage); } public virtual LuResult> GetMultiple(LuOrderByDbo orderBy, int page = 0, int perPage = int.MaxValue) { return GetMultiple(orderBy, GetGetQueryable, page, perPage); } // public virtual LuResult Edit(Expression> predicate, Action update, // Func, T> returnFunc) // { // IList models = null; // var editRes = Execute((db, table) => // { // models = GetEditQueryable(db, table).Where(predicate).ToList(); // foreach (var model in models) // { // update(model); // } // db.SaveChanges(); // return LuResult.Ok(default(T)); // }); // if (!editRes) // { // return editRes; // } // return GetMultiple(models, returnFunc); // } // // public virtual LuResult> EditId(Expression> predicate, Action update) // { // var func = GetIdFunc(); // return Edit(predicate, update, reads => reads.Select(func)); // } // // public virtual LuResult> EditDbo(Expression> predicate, Action update) // { // return Edit(predicate, update, read => read); // } // public virtual LuResult EditSingleById(TId id, Action update, Func returnFunc) // { // var guid = GetId(id); // if (guid == null) // { // return GetNotFoundResult(); // } // return Edit(GetExpression(new KeyValuePair("id", guid)), update, // list => returnFunc(list.FirstOrDefault())); // } // // public virtual LuResult EditSingleByIdId(TId id, Action update) // { // return EditSingleById(id, update, GetIdFunc()); // } // // public virtual LuResult EditSingleByIdDbo(TId id, Action update) // { // return EditSingleById(id, update, read => read); // } protected virtual LuResult _EditSingleById(TModel model, TDboUpdate update, TDbContext db, DbSet table) { return LuResult.Ok(true); } public virtual LuResult EditSingleById(TId id, TDboUpdate update, Func returnFunc) { var guid = GetId(id); if (guid == null) { return GetNotFoundResult(); } var transact = BeginTransaction(null); var editRes = Execute((db, table) => { var model = GetEditQueryable(db, table).FirstOrDefault(GetExpression(new KeyValuePair("id", guid))); EditModelFromTUpdate(update, model); db.SaveChanges(); var res = _EditSingleById(model, update, db, table); if (!res) { RollbackTransaction(transact); return res.To(); } db.SaveChanges(); return LuResult.Ok(default(T)); }); if (!editRes) { RollbackTransaction(transact); return editRes; } var getRes = GetSingleById(id).To(returnFunc); CommitTransaction(transact); return getRes; } public virtual LuResult EditSingleByIdId(TId id, TDboUpdate update) { return EditSingleById(id, update, GetIdFunc()); } public virtual LuResult EditSingleByIdDbo(TId id, TDboUpdate update) { return EditSingleById(id, update, read => read); } public virtual LuResult Delete(Expression> predicate, Func, T> returnFunc) { return Execute((db, table) => { var transact = BeginTransaction(db); var models = GetDeleteQueryable(db, table).Where(predicate).ToList(); var getRes = GetMultiple(models, returnFunc); if (!getRes) { RollbackTransaction(transact); return getRes; } table.RemoveRange(models); db.SaveChanges(); CommitTransaction(transact); return getRes; }); } public virtual LuResult> DeleteId(Expression> predicate) { var func = GetIdFunc(); return Delete(predicate, reads => reads.Select(func)); } public virtual LuResult> DeleteDbo(Expression> predicate) { return Delete(predicate, read => read); } public virtual LuResult DeleteSingleById(TId id, Func returnFunc) { var guid = GetId(id); if (guid == null) { return GetNotFoundResult(); } return Delete(GetExpression(new KeyValuePair("id", guid)), reads => returnFunc(reads.FirstOrDefault())); } public virtual LuResult DeleteSingleByIdId(TId id) { var func = GetIdFunc(); return DeleteSingleById(id, func); } public virtual LuResult DeleteSingleByIdDbo(TId id) { return DeleteSingleById(id, read => read); } } }