不分層封裝的話,下面的代碼就是上端直接依賴於下端,也就是UI層直接依賴於數據訪問層,分層必定要依賴抽象,知足依賴倒置原則,因此咱們要封裝,要分層sql
下面這張圖和傳統的三層略有不一樣,不一樣之處在於,UI層不直接依賴於業務邏輯層,而是UI層依賴於業務邏輯抽象層IBLL,業務邏輯層不直接依賴於數據訪問層,而是業務邏輯層依賴於數據訪問抽象層IDAL數據庫
{ SchoolDBEntities dbContext = new SchoolDBEntities(); dbContext.Set<Student>().Where(s=>s.Student_ID == "0000000001"); }
一、David.General.EF.Bussiness.Interface(IBLL--業務邏輯抽象層)編程
繼承IDisposable的目的是爲了能夠使用using,是爲了釋放Context函數
IBaseService至關於上圖的IBLL(業務邏輯抽象層),DAL已經不存在了,由於EF已經取代了DAL層測試
namespace David.General.EF.Bussiness.Interface { public interface IBaseService : IDisposable//能夠使用using,是爲了釋放Context { #region Query /// <summary> /// 根據id主鍵查詢實體 /// </summary> /// <param name="id"></param> /// <returns></returns> T Find<T>(object id) where T : class; /// <summary> /// 提供對單表的查詢 /// 不推薦對外直接開放 ///IQueryable支持表達式目錄樹 /// </summary> /// <returns>IQueryable類型集合</returns> [Obsolete("儘可能避免使用,using 帶表達式目錄樹的 代替")] IQueryable<T> Set<T>() where T : class; /// <summary> /// 查詢,傳入表達式目錄樹 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="funcWhere">表達式目錄樹</param> /// <returns>IQueryable類型集合</returns> IQueryable<T> Query<T>(Expression<Func<T, bool>> funcWhere) where T : class; /// <summary> /// 分頁查詢 /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="S"></typeparam> /// <param name="funcWhere"></param> /// <param name="pageSize"></param> /// <param name="pageIndex"></param> /// <param name="funcOrderby"></param> /// <param name="isAsc"></param> /// <returns></returns> PageResult<T> QueryPage<T, S>(Expression<Func<T, bool>> funcWhere, int pageSize, int pageIndex, Expression<Func<T, S>> funcOrderby, bool isAsc = true) where T : class; #endregion #region Add /// <summary> /// 新增數據 /// </summary> /// <param name="t"></param> /// <returns>返回帶主鍵的實體</returns> T Insert<T>(T t) where T : class; /// <summary> /// 新增數據 /// 多條sql 一個鏈接,事務插入 /// </summary> /// <param name="tList"></param> IEnumerable<T> Insert<T>(IEnumerable<T> tList) where T : class; #endregion #region Update /// <summary> /// 更新數據 /// </summary> /// <param name="t"></param> void Update<T>(T t) where T : class; /// <summary> /// 更新數據 /// </summary> /// <param name="tList"></param> void Update<T>(IEnumerable<T> tList) where T : class; #endregion #region Delete /// <summary> /// 根據主鍵刪除數據 /// </summary> /// <param name="t"></param> void Delete<T>(int Id) where T : class; /// <su+mary> /// 刪除數據 /// </summary> /// <param name="t"></param> void Delete<T>(T t) where T : class; /// <summary> /// 刪除數據 /// </summary> /// <param name="tList"></param> void Delete<T>(IEnumerable<T> tList) where T : class; #endregion #region Other /// <summary> /// 當即保存所有修改 /// 把增/刪的savechange給放到這裏,是爲了保證事務的 /// </summary> void Commit(); /// <summary> /// 執行sql 返回集合 /// </summary> /// <param name="sql"></param> /// <param name="parameters"></param> /// <returns></returns> IQueryable<T> ExcuteQuery<T>(string sql, SqlParameter[] parameters) where T : class; /// <summary> /// 執行sql,無返回 /// </summary> /// <param name="sql"></param> /// <param name="parameters"></param> void Excute<T>(string sql, SqlParameter[] parameters) where T : class; #endregion } } public class PageResult<T> { public int TotalCount { get; set; } public int PageIndex { get; set; } public int PageSize { get; set; } public List<T> DataList { get; set; } }
二、David.General.EF.Bussiness.Service(業務邏輯實現層)this
namespace David.General.EF.Bussiness.Service { public class BaseService : IBaseService { #region Identity /// <summary> /// protected--保證只有子類能夠看得見 /// { get; private set; }--保證只有子類能夠獲取,子類不能修改,只有本身能夠修改 /// </summary> protected DbContext Context { get; private set; } /// <summary> /// 構造函數注入 /// 一個請求一個,不能全局一個,應該一個實例一個 /// </summary> /// <param name="context"></param> public BaseService(DbContext context) { this.Context = context; } #endregion Identity #region Query /// <summary> /// 經過Id獲得實體 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="id"></param> /// <returns></returns> public T Find<T>(object id) where T : class { return this.Context.Set<T>().Find(id); } /// <summary> /// 不該該暴露給上端使用者,儘可能少用 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> [Obsolete("儘可能避免使用,using 帶表達式目錄樹的代替")] public IQueryable<T> Set<T>() where T : class { return this.Context.Set<T>(); } /// <summary> /// 這纔是合理的作法,上端給條件,這裏查詢 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="funcWhere"></param> /// <returns></returns> public IQueryable<T> Query<T>(Expression<Func<T, bool>> funcWhere) where T : class { return this.Context.Set<T>().Where<T>(funcWhere); } /// <summary> /// 分頁查詢 /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="S"></typeparam> /// <param name="funcWhere">查詢條件表達式目錄樹</param> /// <param name="pageSize">頁大小</param> /// <param name="pageIndex">頁索引</param> /// <param name="funcOrderby">按什麼字段排序</param> /// <param name="isAsc">升序仍是降序</param> /// <returns></returns> public PageResult<T> QueryPage<T, S>(Expression<Func<T, bool>> funcWhere, int pageSize, int pageIndex, Expression<Func<T, S>> funcOrderby, bool isAsc = true) where T : class { var list = this.Set<T>(); if (funcWhere != null) { list = list.Where<T>(funcWhere); } if (isAsc) { list = list.OrderBy(funcOrderby); } else { list = list.OrderByDescending(funcOrderby); } PageResult<T> result = new PageResult<T>() { DataList = list.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(), PageIndex = pageIndex, PageSize = pageSize, TotalCount = this.Context.Set<T>().Count(funcWhere) }; return result; } #endregion #region Insert /// <summary> /// 插入 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <returns></returns> public T Insert<T>(T t) where T : class { this.Context.Set<T>().Add(t); return t; } /// <summary> /// 插入集合 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tList"></param> /// <returns></returns> public IEnumerable<T> Insert<T>(IEnumerable<T> tList) where T : class { this.Context.Set<T>().AddRange(tList); return tList; } #endregion #region Update /// <summary> /// 是沒有實現查詢,直接更新的,須要Attach和State /// /// 若是是已經在context,只能再封裝一個(在具體的service) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> public void Update<T>(T t) where T : class { if (t == null) throw new Exception("t is null"); this.Context.Set<T>().Attach(t);//將數據附加到上下文,支持實體修改和新實體,重置爲UnChanged this.Context.Entry<T>(t).State = EntityState.Modified;//全字段更新 } /// <summary> /// 集合修改 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tList"></param> public void Update<T>(IEnumerable<T> tList) where T : class { foreach (var t in tList) { this.Context.Set<T>().Attach(t); this.Context.Entry<T>(t).State = EntityState.Modified; } } /// <summary> /// 更新數據,指定更新哪些列,哪怕有些列值發生了變化,沒有指定列也不能修改 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> public void UpdateSpecifyFiled<T>(T t, List<string> filedList) where T : class { this.Context.Set<T>().Attach(t);//將數據附加到上下文 foreach(var filed in filedList) { this.Context.Entry<T>(t).Property(filed).IsModified = true;//指定某字段被改過 } } #endregion #region Delete /// <summary> /// 先附加 再刪除 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> public void Delete<T>(T t) where T : class { if (t == null) throw new Exception("t is null"); this.Context.Set<T>().Attach(t); this.Context.Set<T>().Remove(t); } /// <summary> /// 還能夠增長非即時commit版本的, /// 作成protected /// </summary> /// <typeparam name="T"></typeparam> /// <param name="Id"></param> public void Delete<T>(int Id) where T : class { T t = this.Find<T>(Id);//也能夠附加 if (t == null) throw new Exception("t is null"); this.Context.Set<T>().Remove(t); } /// <summary> /// 刪除集合 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tList"></param> public void Delete<T>(IEnumerable<T> tList) where T : class { foreach (var t in tList) { this.Context.Set<T>().Attach(t); } this.Context.Set<T>().RemoveRange(tList); } #endregion #region Other /// <summary> /// 一次性提交 /// </summary> public void Commit() { this.Context.SaveChanges(); } /// <summary> /// sql語句查詢 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="sql"></param> /// <param name="parameters"></param> /// <returns></returns> public IQueryable<T> ExcuteQuery<T>(string sql, SqlParameter[] parameters) where T : class { return this.Context.Database.SqlQuery<T>(sql, parameters).AsQueryable(); } /// <summary> /// 執行sql語句 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="sql"></param> /// <param name="parameters"></param> public void Excute<T>(string sql, SqlParameter[] parameters) where T : class { DbContextTransaction trans = null; try { trans = this.Context.Database.BeginTransaction(); this.Context.Database.ExecuteSqlCommand(sql, parameters); trans.Commit(); } catch (Exception ex) { if (trans != null) trans.Rollback(); throw ex; } } public virtual void Dispose() { if (this.Context != null) { this.Context.Dispose(); } } #endregion } }
雖然封裝完了,可是仍是帶來了2個問題,問題以下代碼所示,因此咱們須要解決下面兩個問題
問題1:經過封裝,完成了經過Service來完成對數據庫的訪問,可是右邊 new StudentService()是細節,不知足依賴倒置原則,應該面向抽象編程
問題2:構造new StudentService()的時候須要一個Context,不能每次都SchoolDBEntities dbContext = new SchoolDBEntities();spa
{ SchoolDBEntities dbContext = new SchoolDBEntities(); using (IStudentService iStudentService = new StudentService(dbContext)) { Student student = iStudentService.Find<Student>("0000000001"); Student student1 = new Student(); student1.Student_ID = "1111111"; student1.Student_Name = "Student1"; iStudentService.Insert(student1); iStudentService.Commit(); } }
一、Nuget引入Unity相關dllcode
二、配置Unity.Configblog
.2.一、給BaseService注入DbContext排序
<register type="System.Data.Entity.DbContext, EntityFramework" mapTo="David.General.EF.Model.SchoolDBEntities,David.General.EF.Model"/>
type中逗號前是完整類型名稱,也就是命名空間System.Data.Entity+類名DbContext,逗號後是dll名稱EntityFramework
mapTo中逗號前是完整類型名稱,也就是命名空間David.General.EF.Model+類名SchoolDBEntities,逗號後是dll名稱David.General.EF.Model
2.二、給IStudentService注入StudentService
<register type="David.General.EF.Bussiness.Interface.IStudentService,David.General.EF.Bussiness.Interface" mapTo="David.General.EF.Bussiness.Service.StudentService, David.General.EF.Bussiness.Service">
type中逗號前是完整類型名稱,也就是命名空間David.General.EF.Bussiness.Interface+接口名IStudentService,逗號後是dll名稱David.General.EF.Bussiness.Interface
mapTo中逗號前是完整類型名稱,也就是命名空間David.General.EF.Bussiness.Service+類名StudentService,逗號後是dll名稱David.General.EF.Bussiness.Service
<configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/> </configSections> <unity> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/> <containers> <container name="MyContainer"> <extension type="Interception"/> <register type="System.Data.Entity.DbContext, EntityFramework" mapTo="David.General.EF.Model.SchoolDBEntities,David.General.EF.Model"/> <register type="David.General.EF.Bussiness.Interface.IStudentService,David.General.EF.Bussiness.Interface" mapTo="David.General.EF.Bussiness.Service.StudentService, David.General.EF.Bussiness.Service"> </register> </container> </containers> </unity> </configuration>
三、調用服務
以下調用代碼和截圖所示
首先:咱們構建學生服務的時候,沒有出現細節StudentService
其次:在構建學生服務的時候,沒有顯式的去傳入DbContext,StudentService繼承BaseService,StudentService的構造函數的參數DbContext是來源於BaseService,而BaseService依賴的DbContext是經過構造函數注入進來的
{ //UnityConfig配置只用初始化一次,因此咱們把讀取UnityConfig配置封裝一下 //使用單例模式,l利用靜態構造函數只初始化1次的特色,達到配置只初始化1次 Unity.IUnityContainer container = ContainerFactory.GetContainer(); //IOC:去掉細節依賴,下降耦合,加強擴展性 using (IStudentService iStudentService = container.Resolve<IStudentService>()) { Student student = iStudentService.Find<Student>("0000000001"); //測試指定更新 Student oldStudent = new Student() { Student_ID = "0000020001", Student_Name = "豬豬",
Student_Sex = "男" }; List<string> filedList = new List<string>(); filedList.Add("Student_Name"); iStudentService.UpdateSpecifyFiled<Student>(oldStudent, filedList); iStudentService.Commit(); }
}
namespace David.General.EF.Bussiness.Service { public class StudentService : BaseService,IStudentService { public StudentService(DbContext context) : base(context) { } /// <summary> /// 記錄學生打架 /// </summary> /// <param name="student"></param> public void RecordStudentFight(Student student) { base.Insert(student); this.Commit(); } } }