SportsStore是《精通ASP.NET MVC3框架(第三版)》中演示的MVC專案,在該專案中涵蓋了MVC的眾多方面,包括:使用DI容器、URL優化、導航、分頁、購物車、訂單、產品管理、影象上傳......是不錯的MVC實踐專案,但該專案不是放在多層框架下開發的,離真實專案還有一段距離。本系列將嘗試在多層框架下實現SportsStore專案,並用自己的方式實現一些功能。
本篇為系列第二篇,包括:
■ 4、三層架構設計
□ 4.2 建立DbSession層 資料訪問層的統一入口
※ 4.2.1 MySportsStore.IDAL詳解
※ 4.2.2 MySportsStore.DAL詳解
□ 4.3 建立BLL層
※ 4.3.1 MySportsStore.IBLL詳解
※ 4.3.2 MySportsStore.BLL詳解
4.2 建立DbSession層 資料訪問層的統一入口
DbSession層主要做了3件事:
1、提交所有變化
2、拿到各個IXXXRepository型別
3、執行SQL語句
4.2.1 MySportsStore.IDAL詳解
→IDbSession介面,資料庫訪問層的統一入口
using System.Data.SqlClient; namespace MySportsStore.IDAL { public interface IDbSession { //獲取所有的倉儲介面 IProductRepository ProductRepository { get; set; } //儲存所有變化 int SaveChanges(); //執行sql語句 int ExeucteSql(string sql, params SqlParameter[] paras); } }
→IDbSessionFactory介面,IDbSession介面的抽象工廠
在BaseRepository中會用到IDbSession的例項,我們藉助"抽象工廠"生產IDbSession的例項。
namespace MySportsStore.IDAL { public interface IDbSessionFactory { IDbSession GetCurrentDbSession(); } }
4.2.2 MySportsStore.DAL詳解
→DbSession,對IDbSession介面的實現
using System.Data.Entity; using MySportsStore.IDAL; namespace MySportsStore.DAL { public class DbSession : IDbSession { private IProductRepository _ProductRepository; public IProductRepository ProductRepository { get { if (_ProductRepository == null) { _ProductRepository = new ProductRepository(); } return _ProductRepository; } set { _ProductRepository = value; } } public int SaveChanges() { IDbContextFactory dbFactory = new DbContextFactory(); DbContext db = dbFactory.GetCurrentThreadInstance(); return db.SaveChanges(); } public int ExeucteSql(string sql, params System.Data.SqlClient.SqlParameter[] paras) { IDbContextFactory dbFactory = new DbContextFactory(); DbContext db = dbFactory.GetCurrentThreadInstance(); return db.Database.ExecuteSqlCommand(sql, paras); } } }
→DbSessionFactory,實現IDbSessionFactory介面,生產執行緒內唯一資料層訪問入口例項
using System.Runtime.Remoting.Messaging; using MySportsStore.IDAL; namespace MySportsStore.DAL { public class DbSessionFactory: IDbSessionFactory { public IDbSession GetCurrentDbSession() { IDbSession dbSession = CallContext.GetData(typeof (DbSession).FullName) as IDbSession; if (dbSession == null) { dbSession = new DbSession(); CallContext.SetData(typeof(DbSession).FullName, dbSession); } return dbSession; } } }
4.3 建立BLL層
4.3.1 MySportsStore.IBLL詳解
→新增引用
● 新增對MySportsStore.Model的引用
● 新增對MySportsStore.IDAL的引用
→IBaseService,是所有IXXXService介面的泛型基介面實現,避免了所有IXXXService介面的重複部分
using System; using System.Linq; using System.Linq.Expressions; using MySportsStore.IDAL; namespace MySportsStore.IBLL { public interface IBaseService<T> where T : class, new() { //資料層訪問統一入口工廠 IDbSessionFactory DbSessionFactory { get; set; } //資料層訪問統一入口 IDbSession DbSessionContext { get; set; } //查詢 IQueryable<T> LoadEntities(Expression<Func<T, bool>> whereLambda); //分頁查詢 IQueryable<T> LoadPageEntities<S>( Expression<Func<T, bool>> whereLambada, Expression<Func<T, S>> orderBy, int pageSize, int pageIndex, out int totalCount, bool isASC); //查詢總數量 int Count(Expression<Func<T, bool>> predicate); //新增 T AddEntity(T entity); //批量新增 int AddEntities(params T[] entities); //刪除 int DeleteEntity(T entity); //批量刪除 int DeleteBy(Expression<Func<T, bool>> whereLambda); //更新 T UpdateEntity(T entity); //批量更新 int UpdateEntities(params T[] entities); } }
為什麼需要DbSessionContext屬性?
--通過該屬性可以拿到型別為IXXXRepository的XXXRepository。
為什麼需要DbSessionFactory屬性?
--通過該"抽象工廠"屬性可以生產DbSessionContext例項。
→IProductService,對基介面IBaseService<Product>的實現
using MySportsStore.Model; namespace MySportsStore.IBLL { public interface IProductService : IBaseService<Product> { } }
4.3.2 MySportsStore.BLL詳解
→新增引用
● 新增對MySportsStore.Model的引用
● 新增對MySportsStore.IDAL的引用
● 新增對MySportsStore.IBLL的引用
→BaseService
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using MySportsStore.DAL; using MySportsStore.IDAL; namespace MySportsStore.BLL { public abstract class BaseService<T> : IDisposable where T:class,new() { //資料層統一訪問入口工廠屬性 private IDbSessionFactory _DbSessionFactory; public IDbSessionFactory DbSessionFactory { get { if (_DbSessionFactory == null) { _DbSessionFactory = new DbSessionFactory(); } return _DbSessionFactory; } set { _DbSessionFactory = value; } } //資料層統一訪問入口屬性 private IDbSession _DbSessionContext; public IDbSession DbSessionContext { get { if (_DbSessionContext == null) { _DbSessionContext = DbSessionFactory.GetCurrentDbSession(); } return _DbSessionContext; } set { _DbSessionContext = value; } } //當前Repository,在子類中實現--通過一個抽象方法在建構函式中設定 protected IBaseRepository<T> CurrentRepository; //藉助此方法在子類中的重寫,為XXXService設定當前Repository public abstract bool SetCurrentRepository(); public BaseService() { this.DisposableObjects = new List<IDisposable>(); this.SetCurrentRepository(); } //查詢 public IQueryable<T> LoadEntities(Expression<Func<T, bool>> whereLambda) { return this.CurrentRepository.LoadEntities(whereLambda); } public IQueryable<T> LoadPageEntities<S>( Expression<Func<T, bool>> whereLambada, Expression<Func<T, S>> orderBy, int pageSize, int pageIndex, out int totalCount, bool isASC) { return this.CurrentRepository.LoadPageEntities<S>( whereLambada, orderBy, pageSize, pageIndex, out totalCount, isASC); } //查詢總數量 public int Count(Expression<Func<T, bool>> predicate) { return this.CurrentRepository.Count(predicate); } //新增 public T AddEntity(T entity) { this.CurrentRepository.AddEntity(entity); DbSessionContext.SaveChanges(); return entity; } //批量新增 public int AddEntities(params T[] entities) { return this.CurrentRepository.AddEntities(entities); } //刪除 public int DeleteEntity(T entity) { this.CurrentRepository.DeleteEntity(entity); return DbSessionContext.SaveChanges(); } //批量刪除 public int DeleteBy(Expression<Func<T, bool>> whereLambda) { this.CurrentRepository.DeleteBy(whereLambda); return DbSessionContext.SaveChanges(); } //更新 public T UpdateEntity(T entity) { this.CurrentRepository.UpdateEntity(entity); if (this.DbSessionContext.SaveChanges() <= 0) { return null; } return entity; } //批量更新 public int UpdateEntities(params T[] entities) { return this.CurrentRepository.UpdateEntities(entities); } public IList<IDisposable> DisposableObjects { get; private set; } protected void AddDisposableObject(object obj) { IDisposable disposable = obj as IDisposable; if (disposable != null) { this.DisposableObjects.Add(disposable); } } public void Dispose() { foreach (IDisposable obj in this.DisposableObjects) { if (obj != null) { obj.Dispose(); } } } } }
BaseService是所有XXXService的泛型基類實現。
關鍵點一:如何在BaseService的子類中確定當前儲存CurrentRepository?
1、抽象基類BaseServic有型別為 IBaseRepository<T>的屬性CurrentRepository
2、通過在抽象基類BaseServic的建構函式中實現抽象方法SetCurrentRepository(),來設定CurrentRepository
3、BaseServic的子類必須重寫SetCurrentRepository()以最終確定當前的CurrentRepository值
關鍵點二:如何把BaseService的子類中的CurrentRepository銷燬?
1、在BaseService建立一個型別為IList<IDisposable>的集合
2、在BaseService中提供一個AddDisposableObject(object obj)方法,允許子類把CurrentRepository放入其中
3、在BaseService的Dispose()方法中,遍歷所有的CurrentRepository進行銷燬
→ProductService,派生於BaseService<Product>,實現IProductService介面
using MySportsStore.IBLL; using MySportsStore.Model; namespace MySportsStore.BLL { public class ProductService : BaseService<Product>, IProductService { public ProductService():base(){} public override bool SetCurrentRepository() { this.CurrentRepository = DbSessionContext.ProductRepository; this.AddDisposableObject(this.CurrentRepository); return true; } } }
至此,完成了三層架構的程式碼實現。
原始碼在這裡。
“MVC專案實踐,在三層架構下實現SportsStore”系列包括: