通用EF框架
之前我老大去网上找了一个DAL里面操作数据库的通用类:
public class DALHelper { public static List<T> Search<T>() where T : SH_SetBase { using (var db = new ShopContext()) { var dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { return dbSet.Where(o => !o.IsDelete).ToList(); } return dbSet.ToList(); } } public static List<T> Search<T>(Expression<Func<T, bool>> where) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { Expression<Func<T, bool>> where2 = (o => (o as SH_SetBase).IsDelete == false); var invokedExpr = Expression.Invoke(where2, where.Parameters.Cast<Expression>()); Expression<Func<T, bool>> where3 = Expression.Lambda<Func<T, bool>>(Expression.And(where.Body, invokedExpr), where.Parameters); return dbSet.Where(where3.Compile()).ToList(); } return dbSet.Where(where).ToList(); } } public static List<T> Search<T>(Expression<Func<T, bool>> where, PageContent pageContent) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { Expression<Func<T, bool>> where2 = (o => (o as SH_SetBase).IsDelete == false); var invokedExpr = Expression.Invoke(where2, where.Parameters.Cast<Expression>()); Expression<Func<T, bool>> where3 = Expression.Lambda<Func<T, bool>>(Expression.And(where.Body, invokedExpr), where.Parameters); where = where3; } pageContent.TotalLogs = Count<T>(where); return dbSet.Where(where.Compile()).Skip((pageContent.PageIndex - 1) * pageContent.PageSize).Take(pageContent.PageSize).ToList(); } } public static List<T> Search<T>(Expression<Func<T, object>> include, Expression<Func<T, bool>> where) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { Expression<Func<T, bool>> where2 = (o => (o as SH_SetBase).IsDelete == false); var invokedExpr = Expression.Invoke(where2, where.Parameters.Cast<Expression>()); Expression<Func<T, bool>> where3 = Expression.Lambda<Func<T, bool>>(Expression.And(where.Body, invokedExpr), where.Parameters); return dbSet.Include(include).Where(where3.Compile()).ToList(); } return dbSet.Include(include).Where(where).ToList(); } } public static List<T> Search<T>(Expression<Func<T, object>> include, Expression<Func<T, bool>> where, PageContent pageContent) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { Expression<Func<T, bool>> where2 = (o => (o as SH_SetBase).IsDelete == false); var invokedExpr = Expression.Invoke(where2, where.Parameters.Cast<Expression>()); Expression<Func<T, bool>> where3 = Expression.Lambda<Func<T, bool>>(Expression.And(where.Body, invokedExpr), where.Parameters); where = where3; } pageContent.TotalLogs = Count<T>(where); return dbSet.Include(include).Where(where.Compile()).Skip((pageContent.PageIndex - 1) * pageContent.PageSize).Take(pageContent.PageSize).ToList(); } } public static List<T> Search<T>(Expression<Func<T, bool>> where, PageContent pageContent, Expression<Func<T, object>> order, bool isAsc) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { Expression<Func<T, bool>> where2 = (o => (o as SH_SetBase).IsDelete == false); var invokedExpr = Expression.Invoke(where2, where.Parameters.Cast<Expression>()); Expression<Func<T, bool>> where3 = Expression.Lambda<Func<T, bool>>(Expression.And(where.Body, invokedExpr), where.Parameters); where = where3; } pageContent.TotalLogs = Count<T>(where); if (isAsc) return dbSet.Where(where.Compile()).OrderBy(order.Compile()).Skip((pageContent.PageIndex - 1) * pageContent.PageSize).Take(pageContent.PageSize).ToList(); else return dbSet.Where(where.Compile()).OrderByDescending(order.Compile()).Skip((pageContent.PageIndex - 1) * pageContent.PageSize).Take(pageContent.PageSize).ToList(); } } public static List<T> Search<T>(Expression<Func<T, object>> include, Expression<Func<T, bool>> where, PageContent pageContent, Expression<Func<T, object>> order, bool isAsc) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { Expression<Func<T, bool>> where2 = (o => (o as SH_SetBase).IsDelete == false); var invokedExpr = Expression.Invoke(where2, where.Parameters.Cast<Expression>()); Expression<Func<T, bool>> where3 = Expression.Lambda<Func<T, bool>>(Expression.And(where.Body, invokedExpr), where.Parameters); where = where3; } pageContent.TotalLogs = Count<T>(where); if (isAsc) return dbSet.Include(include).Where(where.Compile()).OrderBy(order.Compile()).Skip((pageContent.PageIndex - 1) * pageContent.PageSize).Take(pageContent.PageSize).ToList(); else return dbSet.Include(include).Where(where.Compile()).OrderByDescending(order.Compile()).Skip((pageContent.PageIndex - 1) * pageContent.PageSize).Take(pageContent.PageSize).ToList(); } } public static List<T> Search<T>(Expression<Func<T, object>> path1, Expression<Func<T, object>> path2, Expression<Func<T, bool>> where, PageContent pageContent, Expression<Func<T, object>> order, bool isAsc) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { Expression<Func<T, bool>> where2 = (o => (o as SH_SetBase).IsDelete == false); var invokedExpr = Expression.Invoke(where2, where.Parameters.Cast<Expression>()); Expression<Func<T, bool>> where3 = Expression.Lambda<Func<T, bool>>(Expression.And(where.Body, invokedExpr), where.Parameters); where = where3; } pageContent.TotalLogs = Count<T>(where); if (isAsc) return dbSet.Include(path1).Include(path2).Where(where.Compile()).OrderBy(order.Compile()).Skip((pageContent.PageIndex - 1) * pageContent.PageSize).Take(pageContent.PageSize).ToList(); else return dbSet.Include(path1).Include(path2).Where(where.Compile()).OrderByDescending(order.Compile()).Skip((pageContent.PageIndex - 1) * pageContent.PageSize).Take(pageContent.PageSize).ToList(); } } public static bool Exist<T>(Expression<Func<T, bool>> where) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { Expression<Func<T, bool>> where2 = (o => (o as SH_SetBase).IsDelete == false); var invokedExpr = Expression.Invoke(where2, where.Parameters.Cast<Expression>()); Expression<Func<T, bool>> where3 = Expression.Lambda<Func<T, bool>>(Expression.And(where.Body, invokedExpr), where.Parameters); return dbSet.FirstOrDefault(where3.Compile()) != null; } return dbSet.FirstOrDefault(where.Compile()) != null; } } public static int Count<T>(Expression<Func<T, bool>> where) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { Expression<Func<T, bool>> where2 = (o => (o as SH_SetBase).IsDelete == false); var invokedExpr = Expression.Invoke(where2, where.Parameters.Cast<Expression>()); Expression<Func<T, bool>> where3 = Expression.Lambda<Func<T, bool>>(Expression.And(where.Body, invokedExpr), where.Parameters); return dbSet.Count(where3.Compile()); } return dbSet.Count(where); } } public static decimal Sum<T>(Expression<Func<T, bool>> where, Expression<Func<T, decimal>> selector) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { Expression<Func<T, bool>> where2 = (o => (o as SH_SetBase).IsDelete == false); var invokedExpr = Expression.Invoke(where2, where.Parameters.Cast<Expression>()); Expression<Func<T, bool>> where3 = Expression.Lambda<Func<T, bool>>(Expression.And(where.Body, invokedExpr), where.Parameters); return dbSet.Where(where3.Compile()).Sum(selector.Compile()); } return dbSet.Where(where.Compile()).Sum(selector.Compile()); } } public static int Sum<T>(Expression<Func<T, bool>> where, Expression<Func<T, int>> selector) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { Expression<Func<T, bool>> where2 = (o => (o as SH_SetBase).IsDelete == false); var invokedExpr = Expression.Invoke(where2, where.Parameters.Cast<Expression>()); Expression<Func<T, bool>> where3 = Expression.Lambda<Func<T, bool>>(Expression.And(where.Body, invokedExpr), where.Parameters); return dbSet.Where(where3.Compile()).Sum(selector.Compile()); } return dbSet.Where(where.Compile()).Sum(selector.Compile()); } } public static T SearchObject<T>(Expression<Func<T, bool>> where) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { Expression<Func<T, bool>> where2 = (o => (o as SH_SetBase).IsDelete == false); var invokedExpr = Expression.Invoke(where2, where.Parameters.Cast<Expression>()); Expression<Func<T, bool>> where3 = Expression.Lambda<Func<T, bool>>(Expression.And(where.Body, invokedExpr), where.Parameters); return dbSet.FirstOrDefault(where3.Compile()); } return dbSet.FirstOrDefault(where.Compile()); } } public static T Find<T>(long id) where T : SH_Base { using (ShopContext db = new ShopContext()) { DbSet<T> dbSet = GetDBSet(db, typeof(T)) as DbSet<T>; if (typeof(T).IsSubclassOf(typeof(SH_SetBase))) { SH_SetBase model = dbSet.Find(id) as SH_SetBase; if (model != null && !model.IsDelete) return model as T; } return dbSet.Find(id) as T; } } public static bool Save(SH_Base model) { using (ShopContext db = new ShopContext()) { object dbSet = GetDBSet(db, model); if (model.ID == 0) { CallMethod(dbSet, "Add", new object[] { model }); } else { CallMethod(dbSet, "Attach", new object[] { model }); db.Entry(model).State = EntityState.Modified; } if (model.GetType().IsSubclassOf(typeof(SH_SetBase))) { ((SH_SetBase)model).LastUpdateTime = DateTime.Now; ((SH_SetBase)model).IsDelete = false; } else { ((SH_LogBase)model).LogTime = DateTime.Now; } db.SaveChanges(); return true; } } public static bool Delete(SH_Base model) { using (ShopContext db = new ShopContext()) { if (model.GetType().IsSubclassOf(typeof(SH_SetBase))) { ((SH_SetBase)model).LastUpdateTime = DateTime.Now; ((SH_SetBase)model).IsDelete = true; db.Entry(model).State = EntityState.Modified; db.SaveChanges(); return true; } object dbSet = GetDBSet(db, model); CallMethod(dbSet, "Remove", new object[] { model }); db.Entry(model).State = EntityState.Modified; db.SaveChanges(); return true; } } private static object GetDBSet(ShopContext db, SH_Base model) { string modelName = ObjectContext.GetObjectType(model.GetType()).Name; modelName = modelName.Replace("SH_", ""); Type type = db.GetType(); PropertyInfo property = type.GetProperty(modelName); object dbSet = property.GetValue(db); return dbSet; } private static object GetDBSet(ShopContext db, Type type) { type = ObjectContext.GetObjectType(type); string modelName = type.Name; modelName = modelName.Replace("SH_", ""); PropertyInfo property = db.GetType().GetProperty(modelName); object dbSet = property.GetValue(db); return dbSet; } private static object CallMethod(object obj, string methodName, object[] parms) { Type type = obj.GetType(); MethodInfo methodInfo = type.GetMethod(methodName); return methodInfo.Invoke(obj, parms); } }
可以看到里面包含了对SH_SetBase的特殊处理,这个有必要解释一下,因为老大之前的设计是数据库表分为两类,一种是记录表(日志表),他们继承自SH_LogBase,一种是姑且说是对象表吧,他们继承自SH_SetBase,然后全部表继承自SH_Base表,为什么要这样设计呢,其中一个原因是对象表里的数据不能真删除,他们一般是有外键的,级联删除的话删除太多了,而且里面的数据也很重要,只能假删除,所以SH_SetBase得有是否删除IsDelete字段,假删除的话那部分数据是不会再取出来的,除了假删除应该还可以禁用,比如冻结账号,冻结后又可以解冻,这就需要是否启用字段IsEnable字段。
SH_Base表:
public class SH_Base { /// <summary> /// 标识,自动增加 /// </summary> [Key] public long ID { get; set; } /// <summary> /// 备注 /// </summary> [Display(Name = "备注")] public string Summary { get; set; } }
SH_LogBase表:
public class SH_LogBase : SH_Base { private DateTime _logTime = DateTime.Now; /// <summary> /// 记录时间,默认当前时间 /// </summary> [Display(Name = "记录时间")] public DateTime LogTime { get { return _logTime; } set { _logTime = value; } } }
SH_SetBase表:
public class SH_SetBase : SH_Base { /// <summary> /// 是否删除,默认未删除 /// </summary> [Display(Name = "是否删除")] public bool IsDelete { get; set; } private bool _isEnable = true; /// <summary> /// 是否启用,默认为启用 /// </summary> [Display(Name = "是否启用")] public bool IsEnable { get { return _isEnable; } set { _isEnable = value; } } private DateTime _lastUpdateTime = DateTime.Now; /// <summary> /// 最后更新时间,默认当前时间 /// </summary> [Display(Name = "最后更新时间")] public DateTime LastUpdateTime { get { return _lastUpdateTime; } set { _lastUpdateTime = value; } } }
可我发觉DalHelper太臃肿了,而且里面用到了反射,我一直避免用反射,觉得那货效率比较低。后来看了C#线程内唯一的单例用法。把DalHelper拆分成两个BaseDal和SetBaseDal类,他们继承自IBaseDal类,里面的数据库采用线程内单例模式:
public class DbContextFactory { public static ShopContext GetCurrentDbContext() { var db = (ShopContext)CallContext.GetData("ShopContext"); if (db != null) return db; db = new ShopContext(); CallContext.SetData("ShopContext", db); return db; } }
或许有些同志会好奇线程内单例模式有什么好处或者什么用,我这里简单做个测试:
public static ShopContext GetCurrentDbContext() { //单例模式:保证线程实例唯一 var db = (ShopContext)CallContext.GetData("ShopContext"); if (db != null) { File.AppendAllText("E:\\ShopDb.txt", Thread.CurrentThread.ManagedThreadId + "旧"); return db; } File.AppendAllText("E:\\ShopDb.txt", Environment.NewLine + Thread.CurrentThread.ManagedThreadId + "新"); db = new ShopContext(); CallContext.SetData("ShopContext", db); return db; }
然后运行网站程序,随便乱点打开ShopDb.txt一看
可以看出我每点一次网页链接,控制器的Action接收到请求后就会新建一个线程来处理这次请求,由于我在该线程内一直没有另开线程,所以看到的生成的文件内每次请求就会产生一个新的ShopContext,在单次网页请求内用的都是同一个ShopContext的,这样你可能会说有问题呀,单次网页请求共用一个ShopContext,要是某次数据库请求太耗时,难道后续的操作要一直等待吗,这个问题确实存在,解决的办法就是数据库请求采用异步请求,单个线程共用ShopContext有个好处就是单次请求内只需新建一次ShopContext,不用老在内存中新建这个对象,这样的话可能你会说像线程池一样,搞个ShopContext池不是更好吗,线程要的时候去池中取一个ShopContext出来,不用的时候归还给池,这样ShopContext在内存中新建销毁的次数就可以达到最少次数了,这个我就不研究下去了。
接口IBaseDal类:
interface IBaseDal<T> { T Find(long id); T First(Expression<Func<T, bool>> predicate); T Add(T entity); bool Update(T entity); bool Delete(long id); bool Delete(T entity); bool Delete(IEnumerable<T> entities); bool Exist(Expression<Func<T, bool>> predicate); int Count(Expression<Func<T, bool>> predicate); int Sum(Expression<Func<T, bool>> predicate, Expression<Func<T, int>> selector); decimal Sum(Expression<Func<T, bool>> predicate, Expression<Func<T, decimal>> selector); IQueryable<T> LoadEntities(); IQueryable<T> LoadEntities(Expression<Func<T, bool>> predicate); IQueryable<T> LoadPageEntities<TS>(PageContent page, Expression<Func<T, bool>> predicate, Expression<Func<T, TS>> keySelector, bool isAsc); }
日志类使用的BaseDal类:
public class BaseDal<T> : IBaseDal<T> where T : class , new() { public static ShopContext Db { get { return DbContextFactory.GetCurrentDbContext(); } } public virtual T Find(long id) { return Db.Set<T>().Find(id); } public virtual T First(Expression<Func<T, bool>> predicate) { return Db.Set<T>().FirstOrDefault(predicate); } public virtual T Add(T entity) { Db.Set<T>().Add(entity); return entity; } public virtual bool Update(T entity) { Db.Entry(entity).State = EntityState.Modified; return true; } public virtual bool Delete(long id) { return Delete(Find(id)); } public virtual bool Delete(T entity) { Db.Set<T>().Remove(entity); return true; } public virtual bool Delete(IEnumerable<T> entities) { Db.Set<T>().RemoveRange(entities); return true; } public virtual bool Exist(Expression<Func<T, bool>> predicate) { return Db.Set<T>().Any(predicate); } public virtual int Count(Expression<Func<T, bool>> predicate) { return Db.Set<T>().Count(predicate); } public virtual int Sum(Expression<Func<T, bool>> predicate, Expression<Func<T, int>> selector) { try { return Db.Set<T>().Where(predicate).Sum(selector); } catch { return 0; } } public virtual decimal Sum(Expression<Func<T, bool>> predicate, Expression<Func<T, decimal>> selector) { try { return Db.Set<T>().Where(predicate).Sum(selector); } catch { return 0; } } public virtual IQueryable<T> LoadEntities() { return Db.Set<T>().AsQueryable(); } public virtual IQueryable<T> LoadEntities(Expression<Func<T, bool>> predicate) { return Db.Set<T>().Where(predicate); } public virtual IQueryable<T> LoadPageEntities<TS>(PageContent page, Expression<Func<T, bool>> predicate, Expression<Func<T, TS>> keySelector, bool isAsc) { page.TotalItems = Count(predicate); var lst = Db.Set<T>().Where(predicate); lst = isAsc ? lst.OrderBy(keySelector) : lst.OrderByDescending(keySelector); return lst.Skip(page.PageSize * (page.PageIndex - 1)) .Take(page.PageSize); } }
对象类使用的SetBaseDal类:
public class SetBaseDal<T> : IBaseDal<T> where T : SH_SetBase, new() { public static ShopContext Db { get { return DbContextFactory.GetCurrentDbContext(); } } public virtual T Find(long id) { var t = Db.Set<T>().Find(id); return t.IsDelete ? null : t; } public virtual T First(Expression<Func<T, bool>> predicate) { return Db.Set<T>().Where(o => !o.IsDelete).FirstOrDefault(predicate); } public virtual T Add(T entity) { Db.Set<T>().Add(entity); return entity; } public virtual bool Update(T entity) { entity.LastUpdateTime = DateTime.Now; Db.Entry(entity).State = EntityState.Modified; return true; } public virtual bool Delete(long id) { return Delete(Find(id)); } public virtual bool Delete(T entity) { entity.IsDelete = true; entity.LastUpdateTime = DateTime.Now; Db.Entry(entity).State = EntityState.Modified; return true; } public virtual bool Delete(IEnumerable<T> entities) { foreach (var entity in entities) { Delete(entity); } return true; } public virtual bool Exist(Expression<Func<T, bool>> predicate) { return Db.Set<T>().Where(o => !o.IsDelete).Any(predicate); } public virtual int Count(Expression<Func<T, bool>> predicate) { return Db.Set<T>().Where(o => !o.IsDelete).Count(predicate); } public virtual int Sum(Expression<Func<T, bool>> predicate, Expression<Func<T, int>> selector) { try { return Db.Set<T>().Where(predicate).Where(o => !o.IsDelete).Sum(selector); } catch { return 0; } } public virtual decimal Sum(Expression<Func<T, bool>> predicate, Expression<Func<T, decimal>> selector) { try { return Db.Set<T>().Where(predicate).Where(o => !o.IsDelete).Sum(selector); } catch { return 0; } } public virtual IQueryable<T> LoadEntities() { return Db.Set<T>().Where(o => !o.IsDelete); } public virtual IQueryable<T> LoadEntities(Expression<Func<T, bool>> predicate) { return Db.Set<T>().Where(predicate).Where(o => !o.IsDelete); } public virtual IQueryable<T> LoadPageEntities<TS>(PageContent page, Expression<Func<T, bool>> predicate, Expression<Func<T, TS>> keySelector, bool isAsc) { page.TotalItems = Count(predicate); var lst = Db.Set<T>() .Where(predicate) .Where(o => !o.IsDelete); lst = isAsc ? lst.OrderBy(keySelector) : lst.OrderByDescending(keySelector); return lst.Skip(page.PageSize * (page.PageIndex - 1)) .Take(page.PageSize); } }
里面还有个PageContent类:
public class PageContent { public int PageIndex { get; set; } public int PageSize { get; set; } public int TotalItems { get; set; } public int TotalPages { get { if (PageSize == 0) return 0; return (TotalItems + PageSize - 1)/PageSize; } } }
可以看出BaseDal和SetBaseDal之间最大的区别就是BaseDal删除是真删除,SetBaseDal不能真删除,调用删除方法自动执行假删除,而且SetBaseDal获取数据等所有方法都把假删除的数据排除掉了。可能你会问既然假删除的数据都被排除掉了,那他们还有存在的必要吗,当然有必要了,因为除了用户看这些数据,管理人员是可以直接去数据库看的,那就能看到那些被假删除的数据了。
写好这BaseDal和SetBaseDal类之后,BLL的类就可以继承自这两个类或者不继承,在类里使用者两个类,设计理念我不在行,貌似不建议直接继承Dal的通用类,而是在BLL定义一个接口,然后实现每个表的该接口,并在该类里引用Dal里的通用类,不过我觉得怎么方便就怎么用吧,何必一定要符合设计理念呢。还有贫血模型也是,按照面向对象理念是不能设计为贫血模型的,因为一个对象必须是完整的,里面的属性就像一个对象的血肉肢体,而里面的方法就像一个对象能够进行的活动,缺少了方法的实体类就像是尸体类,光有肢体不能动,可我还是那句话,怎么方便就怎么用吧,为何一定要面向对象呢,而且我认为光有属性也有好处,当我只需要该对象来传值的时候其实是不需要该对象的行为的,这样的话贫血模型加载到内存中占用的内存就少了些了。当然,这只是我的胡言乱语,大神请直接忽视。。。