目錄
淺談orm
淺談Entity Framework
Entity Framework 通用資料層類別
DbContextFactory DbContext工廠類別
DbBase 資料層通用操作類別
扩展类,实现读写分离
支持读写分离的 DbContextFactory 类
支持读写分离的 DbBase类
进一步改造,实现多从库读取
IReadDbStrategy 接口
单一策略
随机策略
支持一主多从的 DbContextFactory 类
参考文章
源码分享
首頁 後端開發 C#.Net教程 總結EF通用資料層封裝類別實例詳解

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

Jun 24, 2017 am 09:54 AM
封裝 支援 數據 讀寫 一般

淺談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中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

使用ddrescue在Linux上恢復數據 使用ddrescue在Linux上恢復數據 Mar 20, 2024 pm 01:37 PM

DDREASE是一種用於從檔案或區塊裝置(如硬碟、SSD、RAM磁碟、CD、DVD和USB儲存裝置)復原資料的工具。它將資料從一個區塊設備複製到另一個區塊設備,留下損壞的資料區塊,只移動好的資料區塊。 ddreasue是一種強大的恢復工具,完全自動化,因為它在恢復操作期間不需要任何干擾。此外,由於有了ddasue地圖文件,它可以隨時停止和恢復。 DDREASE的其他主要功能如下:它不會覆寫恢復的數據,但會在迭代恢復的情況下填補空白。但是,如果指示工具明確執行此操作,則可以將其截斷。將資料從多個檔案或區塊還原到單

開源!超越ZoeDepth! DepthFM:快速且精確的單目深度估計! 開源!超越ZoeDepth! DepthFM:快速且精確的單目深度估計! Apr 03, 2024 pm 12:04 PM

0.這篇文章乾了啥?提出了DepthFM:一個多功能且快速的最先進的生成式單目深度估計模型。除了傳統的深度估計任務外,DepthFM還展示了在深度修復等下游任務中的最先進能力。 DepthFM效率高,可以在少數推理步驟內合成深度圖。以下一起來閱讀這項工作~1.論文資訊標題:DepthFM:FastMonocularDepthEstimationwithFlowMatching作者:MingGui,JohannesS.Fischer,UlrichPrestel,PingchuanMa,Dmytr

Google狂喜:JAX性能超越Pytorch、TensorFlow!或成GPU推理訓練最快選擇 Google狂喜:JAX性能超越Pytorch、TensorFlow!或成GPU推理訓練最快選擇 Apr 01, 2024 pm 07:46 PM

谷歌力推的JAX在最近的基準測試中表現已經超過Pytorch和TensorFlow,7項指標排名第一。而且測試並不是JAX性能表現最好的TPU上完成的。雖然現在在開發者中,Pytorch依然比Tensorflow更受歡迎。但未來,也許有更多的大型模型會基於JAX平台進行訓練和運行。模型最近,Keras團隊為三個後端(TensorFlow、JAX、PyTorch)與原生PyTorch實作以及搭配TensorFlow的Keras2進行了基準測試。首先,他們為生成式和非生成式人工智慧任務選擇了一組主流

iPhone上的蜂窩數據網路速度慢:修復 iPhone上的蜂窩數據網路速度慢:修復 May 03, 2024 pm 09:01 PM

在iPhone上面臨滯後,緩慢的行動數據連線?通常,手機上蜂窩互聯網的強度取決於幾個因素,例如區域、蜂窩網絡類型、漫遊類型等。您可以採取一些措施來獲得更快、更可靠的蜂窩網路連線。修復1–強制重啟iPhone有時,強制重啟設備只會重置許多內容,包括蜂窩網路連線。步驟1–只需按一次音量調高鍵並放開即可。接下來,按降低音量鍵並再次釋放它。步驟2–過程的下一部分是按住右側的按鈕。讓iPhone完成重啟。啟用蜂窩數據並檢查網路速度。再次檢查修復2–更改資料模式雖然5G提供了更好的網路速度,但在訊號較弱

超級智能體生命力覺醒!可自我更新的AI來了,媽媽再也不用擔心資料瓶頸難題 超級智能體生命力覺醒!可自我更新的AI來了,媽媽再也不用擔心資料瓶頸難題 Apr 29, 2024 pm 06:55 PM

哭死啊,全球狂煉大模型,一網路的資料不夠用,根本不夠用。訓練模型搞得跟《飢餓遊戲》似的,全球AI研究者,都在苦惱怎麼才能餵飽這群資料大胃王。尤其在多模態任務中,這問題尤其突出。一籌莫展之際,來自人大系的初創團隊,用自家的新模型,率先在國內把「模型生成數據自己餵自己」變成了現實。而且還是理解側和生成側雙管齊下,兩側都能產生高品質、多模態的新數據,對模型本身進行數據反哺。模型是啥?中關村論壇上剛露面的多模態大模型Awaker1.0。團隊是誰?智子引擎。由人大高瓴人工智慧學院博士生高一鑷創立,高

特斯拉機器人進廠打工,馬斯克:手的自由度今年將達到22個! 特斯拉機器人進廠打工,馬斯克:手的自由度今年將達到22個! May 06, 2024 pm 04:13 PM

特斯拉機器人Optimus最新影片出爐,已經可以在工廠裡打工了。正常速度下,它分揀電池(特斯拉的4680電池)是這樣的:官方還放出了20倍速下的樣子——在小小的「工位」上,揀啊揀啊揀:這次放出的影片亮點之一在於Optimus在廠子裡完成這項工作,是完全自主的,全程沒有人為的干預。而且在Optimus的視角之下,它還可以把放歪了的電池重新撿起來放置,主打一個自動糾錯:對於Optimus的手,英偉達科學家JimFan給出了高度的評價:Optimus的手是全球五指機器人裡最靈巧的之一。它的手不僅有觸覺

首個自主完成人類任務機器人出現,五指靈活速度超人,大模型加持虛擬空間訓練 首個自主完成人類任務機器人出現,五指靈活速度超人,大模型加持虛擬空間訓練 Mar 11, 2024 pm 12:10 PM

這週,由OpenAI、微軟、貝佐斯和英偉達投資的機器人公司FigureAI宣布獲得接近7億美元的融資,計劃在未來一年內研發出可獨立行走的人形機器人。而特斯拉的擎天柱也屢屢傳出好消息。沒人懷疑,今年會是人形機器人爆發的一年。一家位於加拿大的機器人公司SanctuaryAI最近發布了一款全新的人形機器人Phoenix。官方號稱它能以和人類一樣的速率自主完成許多工作。世界上第一台能以人類速度自主完成任務的機器人Pheonix可以輕輕地抓取、移動並優雅地將每個物件放置在它的左右兩側。它能夠自主辨識物體的

美國空軍高調展示首個AI戰鬥機!部長親自試駕全程未乾預,10萬行代碼試飛21次 美國空軍高調展示首個AI戰鬥機!部長親自試駕全程未乾預,10萬行代碼試飛21次 May 07, 2024 pm 05:00 PM

最近,軍事圈被這個消息刷屏了:美軍的戰鬥機,已經能由AI完成全自動空戰了。是的,就在最近,美軍的AI戰鬥機首次公開,揭開了神秘面紗。這架戰鬥機的全名是可變穩定性飛行模擬器測試飛機(VISTA),由美空軍部長親自搭乘,模擬了一對一的空戰。 5月2日,美國空軍部長FrankKendall在Edwards空軍基地駕駛X-62AVISTA升空注意,在一小時的飛行中,所有飛行動作都由AI自主完成! Kendall表示——在過去的幾十年中,我們一直在思考自主空對空作戰的無限潛力,但它始終顯得遙不可及。然而如今,

See all articles