首頁 > 後端開發 > C#.Net教程 > 總結EF通用資料層封裝類別實例詳解

總結EF通用資料層封裝類別實例詳解

零下一度
發布: 2017-06-24 09:54:43
原創
3984 人瀏覽過

淺談orm

  記得四年前在學校第一次接觸到Ling to Sql,那時候瞬間發現不用手寫sql語句是多麼的方便,後面慢慢的接觸了許多orm框架,像是EF,Dapper,Hibernate,ServiceStack.OrmLite 等。當然每種orm都有各自的優勢,也有不足的地方。園子裡也有很多大神開源了他們寫的orm,如SqlSugar,Chloe.ORM,CYQ.Data 等。先不說這些開源的orm使用度怎麼樣,我覺得起碼從開源的精神上就很可嘉了,我也曾下載過這幾位大神的源碼進行學習。
  所有orm最終體現的一點就是方便,減少程式設計師重複性的工作,當然目前還有一些公司還是使用手寫sql的方式來做,我覺得整個專案都使用手寫sql來做真的是有點閒到蛋疼,並不是不推薦手寫sql的方式,只是個人覺得最基本的增刪改查這些都手寫的話,那其實考驗不是能力,而是耐力。有人說手寫sql的方式可控性強,效能高,我想說的是orm也能做到,重點是你怎麼去使用。
  orm的優點非常明顯,開發便捷,但或許也是由於這個優點,讓很多偷懶的程式設計師也會漸漸忘了sql語句的寫法,我遇到過很多的程式設計師朋友用了EF後,手寫sql,視圖、預存程序這些都不想用了,我個人覺手寫sql這種還是必要的。不然某一天你看到別人的程式裡面寫著 “exec  xxxx”,你就會突然覺得“啊,好像在哪裡見過.....”。所以我想說的是「該出手時還是得出手」。

淺談Entity Framework

  Entity Framework 是微軟家的orm框架,隨著 Entity Framework 不斷的完善強化,目前相信使用的比例相對其他的orm來說還是較高的。像我目前使用的最多的就是EF和Dapper。確實,EF用起來開發過程中會方便很多,畢竟EF走過了這麼年頭,無論是成熟度,還是性能等都提高了很多,也有很多開發者為EF提供了擴展功能,如entity framework extended 等。而且身為.net開發者來說專案通用性也很強,資料也多,微軟在這塊的更新力道也很給力。不過之前剛出的EF Core也存在一些坑,畢竟還是初期階段,相信現在後面會越來越好的。

  Entity Framework  提供了三種開發模式,code first,db first,model first。目前用的最多的就屬code first了。至於這三種模式的簡單使用和區別,大家可以參考下這篇文章。

  我曾聽一些朋友說過說EF使用起來效能很差,產生的sql語句很難看等。我覺得說這種話之前還是先檢查下程式碼或多看下一些EF文章吧,要先確保自己沒給自己挖坑,然後才能指責別人的不好。如果真心覺得EF或其他的orm用起來很不爽,那就自己寫一個吧,我也曾經和同事用Dapper擴展一個通用的orm,當時是出於一種學習和使用方便的角度。

Entity Framework 通用資料層類別

  這裡提供下EF 通用資料層父類別方法,其實網路上也有很多人提供了自己專案中的EF 通用資料層父類別方法,所以這裡提供的並不是最優、最好的選擇,只能說是可以通用的類,方便大家學習和使用,具體程式碼如下:

DbContextFactory DbContext工廠類別

    public class DbContextFactory
    {public  DbContext GetDbContext()
        {string key = typeof(DBContext.DbContextFactory).Name + "XJHDbContext";
            DbContext dbContext = CallContext.GetData(key) as DbContext;if (dbContext == null)
            {
                dbContext = new XJHDbContext();
                CallContext.SetData(key, dbContext);
            }return dbContext;
        }
    }
登入後複製

DbBase 資料層通用操作類別

 public class DbBase
    {protected DbContext Db = new DbContextFactory().GetDbContext();        #region 自定义其他方法/// <summary>/// 执行存储过程或自定义sql语句--返回集合(自定义返回类型)/// </summary>/// <param name="sql"></param>/// <param name="parms"></param>/// <param name="cmdType"></param>/// <returns></returns>public List<TModel> Query<TModel>(string sql, List<SqlParameter> parms, CommandType cmdType = CommandType.Text)
        {//存储过程(exec getActionUrlId @name,@ID)if (cmdType == CommandType.StoredProcedure)
            {
                StringBuilder paraNames = new StringBuilder();foreach (var sqlPara in parms)
                {
                    paraNames.Append($" @{sqlPara},");
                }
                sql = paraNames.Length > 0 ? $"exec {sql} {paraNames.ToString().Trim(',')}" : $"exec {sql} ";
            }var list = Db.Database.SqlQuery<TModel>(sql, parms.ToArray());var enityList = list.ToList();return enityList;
        }/// <summary>/// 自定义语句和存储过程的增删改--返回影响的行数/// </summary>/// <param name="sql"></param>/// <param name="parms"></param>/// <param name="cmdType"></param>/// <returns></returns>public int Execute(string sql, List<SqlParameter> parms, CommandType cmdType = CommandType.Text)
        {//存储过程(exec getActionUrlId @name,@ID)if (cmdType == CommandType.StoredProcedure)
            {
                StringBuilder paraNames = new StringBuilder();foreach (var sqlPara in parms)
                {
                    paraNames.Append($" @{sqlPara},");
                }
                sql = paraNames.Length > 0 ?$"exec {sql} {paraNames.ToString().Trim(',')}" :
                    $"exec {sql} ";
            }int ret = Db.Database.ExecuteSqlCommand(sql, parms.ToArray());return ret;
        }#endregion 自定义其他方法}/// <summary>/// mssql数据库 数据层 父类/// </summary>/// <typeparam name="T"></typeparam>public class DbBase<T> : DbBase where T : class, new()
    {#region INSERT/// <summary>/// 新增 实体/// </summary>/// <param name="model"></param>/// <returns></returns>public void Insert(T model)
        {
            Db.Set<T>().Add(model);

        }/// <summary>/// 普通批量插入/// </summary>/// <param name="datas"></param>public void InsertRange(List<T> datas)
        {
            Db.Set<T>().AddRange(datas);
        }#endregion INSERT#region DELETE/// <summary>/// 根据模型删除/// </summary>/// <param name="model">包含要删除id的对象</param>/// <returns></returns>public void Delete(T model)
        {
            Db.Set<T>().Attach(model);
            Db.Set<T>().Remove(model);
        }/// <summary>/// 删除/// </summary>/// <param name="whereLambda"></param>public void Delete(Expression<Func<T, bool>> whereLambda)
        {
            Db.Set<T>().Where(whereLambda).Delete();
        }#endregion DELETE#region UPDATE/// <summary>/// 单个对象指定列修改/// </summary>/// <param name="model">要修改的实体对象</param>/// <param name="proNames">要修改的 属性 名称</param>/// <param name="isProUpdate"></param>/// <returns></returns>public void Update(T model, List<string> proNames, bool isProUpdate = true)
        {//将 对象 添加到 EF中Db.Set<T>().Attach(model);var setEntry = ((IObjectContextAdapter)Db).ObjectContext.ObjectStateManager.GetObjectStateEntry(model);//指定列修改if (isProUpdate)
            {foreach (string proName in proNames)
                {
                    setEntry.SetModifiedProperty(proName);
                }
            }//忽略类修改else{
                Type t = typeof(T);
                List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();foreach (var item in proInfos)
                {string proName = item.Name;if (proNames.Contains(proName))
                    {continue;
                    }
                    setEntry.SetModifiedProperty(proName);
                }
            }
        }/// <summary>/// 单个对象修改/// </summary>/// <param name="model"></param>/// <returns></returns>public void Update(T model)
        {
            DbEntityEntry entry = Db.Entry<T>(model);
            Db.Set<T>().Attach(model);
            entry.State = EntityState.Modified;

        }/// <summary>/// 批量修改/// </summary>/// <param name="whereLambda"></param>/// <param name="updateExpression"></param>public void Update(Expression<Func<T, bool>> whereLambda, Expression<Func<T, T>> updateExpression)
        {
            Db.Set<T>().Where(whereLambda).Update(updateExpression);
        }/// <summary>/// 批量修改/// </summary>/// <param name="models"></param>/// <returns></returns>public void UpdateAll(List<T> models)
        {foreach (var model in models)
            {
                DbEntityEntry entry = Db.Entry(model);
                entry.State = EntityState.Modified;
            }


        }/// <summary>/// 批量统一修改/// </summary>/// <param name="model">要修改的实体对象</param>/// <param name="whereLambda">查询条件</param>/// <param name="modifiedProNames"></param>/// <returns></returns>public void Update(T model, Expression<Func<T, bool>> whereLambda, params string[] modifiedProNames)
        {//查询要修改的数据List<T> listModifing = Db.Set<T>().Where(whereLambda).ToList();
            Type t = typeof(T);
            List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
            Dictionary<string, PropertyInfo> dictPros = new Dictionary<string, PropertyInfo>();
            proInfos.ForEach(p =>{if (modifiedProNames.Contains(p.Name))
                {
                    dictPros.Add(p.Name, p);
                }
            });if (dictPros.Count <= 0)
            {throw new Exception("指定修改的字段名称有误或为空");
            }foreach (var item in dictPros)
            {
                PropertyInfo proInfo = item.Value;//取出 要修改的值object newValue = proInfo.GetValue(model, null);//批量设置 要修改 对象的 属性foreach (T oModel in listModifing)
                {//为 要修改的对象 的 要修改的属性 设置新的值proInfo.SetValue(oModel, newValue, null);
                }
            }

        }#endregion UPDATE#region SELECT/// <summary>/// 根据主键查询/// </summary>/// <param name="id"></param>/// <returns></returns>public T FindById(dynamic id)
        {return Db.Set<T>().Find(id);
        }/// <summary>/// 获取默认一条数据,没有则为NULL/// </summary>/// <param name="whereLambda"></param>/// <returns></returns>public T FirstOrDefault(Expression<Func<T, bool>> whereLambda = null)
        {if (whereLambda == null)
            {return Db.Set<T>().FirstOrDefault();
            }return Db.Set<T>().FirstOrDefault(whereLambda);
        }/// <summary>/// 获取全部数据/// </summary>/// <returns></returns>public List<T> GetAll(string ordering = null)
        {return ordering == null? Db.Set<T>().ToList()
                : Db.Set<T>().OrderBy(ordering).ToList();
        }/// <summary>/// 带条件查询获取数据/// </summary>/// <param name="whereLambda"></param>/// <param name="ordering"></param>/// <returns></returns>public List<T> GetAll(Expression<Func<T, bool>> whereLambda, string ordering = null)
        {var iQueryable = Db.Set<T>().Where(whereLambda);return ordering == null? iQueryable.ToList()
                : iQueryable.OrderBy(ordering).ToList();
        }/// <summary>/// 带条件查询获取数据/// </summary>/// <param name="whereLambda"></param>/// <returns></returns>public IQueryable<T> GetAllIQueryable(Expression<Func<T, bool>> whereLambda = null)
        {return whereLambda == null ? Db.Set<T>() : Db.Set<T>().Where(whereLambda);
        }/// <summary>/// 获取数量/// </summary>/// <param name="whereLambd"></param>/// <returns></returns>public int GetCount(Expression<Func<T, bool>> whereLambd = null)
        {return whereLambd == null ? Db.Set<T>().Count() : Db.Set<T>().Where(whereLambd).Count();
        }/// <summary>/// 判断对象是否存在/// </summary>/// <param name="whereLambd"></param>/// <returns></returns>public bool Any(Expression<Func<T, bool>> whereLambd)
        {return Db.Set<T>().Where(whereLambd).Any();
        }/// <summary>/// 分页查询/// </summary>/// <param name="pageIndex">当前页码</param>/// <param name="pageSize">每页大小</param>/// <param name="rows">总条数</param>/// <param name="orderBy">排序条件(一定要有)</param>/// <param name="whereLambda">查询添加(可有,可无)</param>/// <param name="isOrder">是否是Order排序</param>/// <returns></returns>public List<T> Page<TKey>(int pageIndex, int pageSize, out int rows, Expression<Func<T, TKey>> orderBy, Expression<Func<T, bool>> whereLambda = null, bool isOrder = true)
        {
            IQueryable<T> data = isOrder ?Db.Set<T>().OrderBy(orderBy) :
                Db.Set<T>().OrderByDescending(orderBy);if (whereLambda != null)
            {
                data = data.Where(whereLambda);
            }
            rows = data.Count();return data.PageBy((pageIndex - 1) * pageSize, pageSize).ToList();
        }/// <summary>/// 分页查询/// </summary>/// <param name="pageIndex">当前页码</param>/// <param name="pageSize">每页大小</param>/// <param name="rows">总条数</param>/// <param name="ordering">排序条件(一定要有)</param>/// <param name="whereLambda">查询添加(可有,可无)</param>/// <returns></returns>public List<T> Page(int pageIndex, int pageSize, out int rows, string ordering, Expression<Func<T, bool>> whereLambda = null)
        {// 分页 一定注意: Skip 之前一定要 OrderByvar data = Db.Set<T>().OrderBy(ordering);if (whereLambda != null)
            {
                data = data.Where(whereLambda);
            }
            rows = data.Count();return data.PageBy((pageIndex - 1) * pageSize, pageSize).ToList();
        }/// <summary>/// 查询转换/// </summary>/// <typeparam name="TDto"></typeparam>/// <param name="whereLambda"></param>/// <returns></returns>public List<TDto> Select<TDto>(Expression<Func<T, bool>> whereLambda)
        {return Db.Set<T>().Where(whereLambda).Select<TDto>().ToList();
        }#endregion SELECT#region ORTHER/// <summary>/// 执行存储过程或自定义sql语句--返回集合/// </summary>/// <param name="sql"></param>/// <param name="parms"></param>/// <param name="cmdType"></param>/// <returns></returns>public List<T> Query(string sql, List<SqlParameter> parms, CommandType cmdType = CommandType.Text)
        {return Query<T>(sql, parms, cmdType);
        }/// <summary>/// 提交保存/// </summary>/// <returns></returns>public int SaveChanges()
        {return Db.SaveChanges();
        }/// <summary>/// 回滚/// </summary>public void RollBackChanges()
        {var items = Db.ChangeTracker.Entries().ToList();
            items.ForEach(o => o.State = EntityState.Unchanged);
        }#endregion ORTHER}
登入後複製
DbBase

扩展类,实现读写分离

  上面的通用类是比较基础简单通用的,适合于单库读写操作。对于EF实现读写分离,之前网上找过类似的参考文章,很多人文章都是使用 DbCommandInterceptor拦截器 来实现,具体的做法是通过拦截到sql语句,然后根据具体条件去判断是走主库还是从库。这种做法不是不行,只是个人感觉不是很好扩展,而且要在拦截器里面做限制判断。

  其实说白了EF本身就是一个读写分离的orm。用过EF的人知道,EF提供访问数据库的是 DbContext 这个对象,所以想实现读写分离的就很简单了,只要在程序中使用两个不同的DbContext对象,一个负责读,一个负责写就好了。

  所以在上面提供的通用封装类中稍微做下修改,修改如下DbContextFactory中获取DbContext的方法,实现一个读的DbContext和一个写的DbContext对象的获取。

  这里要注意下,对于读的DbContext来说,要做下设置
  1.使用 Database.SetInitializer(new NullDatabaseInitializer()); 改变ef初始化策略,因为一般我们使用读写分离的话从库都是同步于主库的,所以不使用ef的自动创建数据库策略。
  2.重写 SaveChanges 方法,对应从库来说,只提供读取的功能,所以防止误操作,这里禁用掉SaveChanges方法,一般需要使用从读的保存方法,就对外抛出异常。

  代码如下:

支持读写分离的 DbContextFactory 类

  public class DbContextFactory
    {public DbContext GetWriteDbContext(){string key = typeof(DbContextFactory).Name + "WriteDbContext";
            DbContext dbContext = CallContext.GetData(key) as DbContext;if (dbContext == null)
            {
                dbContext = new WriteDbContext();
                CallContext.SetData(key, dbContext);
            }return dbContext;
        }public DbContext GetReadDbContext(){string key = typeof(DbContextFactory).Name + "ReadDbContext";
            DbContext dbContext = CallContext.GetData(key) as DbContext;if (dbContext == null)
            {
                dbContext = new ReadDbContext();
                CallContext.SetData(key, dbContext);
            }return dbContext;
        }
    }
登入後複製

   对应的 DbBase 类也做下修改,主要将上面的Db对象改作 MasterDb  SlaveDb 对象,并且把上面的读写方法坐下调整,修改后如下:

支持读写分离的 DbBase类

public class DbBase
    {//是否读写分离(可以配置在配置文件中)private static readonly bool IsReadWriteSeparation = true;#region EF上下文对象(主库)protected DbContext MasterDb => _masterDb.Value;private readonly Lazy<DbContext> _masterDb = new Lazy<DbContext>(() => new DbContextFactory().GetWriteDbContext());#endregion EF上下文对象(主库)#region EF上下文对象(从库)protected DbContext SlaveDb => IsReadWriteSeparation ? _slaveDb.Value : _masterDb.Value;private readonly Lazy<DbContext> _slaveDb = new Lazy<DbContext>(() => new DbContextFactory().GetReadDbContext());#endregion EF上下文对象(从库)#region 自定义其他方法/// <summary>/// 执行存储过程或自定义sql语句--返回集合(自定义返回类型)/// </summary>/// <param name="sql"></param>/// <param name="parms"></param>/// <param name="cmdType"></param>/// <returns></returns>public List<TModel> Query<TModel>(string sql, List<SqlParameter> parms, CommandType cmdType = CommandType.Text)
        {//存储过程(exec getActionUrlId @name,@ID)if (cmdType == CommandType.StoredProcedure)
            {
                StringBuilder paraNames = new StringBuilder();foreach (var sqlPara in parms)
                {
                    paraNames.Append($" @{sqlPara},");
                }
                sql = paraNames.Length > 0 ? $"exec {sql} {paraNames.ToString().Trim(',')}" : $"exec {sql} ";
            }var list = SlaveDb.Database.SqlQuery<TModel>(sql, parms.ToArray());var enityList = list.ToList();return enityList;
        }/// <summary>/// 自定义语句和存储过程的增删改--返回影响的行数/// </summary>/// <param name="sql"></param>/// <param name="parms"></param>/// <param name="cmdType"></param>/// <returns></returns>public int Execute(string sql, List<SqlParameter> parms, CommandType cmdType = CommandType.Text)
        {//存储过程(exec getActionUrlId @name,@ID)if (cmdType == CommandType.StoredProcedure)
            {
                StringBuilder paraNames = new StringBuilder();foreach (var sqlPara in parms)
                {
                    paraNames.Append($" @{sqlPara},");
                }
                sql = paraNames.Length > 0 ?$"exec {sql} {paraNames.ToString().Trim(',')}" :
                    $"exec {sql} ";
            }int ret = MasterDb.Database.ExecuteSqlCommand(sql, parms.ToArray());return ret;
        }#endregion 自定义其他方法}/// <summary>/// mssql数据库 数据层 父类/// </summary>/// <typeparam name="T"></typeparam>public class DbBase<T> : DbBase where T : class, new()
    {#region INSERT/// <summary>/// 新增 实体/// </summary>/// <param name="model"></param>/// <returns></returns>public void Insert(T model)
        {
            MasterDb.Set<T>().Add(model);
        }/// <summary>/// 普通批量插入/// </summary>/// <param name="datas"></param>public void InsertRange(List<T> datas)
        {
            MasterDb.Set<T>().AddRange(datas);
        }#endregion INSERT#region DELETE/// <summary>/// 根据模型删除/// </summary>/// <param name="model">包含要删除id的对象</param>/// <returns></returns>public void Delete(T model)
        {
            MasterDb.Set<T>().Attach(model);
            MasterDb.Set<T>().Remove(model);
        }/// <summary>/// 删除/// </summary>/// <param name="whereLambda"></param>public void Delete(Expression<Func<T, bool>> whereLambda)
        {
            MasterDb.Set<T>().Where(whereLambda).Delete();
        }#endregion DELETE#region UPDATE/// <summary>/// 单个对象指定列修改/// </summary>/// <param name="model">要修改的实体对象</param>/// <param name="proNames">要修改的 属性 名称</param>/// <param name="isProUpdate"></param>/// <returns></returns>public void Update(T model, List<string> proNames, bool isProUpdate = true)
        {//将 对象 添加到 EF中MasterDb.Set<T>().Attach(model);var setEntry = ((IObjectContextAdapter)MasterDb).ObjectContext.ObjectStateManager.GetObjectStateEntry(model);//指定列修改if (isProUpdate)
            {foreach (string proName in proNames)
                {
                    setEntry.SetModifiedProperty(proName);
                }
            }//忽略类修改else{
                Type t = typeof(T);
                List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();foreach (var item in proInfos)
                {string proName = item.Name;if (proNames.Contains(proName))
                    {continue;
                    }
                    setEntry.SetModifiedProperty(proName);
                }
            }
        }/// <summary>/// 单个对象修改/// </summary>/// <param name="model"></param>/// <returns></returns>public void Update(T model)
        {
            DbEntityEntry entry = MasterDb.Entry<T>(model);
            MasterDb.Set<T>().Attach(model);
            entry.State = EntityState.Modified;
        }/// <summary>/// 批量修改/// </summary>/// <param name="whereLambda"></param>/// <param name="updateExpression"></param>public void Update(Expression<Func<T, bool>> whereLambda, Expression<Func<T, T>> updateExpression)
        {
            MasterDb.Set<T>().Where(whereLambda).Update(updateExpression);
        }/// <summary>/// 批量修改/// </summary>/// <param name="models"></param>/// <returns></returns>public void UpdateAll(List<T> models)
        {foreach (var model in models)
            {
                DbEntityEntry entry = MasterDb.Entry(model);
                entry.State = EntityState.Modified;
            }
        }/// <summary>/// 批量统一修改/// </summary>/// <param name="model">要修改的实体对象</param>/// <param name="whereLambda">查询条件</param>/// <param name="modifiedProNames"></param>/// <returns></returns>public void Update(T model, Expression<Func<T, bool>> whereLambda, params string[] modifiedProNames)
        {//查询要修改的数据List<T> listModifing = MasterDb.Set<T>().Where(whereLambda).ToList();
            Type t = typeof(T);
            List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
            Dictionary<string, PropertyInfo> dictPros = new Dictionary<string, PropertyInfo>();
            proInfos.ForEach(p =>{if (modifiedProNames.Contains(p.Name))
                {
                    dictPros.Add(p.Name, p);
                }
            });if (dictPros.Count <= 0)
            {throw new Exception("指定修改的字段名称有误或为空");
            }foreach (var item in dictPros)
            {
                PropertyInfo proInfo = item.Value;//取出 要修改的值object newValue = proInfo.GetValue(model, null);//批量设置 要修改 对象的 属性foreach (T oModel in listModifing)
                {//为 要修改的对象 的 要修改的属性 设置新的值proInfo.SetValue(oModel, newValue, null);
                }
            }
        }#endregion UPDATE#region SELECT/// <summary>/// 根据主键查询/// </summary>/// <param name="id"></param>/// <returns></returns>public T FindById(dynamic id)
        {return SlaveDb.Set<T>().Find(id);
        }/// <summary>/// 获取默认一条数据,没有则为NULL/// </summary>/// <param name="whereLambda"></param>/// <returns></returns>public T FirstOrDefault(Expression<Func<T, bool>> whereLambda = null)
        {if (whereLambda == null)
            {return SlaveDb.Set<T>().FirstOrDefault();
            }return SlaveDb.Set<T>().FirstOrDefault(whereLambda);
        }/// <summary>/// 获取全部数据/// </summary>/// <returns></returns>public List<T> GetAll(string ordering = null)
        {return ordering == null? SlaveDb.Set<T>().ToList()
                : SlaveDb.Set<T>().OrderBy(ordering).ToList();
        }/// <summary>/// 带条件查询获取数据/// </summary>/// <param name="whereLambda"></param>/// <param name="ordering"></param>/// <returns></returns>public List<T> GetAll(Expression<Func<T, bool>> whereLambda, string ordering = null)
        {var iQueryable = SlaveDb.Set<T>().Where(whereLambda);return ordering == null? iQueryable.ToList()
                : iQueryable.OrderBy(ordering).ToList();
        }/// <summary>/// 带条件查询获取数据/// </summary>/// <param name="whereLambda"></param>/// <returns></returns>public IQueryable<T> GetAllIQueryable(Expression<Func<T, bool>> whereLambda = null)
        {return whereLambda == null ? SlaveDb.Set<T>() : SlaveDb.Set<T>().Where(whereLambda);
        }/// <summary>/// 获取数量/// </summary>/// <param name="whereLambd"></param>/// <returns></returns>public int GetCount(Expression<Func<T, bool>> whereLambd = null)
        {return whereLambd == null ? SlaveDb.Set<T>().Count() : SlaveDb.Set<T>().Where(whereLambd).Count();
        }/// <summary>/// 判断对象是否存在/// </summary>/// <param name="whereLambd"></param>/// <returns></returns>public bool Any(Expression<Func<T, bool>> whereLambd)
        {return SlaveDb.Set<T>().Where(whereLambd).Any();
        }/// <summary>/// 分页查询/// </summary>/// <param name="pageIndex">当前页码</param>/// <param name="pageSize">每页大小</param>/// <param name="rows">总条数</param>/// <param name="orderBy">排序条件(一定要有)</param>/// <param name="whereLambda">查询添加(可有,可无)</param>/// <param name="isOrder">是否是Order排序</param>/// <returns></returns>public List<T> Page<TKey>(int pageIndex, int pageSize, out int rows, Expression<Func<T, TKey>> orderBy, Expression<Func<T, bool>> whereLambda = null, bool isOrder = true)
        {
            IQueryable<T> data = isOrder ?SlaveDb.Set<T>().OrderBy(orderBy) :
                SlaveDb.Set<T>().OrderByDescending(orderBy);if (whereLambda != null)
            {
                data = data.Where(whereLambda);
            }
            rows = data.Count();return data.PageBy((pageIndex - 1) * pageSize, pageSize).ToList();
        }/// <summary>/// 分页查询/// </summary>/// <param name="pageIndex">当前页码</param>/// <param name="pageSize">每页大小</param>/// <param name="rows">总条数</param>/// <param name="ordering">排序条件(一定要有)</param>/// <param name="whereLambda">查询添加(可有,可无)</param>/// <returns></returns>public List<T> Page(int pageIndex, int pageSize, out int rows, string ordering, Expression<Func<T, bool>> whereLambda = null)
        {// 分页 一定注意: Skip 之前一定要 OrderByvar data = SlaveDb.Set<T>().OrderBy(ordering);if (whereLambda != null)
            {
                data = data.Where(whereLambda);
            }
            rows = data.Count();return data.PageBy((pageIndex - 1) * pageSize, pageSize).ToList();
        }/// <summary>/// 查询转换/// </summary>/// <typeparam name="TDto"></typeparam>/// <param name="whereLambda"></param>/// <returns></returns>public List<TDto> Select<TDto>(Expression<Func<T, bool>> whereLambda)
        {return SlaveDb.Set<T>().Where(whereLambda).Select<TDto>().ToList();
        }#endregion SELECT#region ORTHER/// <summary>/// 执行存储过程或自定义sql语句--返回集合/// </summary>/// <param name="sql"></param>/// <param name="parms"></param>/// <param name="cmdType"></param>/// <returns></returns>public List<T> Query(string sql, List<SqlParameter> parms, CommandType cmdType = CommandType.Text)
        {return Query<T>(sql, parms, cmdType);
        }/// <summary>/// 提交保存/// </summary>/// <returns></returns>public int SaveChanges()
        {return MasterDb.SaveChanges();
        }/// <summary>/// 回滚/// </summary>public void RollBackChanges()
        {var items = MasterDb.ChangeTracker.Entries().ToList();
            items.ForEach(o => o.State = EntityState.Unchanged);
        }#endregion ORTHER}
登入後複製
DbBase

  这样简单的读写分离就实现了,实现逻辑也比较清晰,方便扩展。

进一步改造,实现多从库读取

  一般做读写分离,都会做一主多从,特别对读取量比较大的项目,这样多库读取就能减轻读库的压力。所以对于上面的方法,做下改造。
  上面可以看到,主库和从库都是通过 DbContextFactory 这个类来获取的,在GetReadDbContext 方法中每次都是获取 ReadDbContext 这个对象。那么对于多个从库的情况下,每次读取到底要去哪个库读取数据呢?这里就是一个算法规则的问题了,或者说是策略吧,如果使用过nginx的朋友就知道,nginx本身内部在实现负载均衡的时候提供了多种策略,比如轮询,加权轮询,ip_hash等策略。其实上面获取同一个ReadDbContext 的方法也算一种策略,叫单一策略,每次都获取单一的对象。

  多从库的情况下,我们简单的来实现另一种获取策略,随机策略,每次都随机获取到一个从库的对象,这种是最简单的策略,当然,正式使用的话大家可以发挥自己的创造力,写出多了的算法策略。

首先,定义一个策略接口,方便策略的扩展和切换,代码如下:

IReadDbStrategy 接口

 /// <summary>
  /// 从数据库获取策略接口  /// </summary>
  public interface IReadDbStrategy
  {      /// <summary>  /// 获取读库      /// </summary>  /// <returns></returns>      DbContext GetDbContext();
  }
登入後複製

单从库情况下,定义一个单一策略,代码如下:

单一策略

   /// <summary>
   /// 单一策略   /// </summary>
   public class SingleStrategy : IReadDbStrategy
   {       public DbContext GetDbContext()
       {           return new ReadDbContext();
       }
   }
登入後複製

多从库情况下,定义一个随机策略,代码如下:

随机策略

    /// <summary>/// 随机策略/// </summary>public class RandomStrategy : IReadDbStrategy
    {//所有读库类型public static List<Type> DbTypes; static RandomStrategy()
        {
            LoadDbs();
        } //加载所有的读库类型static void LoadDbs()
        {
            DbTypes = new List<Type>();var assembly = Assembly.GetExecutingAssembly();var types = assembly.GetTypes();foreach (var type in types)
            {if (type.BaseType == typeof(BaseReadDbContext))
                {
                    DbTypes.Add(type);
                }
            }
        } public DbContext GetDbContext()
        {int randomIndex = new Random().Next(0, DbTypes.Count);var dbType = DbTypes[randomIndex];var dbContext = Activator.CreateInstance(dbType) as DbContext;return dbContext;
        }
    }
登入後複製

  这样,所有从库我们都基于策略去获取,扩展也比较方便。修改下 DbContextFactory 类的 GetReadDbContext 方法,通过策略接口来获取,代码如下:

支持一主多从的 DbContextFactory 类

  public class DbContextFactory
    {//todo:这里可以自己通过注入的方式来实现,就会更加灵活private static readonly IReadDbStrategy ReadDbStrategy = new RandomStrategy();public DbContext GetWriteDbContext()
        {string key = typeof(DbContextFactory).Name + "WriteDbContext";
            DbContext dbContext = CallContext.GetData(key) as DbContext;if (dbContext == null)
            {
                dbContext = new WriteDbContext();
                CallContext.SetData(key, dbContext);
            }return dbContext;
        }public DbContext GetReadDbContext()
        {string key = typeof(DbContextFactory).Name + "ReadDbContext";
            DbContext dbContext = CallContext.GetData(key) as DbContext;if (dbContext == null)
            {
                dbContext = ReadDbStrategy.GetDbContext();CallContext.SetData(key, dbContext);
            }return dbContext;
        }
    }
登入後複製

  这样简单的一主多从也实现了。

参考文章

  
  

源码分享

  所有的代码提供给大家的更多的是一种思路和学习的参考,如果有什么不足的地方也欢迎大家批评指正,如果觉得对你有帮助,不要吝啬你的鼠标,帮忙点个星,点个赞吧。

以上是總結EF通用資料層封裝類別實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板