溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何實(shí)現(xiàn)EF通用數(shù)據(jù)層封裝類

發(fā)布時(shí)間:2020-07-13 10:56:00 來(lái)源:億速云 閱讀:429 作者:Leah 欄目:編程語(yǔ)言

這篇文章運(yùn)用簡(jiǎn)單易懂的例子給大家介紹如何實(shí)現(xiàn)EF通用數(shù)據(jù)層封裝類,代碼非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

淺談orm

  記得四年前在學(xué)校第一次接觸到 Ling to Sql,那時(shí)候瞬間發(fā)現(xiàn)不用手寫(xiě)sql語(yǔ)句是多么的方便,后面慢慢的接觸了許多orm框架,像 EF,Dapper,Hibernate,ServiceStack.OrmLite 等。當(dāng)然每種orm都有各自的優(yōu)勢(shì),也有不足的地方。園子里也有很多大神開(kāi)源了他們寫(xiě)的orm,如SqlSugar,Chloe.ORM,CYQ.Data 等。先不說(shuō)這些開(kāi)源的orm使用度怎么樣,我覺(jué)得起碼從開(kāi)源的精神上就很可嘉了,我也曾下載過(guò)這幾位大神的源碼進(jìn)行學(xué)習(xí)。
  所有orm最終體現(xiàn)的一點(diǎn)就是方便,減少程序員重復(fù)性的工作,當(dāng)然目前還有一些公司還是使用手寫(xiě)sql的方式來(lái)做,我覺(jué)得整個(gè)項(xiàng)目都使用手寫(xiě)sql來(lái)做真的是有點(diǎn)閑到蛋疼,并不是不推薦手寫(xiě)sql的方式,只是個(gè)人覺(jué)得最基本的增刪改查這些都手寫(xiě)的話,那其實(shí)考驗(yàn)不是能力,而是耐力。有人說(shuō)手寫(xiě)sql的方式可控性強(qiáng),性能高,我想說(shuō)的是orm也能做到,關(guān)鍵是你怎么去使用。
  orm的優(yōu)點(diǎn)非常明顯,開(kāi)發(fā)便捷,但或許也是由于這個(gè)優(yōu)點(diǎn),讓很多偷懶的程序員也會(huì)漸漸忘了sql語(yǔ)句的寫(xiě)法,我遇到過(guò)很多的程序員朋友用了EF后,手寫(xiě)sql,視圖、存儲(chǔ)過(guò)程這些都不想用了,我個(gè)人覺(jué)手寫(xiě)sql這種還是必要的。不然某一天你看到別人的程序里面寫(xiě)著 “exec  xxxx”,你就會(huì)突然覺(jué)得“啊,好像在哪里見(jiàn)過(guò).....”。所以我想說(shuō)的是“該出手時(shí)還是得出手"。

淺談Entity Framework

  Entity Framework 是微軟家的orm框架,隨著 Entity Framework 不斷的完善強(qiáng)化,目前相信使用的比例相對(duì)其他的orm來(lái)說(shuō)還是較高的。像我目前使用的最多的就是EF和Dapper。確實(shí),EF用起來(lái)開(kāi)發(fā)過(guò)程中會(huì)方便很多,畢竟EF走過(guò)了這么年頭,無(wú)論是成熟度,還是性能等都提高了很多,也有很多開(kāi)發(fā)者為EF提供了擴(kuò)展功能,如entity framework extended 等。而且作為.net開(kāi)發(fā)者來(lái)說(shuō)項(xiàng)目通用性也很強(qiáng),資料也多,微軟在這塊的更新力度也很給力。不過(guò)之前剛出的EF Core也存在一些坑,畢竟還是初期階段,相信現(xiàn)在后面會(huì)越來(lái)越好的。

  Entity Framework  提供了三種開(kāi)發(fā)模式,code first,db first,model first。目前用的最多的就屬code first了。至于這三種模式的簡(jiǎn)單使用和區(qū)別,大家可以參考下這篇文章。

  我曾聽(tīng)一些朋友說(shuō)過(guò)說(shuō)EF使用起來(lái)性能很差,生成的sql語(yǔ)句很難看等。我覺(jué)得說(shuō)這種話之前還是先檢查下代碼或者多看下一些EF文章吧,要先確保自己沒(méi)給自己挖坑,然后才能指責(zé)別人的不好。如果真心覺(jué)得EF或者其他的orm用起來(lái)很不爽,那就自己寫(xiě)一個(gè)吧,我也曾經(jīng)和同事用Dapper擴(kuò)展一個(gè)通用的orm,當(dāng)時(shí)是出于一種學(xué)習(xí)和使用方便的角度。

Entity Framework 通用數(shù)據(jù)層類

  這里提供下 EF 通用數(shù)據(jù)層父類方法,其實(shí)網(wǎng)上也有很多人提供了自己項(xiàng)目中的 EF 通用數(shù)據(jù)層父類方法,所以這里提供的并不是最優(yōu)和最好的選擇,只能說(shuō)是可以通用的類,方便大家學(xué)習(xí)和使用,具體代碼如下:

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 數(shù)據(jù)層通用操作類
如何實(shí)現(xiàn)EF通用數(shù)據(jù)層封裝類如何實(shí)現(xiàn)EF通用數(shù)據(jù)層封裝類
 public class DbBase
    {protected DbContext Db = new DbContextFactory().GetDbContext();        #region 自定義其他方法/// <summary>/// 執(zhí)行存儲(chǔ)過(guò)程或自定義sql語(yǔ)句--返回集合(自定義返回類型)/// </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)
        {//存儲(chǔ)過(guò)程(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>/// 自定義語(yǔ)句和存儲(chǔ)過(guò)程的增刪改--返回影響的行數(shù)/// </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)
        {//存儲(chǔ)過(guò)程(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數(shù)據(jù)庫(kù) 數(shù)據(jù)層 父類/// </summary>/// <typeparam name="T"></typeparam>public class DbBase<T> : DbBase where T : class, new()
    {#region INSERT/// <summary>/// 新增 實(shí)體/// </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>/// 根據(jù)模型刪除/// </summary>/// <param name="model">包含要?jiǎng)h除id的對(duì)象</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>/// 單個(gè)對(duì)象指定列修改/// </summary>/// <param name="model">要修改的實(shí)體對(duì)象</param>/// <param name="proNames">要修改的 屬性 名稱</param>/// <param name="isProUpdate"></param>/// <returns></returns>public void Update(T model, List<string> proNames, bool isProUpdate = true)
        {//將 對(duì)象 添加到 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>/// 單個(gè)對(duì)象修改/// </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>/// 批量統(tǒng)一修改/// </summary>/// <param name="model">要修改的實(shí)體對(duì)象</param>/// <param name="whereLambda">查詢條件</param>/// <param name="modifiedProNames"></param>/// <returns></returns>public void Update(T model, Expression<Func<T, bool>> whereLambda, params string[] modifiedProNames)
        {//查詢要修改的數(shù)據(jù)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("指定修改的字段名稱有誤或?yàn)榭?quot;);
            }foreach (var item in dictPros)
            {
                PropertyInfo proInfo = item.Value;//取出 要修改的值object newValue = proInfo.GetValue(model, null);//批量設(shè)置 要修改 對(duì)象的 屬性foreach (T oModel in listModifing)
                {//為 要修改的對(duì)象 的 要修改的屬性 設(shè)置新的值proInfo.SetValue(oModel, newValue, null);
                }
            }

        }#endregion UPDATE#region SELECT/// <summary>/// 根據(jù)主鍵查詢/// </summary>/// <param name="id"></param>/// <returns></returns>public T FindById(dynamic id)
        {return Db.Set<T>().Find(id);
        }/// <summary>/// 獲取默認(rèn)一條數(shù)據(jù),沒(méi)有則為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>/// 獲取全部數(shù)據(jù)/// </summary>/// <returns></returns>public List<T> GetAll(string ordering = null)
        {return ordering == null? Db.Set<T>().ToList()
                : Db.Set<T>().OrderBy(ordering).ToList();
        }/// <summary>/// 帶條件查詢獲取數(shù)據(jù)/// </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>/// 帶條件查詢獲取數(shù)據(jù)/// </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>/// 獲取數(shù)量/// </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>/// 判斷對(duì)象是否存在/// </summary>/// <param name="whereLambd"></param>/// <returns></returns>public bool Any(Expression<Func<T, bool>> whereLambd)
        {return Db.Set<T>().Where(whereLambd).Any();
        }/// <summary>/// 分頁(yè)查詢/// </summary>/// <param name="pageIndex">當(dāng)前頁(yè)碼</param>/// <param name="pageSize">每頁(yè)大小</param>/// <param name="rows">總條數(shù)</param>/// <param name="orderBy">排序條件(一定要有)</param>/// <param name="whereLambda">查詢添加(可有,可無(wú))</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>/// 分頁(yè)查詢/// </summary>/// <param name="pageIndex">當(dāng)前頁(yè)碼</param>/// <param name="pageSize">每頁(yè)大小</param>/// <param name="rows">總條數(shù)</param>/// <param name="ordering">排序條件(一定要有)</param>/// <param name="whereLambda">查詢添加(可有,可無(wú))</param>/// <returns></returns>public List<T> Page(int pageIndex, int pageSize, out int rows, string ordering, Expression<Func<T, bool>> whereLambda = null)
        {// 分頁(yè) 一定注意: 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>/// 查詢轉(zhuǎn)換/// </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>/// 執(zhí)行存儲(chǔ)過(guò)程或自定義sql語(yǔ)句--返回集合/// </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

擴(kuò)展類,實(shí)現(xiàn)讀寫(xiě)分離

  上面的通用類是比較基礎(chǔ)簡(jiǎn)單通用的,適合于單庫(kù)讀寫(xiě)操作。對(duì)于EF實(shí)現(xiàn)讀寫(xiě)分離,之前網(wǎng)上找過(guò)類似的參考文章,很多人文章都是使用 DbCommandInterceptor攔截器 來(lái)實(shí)現(xiàn),具體的做法是通過(guò)攔截到sql語(yǔ)句,然后根據(jù)具體條件去判斷是走主庫(kù)還是從庫(kù)。這種做法不是不行,只是個(gè)人感覺(jué)不是很好擴(kuò)展,而且要在攔截器里面做限制判斷。

  其實(shí)說(shuō)白了EF本身就是一個(gè)讀寫(xiě)分離的orm。用過(guò)EF的人知道,EF提供訪問(wèn)數(shù)據(jù)庫(kù)的是 DbContext 這個(gè)對(duì)象,所以想實(shí)現(xiàn)讀寫(xiě)分離的就很簡(jiǎn)單了,只要在程序中使用兩個(gè)不同的DbContext對(duì)象,一個(gè)負(fù)責(zé)讀,一個(gè)負(fù)責(zé)寫(xiě)就好了。

  所以在上面提供的通用封裝類中稍微做下修改,修改如下DbContextFactory中獲取DbContext的方法,實(shí)現(xiàn)一個(gè)讀的DbContext和一個(gè)寫(xiě)的DbContext對(duì)象的獲取。

  這里要注意下,對(duì)于讀的DbContext來(lái)說(shuō),要做下設(shè)置
  1.使用 Database.SetInitializer(new NullDatabaseInitializer<ReadDbContext>()); 改變ef初始化策略,因?yàn)橐话阄覀兪褂米x寫(xiě)分離的話從庫(kù)都是同步于主庫(kù)的,所以不使用ef的自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)策略。
  2.重寫(xiě) SaveChanges 方法,對(duì)應(yīng)從庫(kù)來(lái)說(shuō),只提供讀取的功能,所以防止誤操作,這里禁用掉SaveChanges方法,一般需要使用從讀的保存方法,就對(duì)外拋出異常。

  代碼如下:

支持讀寫(xiě)分離的 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;
        }
    }

對(duì)應(yīng)的 DbBase 類也做下修改,主要將上面的Db對(duì)象改作 MasterDb SlaveDb 對(duì)象,并且把上面的讀寫(xiě)方法坐下調(diào)整,修改后如下:

支持讀寫(xiě)分離的 DbBase類
如何實(shí)現(xiàn)EF通用數(shù)據(jù)層封裝類如何實(shí)現(xiàn)EF通用數(shù)據(jù)層封裝類
public class DbBase
    {//是否讀寫(xiě)分離(可以配置在配置文件中)private static readonly bool IsReadWriteSeparation = true;#region EF上下文對(duì)象(主庫(kù))protected DbContext MasterDb => _masterDb.Value;private readonly Lazy<DbContext> _masterDb = new Lazy<DbContext>(() => new DbContextFactory().GetWriteDbContext());#endregion EF上下文對(duì)象(主庫(kù))#region EF上下文對(duì)象(從庫(kù))protected DbContext SlaveDb => IsReadWriteSeparation ? _slaveDb.Value : _masterDb.Value;private readonly Lazy<DbContext> _slaveDb = new Lazy<DbContext>(() => new DbContextFactory().GetReadDbContext());#endregion EF上下文對(duì)象(從庫(kù))#region 自定義其他方法/// <summary>/// 執(zhí)行存儲(chǔ)過(guò)程或自定義sql語(yǔ)句--返回集合(自定義返回類型)/// </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)
        {//存儲(chǔ)過(guò)程(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>/// 自定義語(yǔ)句和存儲(chǔ)過(guò)程的增刪改--返回影響的行數(shù)/// </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)
        {//存儲(chǔ)過(guò)程(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數(shù)據(jù)庫(kù) 數(shù)據(jù)層 父類/// </summary>/// <typeparam name="T"></typeparam>public class DbBase<T> : DbBase where T : class, new()
    {#region INSERT/// <summary>/// 新增 實(shí)體/// </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>/// 根據(jù)模型刪除/// </summary>/// <param name="model">包含要?jiǎng)h除id的對(duì)象</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>/// 單個(gè)對(duì)象指定列修改/// </summary>/// <param name="model">要修改的實(shí)體對(duì)象</param>/// <param name="proNames">要修改的 屬性 名稱</param>/// <param name="isProUpdate"></param>/// <returns></returns>public void Update(T model, List<string> proNames, bool isProUpdate = true)
        {//將 對(duì)象 添加到 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>/// 單個(gè)對(duì)象修改/// </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>/// 批量統(tǒng)一修改/// </summary>/// <param name="model">要修改的實(shí)體對(duì)象</param>/// <param name="whereLambda">查詢條件</param>/// <param name="modifiedProNames"></param>/// <returns></returns>public void Update(T model, Expression<Func<T, bool>> whereLambda, params string[] modifiedProNames)
        {//查詢要修改的數(shù)據(jù)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("指定修改的字段名稱有誤或?yàn)榭?quot;);
            }foreach (var item in dictPros)
            {
                PropertyInfo proInfo = item.Value;//取出 要修改的值object newValue = proInfo.GetValue(model, null);//批量設(shè)置 要修改 對(duì)象的 屬性foreach (T oModel in listModifing)
                {//為 要修改的對(duì)象 的 要修改的屬性 設(shè)置新的值proInfo.SetValue(oModel, newValue, null);
                }
            }
        }#endregion UPDATE#region SELECT/// <summary>/// 根據(jù)主鍵查詢/// </summary>/// <param name="id"></param>/// <returns></returns>public T FindById(dynamic id)
        {return SlaveDb.Set<T>().Find(id);
        }/// <summary>/// 獲取默認(rèn)一條數(shù)據(jù),沒(méi)有則為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>/// 獲取全部數(shù)據(jù)/// </summary>/// <returns></returns>public List<T> GetAll(string ordering = null)
        {return ordering == null? SlaveDb.Set<T>().ToList()
                : SlaveDb.Set<T>().OrderBy(ordering).ToList();
        }/// <summary>/// 帶條件查詢獲取數(shù)據(jù)/// </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>/// 帶條件查詢獲取數(shù)據(jù)/// </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>/// 獲取數(shù)量/// </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>/// 判斷對(duì)象是否存在/// </summary>/// <param name="whereLambd"></param>/// <returns></returns>public bool Any(Expression<Func<T, bool>> whereLambd)
        {return SlaveDb.Set<T>().Where(whereLambd).Any();
        }/// <summary>/// 分頁(yè)查詢/// </summary>/// <param name="pageIndex">當(dāng)前頁(yè)碼</param>/// <param name="pageSize">每頁(yè)大小</param>/// <param name="rows">總條數(shù)</param>/// <param name="orderBy">排序條件(一定要有)</param>/// <param name="whereLambda">查詢添加(可有,可無(wú))</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>/// 分頁(yè)查詢/// </summary>/// <param name="pageIndex">當(dāng)前頁(yè)碼</param>/// <param name="pageSize">每頁(yè)大小</param>/// <param name="rows">總條數(shù)</param>/// <param name="ordering">排序條件(一定要有)</param>/// <param name="whereLambda">查詢添加(可有,可無(wú))</param>/// <returns></returns>public List<T> Page(int pageIndex, int pageSize, out int rows, string ordering, Expression<Func<T, bool>> whereLambda = null)
        {// 分頁(yè) 一定注意: 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>/// 查詢轉(zhuǎn)換/// </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>/// 執(zhí)行存儲(chǔ)過(guò)程或自定義sql語(yǔ)句--返回集合/// </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

  這樣簡(jiǎn)單的讀寫(xiě)分離就實(shí)現(xiàn)了,實(shí)現(xiàn)邏輯也比較清晰,方便擴(kuò)展。

進(jìn)一步改造,實(shí)現(xiàn)多從庫(kù)讀取

  一般做讀寫(xiě)分離,都會(huì)做一主多從,特別對(duì)讀取量比較大的項(xiàng)目,這樣多庫(kù)讀取就能減輕讀庫(kù)的壓力。所以對(duì)于上面的方法,做下改造。
  上面可以看到,主庫(kù)和從庫(kù)都是通過(guò) DbContextFactory 這個(gè)類來(lái)獲取的,在GetReadDbContext 方法中每次都是獲取 ReadDbContext 這個(gè)對(duì)象。那么對(duì)于多個(gè)從庫(kù)的情況下,每次讀取到底要去哪個(gè)庫(kù)讀取數(shù)據(jù)呢?這里就是一個(gè)算法規(guī)則的問(wèn)題了,或者說(shuō)是策略吧,如果使用過(guò)nginx的朋友就知道,nginx本身內(nèi)部在實(shí)現(xiàn)負(fù)載均衡的時(shí)候提供了多種策略,比如輪詢,加權(quán)輪詢,ip_hash等策略。其實(shí)上面獲取同一個(gè)ReadDbContext 的方法也算一種策略,叫單一策略,每次都獲取單一的對(duì)象。

  多從庫(kù)的情況下,我們簡(jiǎn)單的來(lái)實(shí)現(xiàn)另一種獲取策略,隨機(jī)策略,每次都隨機(jī)獲取到一個(gè)從庫(kù)的對(duì)象,這種是最簡(jiǎn)單的策略,當(dāng)然,正式使用的話大家可以發(fā)揮自己的創(chuàng)造力,寫(xiě)出多了的算法策略。

首先,定義一個(gè)策略接口,方便策略的擴(kuò)展和切換,代碼如下:

IReadDbStrategy 接口
 /// <summary>
  /// 從數(shù)據(jù)庫(kù)獲取策略接口  /// </summary>
  public interface IReadDbStrategy
  {      /// <summary>  /// 獲取讀庫(kù)      /// </summary>  /// <returns></returns>      DbContext GetDbContext();
  }

單從庫(kù)情況下,定義一個(gè)單一策略,代碼如下:

單一策略
   /// <summary>
   /// 單一策略   /// </summary>
   public class SingleStrategy : IReadDbStrategy
   {       public DbContext GetDbContext()
       {           return new ReadDbContext();
       }
   }

多從庫(kù)情況下,定義一個(gè)隨機(jī)策略,代碼如下:

隨機(jī)策略
    /// <summary>/// 隨機(jī)策略/// </summary>public class RandomStrategy : IReadDbStrategy
    {//所有讀庫(kù)類型public static List<Type> DbTypes; static RandomStrategy()
        {
            LoadDbs();
        } //加載所有的讀庫(kù)類型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;
        }
    }

  這樣,所有從庫(kù)我們都基于策略去獲取,擴(kuò)展也比較方便。修改下 DbContextFactory 類的 GetReadDbContext 方法,通過(guò)策略接口來(lái)獲取,代碼如下:

支持一主多從的 DbContextFactory 類
  public class DbContextFactory
    {//todo:這里可以自己通過(guò)注入的方式來(lái)實(shí)現(xiàn),就會(huì)更加靈活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;
        }
    }

  這樣簡(jiǎn)單的一主多從也實(shí)現(xiàn)了。

  
  

關(guān)于如何實(shí)現(xiàn)EF通用數(shù)據(jù)層封裝類就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI