記得四年前在學校第一次接觸到 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」,你就會忽然以爲「啊,好像在哪裏見過.....」。因此我想說的是「該出手時仍是得出手"。html
Entity Framework 是微軟家的orm框架,隨着 Entity Framework 不斷的完善強化,目前相信使用的比例相對其餘的orm來講仍是較高的。像我目前使用的最多的就是EF和Dapper。確實,EF用起來開發過程當中會方便不少,畢竟EF走過了這麼年頭,不管是成熟度,仍是性能等都提升了不少,也有不少開發者爲EF提供了擴展功能,如entity framework extended 等。並且做爲.net開發者來講項目通用性也很強,資料也多,微軟在這塊的更新力度也很給力。不過以前剛出的EF Core也存在一些坑,畢竟仍是初期階段,相信如今後面會愈來愈好的。nginx
Entity Framework 提供了三種開發模式,code first,db first,model first。目前用的最多的就屬code first了。至於這三種模式的簡單使用和區別,你們能夠參考下這篇文章。git
我曾聽一些朋友說過說EF使用起來性能不好,生成的sql語句很難看等。我以爲說這種話以前仍是先檢查下代碼或者多看下一些EF文章吧,要先確保本身沒給本身挖坑,而後才能指責別人的很差。若是真心以爲EF或者其餘的orm用起來很不爽,那就本身寫一個吧,我也曾經和同事用Dapper擴展一個通用的orm,當時是出於一種學習和使用方便的角度。程序員
這裏提供下 EF 通用數據層父類方法,其實網上也有不少人提供了本身項目中的 EF 通用數據層父類方法,因此這裏提供的並非最優和最好的選擇,只能說是能夠通用的類,方便你們學習和使用,具體代碼以下:github
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; } }
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 以前必定要 OrderBy var 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 }
上面的通用類是比較基礎簡單通用的,適合於單庫讀寫操做。對於EF實現讀寫分離,以前網上找過相似的參考文章,不少人文章都是使用 DbCommandInterceptor攔截器 來實現,具體的作法是經過攔截到sql語句,而後根據具體條件去判斷是走主庫仍是從庫。這種作法不是不行,只是我的感受不是很好擴展,並且要在攔截器裏面作限制判斷。算法
其實說白了EF自己就是一個讀寫分離的orm。用過EF的人知道,EF提供訪問數據庫的是 DbContext 這個對象,因此想實現讀寫分離的就很簡單了,只要在程序中使用兩個不一樣的DbContext對象,一個負責讀,一個負責寫就行了。sql
因此在上面提供的通用封裝類中稍微作下修改,修改以下DbContextFactory中獲取DbContext的方法,實現一個讀的DbContext和一個寫的DbContext對象的獲取。數據庫
這裏要注意下,對於讀的DbContext來講,要作下設置
1.使用 Database.SetInitializer(new NullDatabaseInitializer<ReadDbContext>()); 改變ef初始化策略,由於通常咱們使用讀寫分離的話從庫都是同步於主庫的,因此不使用ef的自動建立數據庫策略。
2.重寫 SaveChanges 方法,對應從庫來講,只提供讀取的功能,因此防止誤操做,這裏禁用掉SaveChanges方法,通常須要使用從讀的保存方法,就對外拋出異常。app
代碼以下:負載均衡
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 對象,而且把上面的讀寫方法坐下調整,修改後以下:
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 以前必定要 OrderBy var 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 }
這樣簡單的讀寫分離就實現了,實現邏輯也比較清晰,方便擴展。
通常作讀寫分離,都會作一主多從,特別對讀取量比較大的項目,這樣多庫讀取就能減輕讀庫的壓力。因此對於上面的方法,作下改造。
上面能夠看到,主庫和從庫都是經過 DbContextFactory 這個類來獲取的,在GetReadDbContext 方法中每次都是獲取 ReadDbContext 這個對象。那麼對於多個從庫的狀況下,每次讀取到底要去哪一個庫讀取數據呢?這裏就是一個算法規則的問題了,或者說是策略吧,若是使用過nginx的朋友就知道,nginx自己內部在實現負載均衡的時候提供了多種策略,好比輪詢,加權輪詢,ip_hash等策略。其實上面獲取同一個ReadDbContext 的方法也算一種策略,叫單一策略,每次都獲取單一的對象。
多從庫的狀況下,咱們簡單的來實現另外一種獲取策略,隨機策略,每次都隨機獲取到一個從庫的對象,這種是最簡單的策略,固然,正式使用的話你們能夠發揮本身的創造力,寫出多了的算法策略。
首先,定義一個策略接口,方便策略的擴展和切換,代碼以下:
/// <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 方法,經過策略接口來獲取,代碼以下:
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; } }
這樣簡單的一主多從也實現了。
http://www.cnblogs.com/zhaopei/p/5721789.html
http://www.cnblogs.com/GuZhenYin/p/5482288.html
全部的代碼提供給你們的更多的是一種思路和學習的參考,若是有什麼不足的地方也歡迎你們批評指正,若是以爲對你有幫助,不要吝嗇你的鼠標,幫忙點個星,點個贊吧。