為什麼分層? 不分層封裝的話,下麵的代碼就是上端直接依賴於下端,也就是UI層直接依賴於數據訪問層,分層一定要依賴抽象,滿足依賴倒置原則,所以我們要封裝,要分層 下麵這張圖和傳統的三層略有不同,不同之處在於,UI層不直接依賴於業務邏輯層,而是UI層依賴於業務邏輯抽象層IBLL,業務邏輯層不直接依賴於數 ...
- 為什麼分層?
不分層封裝的話,下麵的代碼就是上端直接依賴於下端,也就是UI層直接依賴於數據訪問層,分層一定要依賴抽象,滿足依賴倒置原則,所以我們要封裝,要分層
下麵這張圖和傳統的三層略有不同,不同之處在於,UI層不直接依賴於業務邏輯層,而是UI層依賴於業務邏輯抽象層IBLL,業務邏輯層不直接依賴於數據訪問層,而是業務邏輯層依賴於數據訪問抽象層IDAL
{ SchoolDBEntities dbContext = new SchoolDBEntities(); dbContext.Set<Student>().Where(s=>s.Student_ID == "0000000001"); }
- 封裝分層
1、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; } }
2、David.General.EF.Bussiness.Service(業務邏輯實現層)
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 } }
- 整合Unity,實現IOC,依賴註入解決問題
雖然封裝完了,但是還是帶來了2個問題,問題如下代碼所示,所以我們需要解決下麵兩個問題
問題1:通過封裝,完成了通過Service來完成對資料庫的訪問,但是右邊 new StudentService()是細節,不滿足依賴倒置原則,應該面向抽象編程
問題2:構造new StudentService()的時候需要一個Context,不能每次都SchoolDBEntities dbContext = new SchoolDBEntities();
{ 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(); } }
1、Nuget引入Unity相關dll
2、配置Unity.Config
.2.1、給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.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>
3、調用服務
如下調用代碼和截圖所示
首先:我們構建學生服務的時候,沒有出現細節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(); } } }