ASP.NET MVC OA框架搭建

企業(yè)級應(yīng)用架構(gòu)設(shè)計

基本設(shè)計原則

一個設(shè)計精良的系統(tǒng)并不是一系列指令和修改的堆砌,其中包含很多與設(shè)計直接或間接相關(guān)的要素,與國際化標(biāo)準(zhǔn)中定義的其它質(zhì)量特性相比,需要更加重視代碼的可維護性,之所以選擇這個特性,并不是因為其他特性和可維護性相比不重要,而是保持代碼的可維護性的代碼比較高,而且容易讓開發(fā)者忽視,可維護是最關(guān)鍵的問題。

對于可維護性可分為兩點解析,結(jié)構(gòu)化設(shè)計是第一要素,可通過一系列的編碼技術(shù)來保證。代碼的可讀性是另外一個重要因素。

結(jié)構(gòu)化設(shè)計

結(jié)構(gòu)化設(shè)計從理念誕生開始,內(nèi)部隱藏的結(jié)構(gòu)化設(shè)計核心原則一直是今天的指導(dǎo)原則,高內(nèi)聚和低耦合這兩個原則在面向?qū)ο笫澜缰酗L(fēng)光無限。內(nèi)聚的衡量標(biāo)準(zhǔn)從低到高,內(nèi)聚越高說明軟件設(shè)計的越好。高內(nèi)聚的模塊意味著可維護性和可重用性,因為這些模塊的外部依賴很少。低內(nèi)聚的模塊容易在其他模塊中留下依賴,讓軟件變得頑固且高粘度。耦合的衡量標(biāo)準(zhǔn)從低到高,耦合越低說明軟件設(shè)計越好。高內(nèi)聚和低耦合唇齒相依,若系統(tǒng)滿足這兩個條件,則說明該系統(tǒng)基本滿足高可讀性、高可維護性,并易于測試和易于重用的要求。

分離關(guān)注點

在設(shè)計系統(tǒng)時需要考慮內(nèi)聚和耦合兩個因素時,分離關(guān)注點有助于實現(xiàn)這個目標(biāo)。分離關(guān)注點的核心在于將系統(tǒng)拆分成各不相同且最好沒有重疊的功能。分離關(guān)注點原則建議一次只處理一個關(guān)注點。這并不表示此時要將所有的其他關(guān)注點都拋之腦后,而是說當(dāng)決定用某個模塊來實現(xiàn)這個關(guān)注點之后,只需要全神貫注于實現(xiàn)該模塊即可。從這個角度考慮,其他關(guān)注點都是不相關(guān)的。具體來說,分離關(guān)注點是通過模塊化代碼以及大量運用信息隱藏來實現(xiàn)的。模塊化編碼鼓勵使用不同的模塊來實現(xiàn)不同的功能,模塊擁有自己的公開接口,和其他模塊通信,模塊同時包含大量內(nèi)部信息,供自己使用。信息隱藏是一條通用的設(shè)計選擇,固定的公開接口隱藏軟件模塊的實現(xiàn)細節(jié),以降低未來修改造成的影響。

其實分離關(guān)注點第一種支持的編程理念是過程式編程,在過程式編程中,分離關(guān)注點依靠函數(shù)和過程來實現(xiàn)。不僅分離關(guān)注點的概念不僅限于編程語言,它超越了純粹編程的領(lǐng)域。還引用了軟件架構(gòu)的很多方面。在面向服務(wù)架構(gòu)SOA中,服務(wù)用來表示關(guān)注點。分層架構(gòu)也基于分離關(guān)注點的原則構(gòu)建。

面向?qū)ο笤O(shè)計

面向?qū)ο笤O(shè)計可以說是一座里程碑,面向?qū)ο笤O(shè)計中的一個重要的步驟就是為問題領(lǐng)域?qū)ふ乙粋€趕緊靈活的抽象。若要很好的完整這一步,應(yīng)該考慮的是事情而非流程。應(yīng)該關(guān)注的是“什么”而非“如何”。

可重用性是面向?qū)ο罄砟钪幸粋€非常重要的方面,也是面向?qū)ο髲V泛使用的根本原因。《設(shè)計模式》總結(jié)出兩種實現(xiàn)重用的方式:白盒重用和黑盒重用。白盒重用基于繼承,黑盒重用基于對象組合。在實際設(shè)計中,盡量使用對象組合而非類型繼承。在現(xiàn)實世界中,對象組合更加安全,易于維護和測試。在組合中,修改組合對象并不會影響到內(nèi)部對象。

高級原則

開放封閉原則

開放封閉原則能夠幫助軟件的單元(包括類型、函數(shù)、模塊)更加容易適應(yīng)變化。每次發(fā)生變化時,要通過添加新代碼來增強現(xiàn)有類型的行為,而非修改原有代碼。當(dāng)前這個原則最好的方式是提供一個固定的接口,然后讓可能發(fā)生變化的類實現(xiàn)該接口,隨后調(diào)用者基于該接口操作。

里氏替換原則

但某個類型派生于某個現(xiàn)有類型時,派生類應(yīng)該能夠用于任何可以使用父類的地方即多態(tài)。開放封閉原則和里氏替換原則有著緊密的關(guān)系,任何使用違反里氏替換原則的類型的方法都無法滿足開放封閉原則。

依賴倒置原則

依賴倒置原則中的倒置表示在實現(xiàn)過程中應(yīng)采用自頂向下的方式,且應(yīng)該關(guān)注于高層次模塊的工作流,而非低層次的模塊具體的實現(xiàn)。從這點考慮,低層次模塊可以直接插入到高層次模塊中。

五層架構(gòu)

知識點

ASP.NET MVC請求是如何進入管道的

請求處理管道

請求管道是用于處理HTTP請求的模塊組合,在ASP.NET中請求管道有兩大核心組件IHttpModuleIHttpHandler。所有HTTP請求會進入IHttpHandler,有IHttpHandler進行最終的處理,而IHttpModule通過訂閱HttpApplication對象中的事件,可在IHttpHandler對HTTP請求進行處理前對請求進行預(yù)處理或IHttpHandler對HTTP請求之后進行再次處理。

IIS7之前請求處理管道分為兩個:IIS請求處理管道和ASP.NET管道,若客戶端請求靜態(tài)資源則只有IIS管道進行處理,而ASP.NET管道不會處理該請求。從IIS7開始兩個管道合二為一,稱為集成管道。

ASP.NET MVC處理管道

ASP.NET MVC 處理管道

ASP.NET MVC的請求管理和ASP.MET請求管道基本類似,

ASP.NET MVC請求處理流程

  1. 瀏覽器發(fā)送HTTP請求
  2. Web服務(wù)器IIS
  3. ISAPIRuntime
  4. HttpWorkRequest
  5. HttpRuntime
  6. HttpContext
  7. 尋找Global文件并編譯
  8. 確保Global文件中Application_Start被調(diào)用
  9. 創(chuàng)建HttpApplication,使用了池(棧)。如果池中沒有根據(jù)Global文件編譯的類型,則通過反射的形式創(chuàng)建出HttpApplication。
  10. 獲取所有在Web.config配置文件中的HttpModules,此時System.Web.Routing下UrlRoutingModule也被獲取,執(zhí)行每個Module下的Init方法。UrlRoutingModule的Init方法完成了請求管道第七個事件的注冊。
  11. 進入管道
  12. 第七個事件觸發(fā)執(zhí)行相應(yīng)方法,完成MVCHandler的創(chuàng)建。
  13. 走到請求管道中的11與12事件之間,執(zhí)行MVCHandler中的ProcessRequest方法,該方法尋找控制器和方法,執(zhí)行方法中的代碼,最終尋找視圖并渲染。


    ASP.NET MVC請求處理流程

搭建框架

新建空白解決方案并命名為OA,在空白項目中添加類庫與ASP.NET MVC的Web應(yīng)用程序。

  1. 添加類庫 OA.Common 公共幫助類庫
  2. 添加類庫 OA.Model EF實體數(shù)據(jù)模型
  3. 添加類庫 OA.DAL
  4. 添加類庫 OA.IDAL 數(shù)據(jù)訪問層
  5. 添加類庫 OA.DALFactory 抽象工廠類
  6. 添加類庫 OA.BLL
  7. 添加類庫 OA.IBLL 業(yè)務(wù)邏輯層
  8. 添加Web應(yīng)用程序 OA.WebApp,并將其設(shè)置為解決方案的默認啟動項。
OA體系結(jié)構(gòu)
新建類庫
添加ASP.NET MVC

模型層

模型層

創(chuàng)建數(shù)據(jù)模型

  1. OA.Model中添加ADO.NET實體數(shù)據(jù)模型并命名為OAModel,并采用Code First編碼優(yōu)先的方式創(chuàng)建數(shù)據(jù)模型。
image.png
  1. 查看OA.Model中的App.config應(yīng)用配置文件,添加本地數(shù)據(jù)庫連接字符串。
  <connectionStrings>
    <add 
    name="OAModel" 
    connectionString="data source=(LocalDb)\MSSQLLocalDB;initial catalog=OA.Model.OAModel;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" 
    providerName="System.Data.SqlClient" />
  </connectionStrings>
  1. 在模型中添加實體
$ vim OA.Model/OAModel.cs
namespace OA.Model
{
    using System;
    using System.Data.Entity;
    public class OAModel : DbContext
    {
        //上下文已配置為從應(yīng)用程序的配置文件(App.config 或 Web.config)使用“OAModel”連接字符串
        public OAModel(): base("name=OAModel")
        {
        }

        //為要在模型中包含的每種實體類型都添加 DbSet。
        public virtual DbSet<UserInfo> UserInfo { get; set; }
    }
    public class UserInfo
    {
        public int ID { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public DateTime CreatedAt { get; set; }
        public DateTime UpdatedAt { get; set; }
        public bool Status { get; set; }
        public int Sort { get; set; }
        public String Remark { get; set; }
    }
}

數(shù)據(jù)訪問層

數(shù)據(jù)訪問層的設(shè)計很大程度上取決于項目干系人需求的影響。例如,數(shù)據(jù)訪問層應(yīng)該持久化對象模型還是簡單的值的集合呢?數(shù)據(jù)訪問層應(yīng)該支持一種數(shù)據(jù)庫還是多種數(shù)據(jù)庫呢?

數(shù)據(jù)庫獨立性

數(shù)據(jù)訪問層是系統(tǒng)中唯一知道并使用連接字符串和數(shù)據(jù)表名的地方,考慮到這些,數(shù)據(jù)訪問層必須要依賴于數(shù)據(jù)庫管理系統(tǒng)DBMS。對于外部觀察者,數(shù)據(jù)訪問層應(yīng)該是一個黑盒,可以插入到現(xiàn)有系統(tǒng)中,封裝了為某個特定DBMS實現(xiàn)的讀取和寫入的操作。

像插入一樣可以配置

通常來說,數(shù)據(jù)庫獨立性需要一套普通的、跨庫的應(yīng)用編程接口。實現(xiàn)真正數(shù)據(jù)庫獨立需要將數(shù)據(jù)庫訪問層作為一個黑盒,該黑盒提供了一個固定的接口,并從配置文件中動態(tài)地讀取當(dāng)前數(shù)據(jù)庫訪問層組件的細節(jié)。還有一種實現(xiàn)數(shù)據(jù)庫獨立性的做法是使用對象/關(guān)系映射工具ORM。ORM提供一套公共的API,讓你僅需簡單修改配置參數(shù)就可以切換到另一個數(shù)據(jù)庫。不過,有時候項目允許你使用ORM,有時卻不行。

持久化應(yīng)用程序的對象模型

無論何種形式,數(shù)據(jù)訪問層都必須能夠持久化應(yīng)用程序的數(shù)據(jù)。若必須提供對象模型,那么數(shù)據(jù)訪問層要能夠?qū)⒛P统志没陵P(guān)系型結(jié)構(gòu)中。當(dāng)然,會造成臭名昭著的“對象關(guān)系阻抗失調(diào)”問題。關(guān)系型數(shù)據(jù)庫實際上存放的數(shù)據(jù)元組,而對象模型則構(gòu)造出一張對象圖。因此,兩個模型之間自然需要映射,這也是數(shù)據(jù)訪問層的主要功能。持久化應(yīng)用程序的對象模型是指將數(shù)據(jù)加載到新創(chuàng)建對象模型和將某個實例的內(nèi)容寫回數(shù)據(jù)庫的能力。無論選擇什么樣的DBMS或物理上的表結(jié)構(gòu),持久化功能都不應(yīng)該受到影響。依照領(lǐng)域模型模式設(shè)計的對象模型并不了解數(shù)據(jù)訪問層的存在,不過若對象模型屬于活動記錄,那么數(shù)據(jù)訪問層內(nèi)嵌在實現(xiàn)模型的所有框架中。

數(shù)據(jù)訪問層的職責(zé)

數(shù)據(jù)訪問層對使用者來說有4個職責(zé):
首先,數(shù)據(jù)訪問層需要將數(shù)據(jù)持久化至物理存儲中,并為外部世界提供CRUD服務(wù)。
其次,數(shù)據(jù)訪問層還要處理其接收的所有數(shù)據(jù)相關(guān)的請求。
再次,數(shù)據(jù)訪問層必須滿足事務(wù)性需求。
最后,數(shù)據(jù)訪問層也要合理的處理并發(fā)。
從概念角度,數(shù)據(jù)訪問層可以看作是封裝了4種服務(wù)的黑箱。

數(shù)據(jù)訪問層

創(chuàng)建數(shù)據(jù)訪問層接口

  1. 為數(shù)據(jù)訪問層接口添加對模型層的引用

    為數(shù)據(jù)訪問層接口添加對模型層的引用

  2. 創(chuàng)建基礎(chǔ)數(shù)據(jù)訪問層接口,定義公共的數(shù)據(jù)操作方法接口,其它數(shù)據(jù)訪問接口均繼承于它。

定義基礎(chǔ)的數(shù)據(jù)操作方法,如CURD與分頁。

$ vim OA.IDAL/IBaseDal.cs
using System;
using System.Linq;
using System.Linq.Expressions;

namespace OA.IDAL
{
    public interface IBaseDal<T> where T : class, new()
    {
        /*查詢*/
        IQueryable<T> Get(Expression<Func<T, bool>> whereLambda);
        /**
         * 分頁
         * s為方法泛型,表示排序字段的數(shù)據(jù)類型。
         */
        IQueryable<T> Page<s>(int pageIndex, int pageSize, out int totalCount, Expression<Func<T, bool>> whereLambda, Expression<Func<T, s>> orderByLambda, bool isAsc);
        /*刪除*/
        bool Delete(T entity);
        /*更新*/
        bool Update(T entity);
        /*創(chuàng)建*/
        T Create(T entity);
    }
}
  1. 創(chuàng)建具體數(shù)據(jù)訪問層接口
$ vim OA.IDAL/IUserInfo.cs
using OA.Model;

namespace OA.IDAL
{
    /*數(shù)據(jù)訪問接口*/
    public interface IUserInfoDal:IBaseDal<UserInfo>
    {
    }
}

創(chuàng)建數(shù)據(jù)持久層類并實現(xiàn)其接口

  1. 添加數(shù)據(jù)訪問層DAL對模型層Model和數(shù)據(jù)訪問層接口IDAL的引用


    添加數(shù)據(jù)訪問層DAL對模型層Model和數(shù)據(jù)訪問層接口IDAL的引用
  1. 添加對EF的引用

查看模型層中對EF的引用分別為EntityFrameworkEntityFramework.SqlServer

EF引用

查看模型層中EF引用的版本,注意整個解決方案中所有引入EF的位置必須保證版本一致。

$ vim OA.Model/App.config
<configSections>
  <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
添加對EF的引用

在數(shù)據(jù)訪問層添加臨時的模型層,系統(tǒng)會自動加載所需的EF程序集的引用,添加后刪除該模型文件。

添加EF引用
  1. 創(chuàng)建數(shù)據(jù)操作類并實現(xiàn)公共的基礎(chǔ)接口方法
$ vim OA.DAL/UserInfoDal.cs
using OA.IDAL;
using OA.Model;
using System;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace OA.DAL
{
    public class UserInfoDal : IUserInfoDal
    {
        OAModel db = new OAModel();
        public UserInfo Create(UserInfo entity)
        {
            db.UserInfo.Add(entity);
            return entity;
        }

        public bool Delete(UserInfo entity)
        {
            db.Entry<UserInfo>(entity).State = EntityState.Deleted;
            return db.SaveChanges() > 0;
        }

        public IQueryable<UserInfo> Get(Expression<Func<UserInfo, bool>> whereLambda)
        {
            return db.UserInfo.Where<UserInfo>(whereLambda);
        }

        public IQueryable<UserInfo> Page<s>(int pageIndex, int pageSize, out int totalCount, Expression<Func<UserInfo, bool>> whereLambda, Expression<Func<UserInfo, s>> orderByLambda, bool isAsc)
        {

            var temp = db.UserInfo.Where<UserInfo>(whereLambda);
            totalCount = temp.Count();
            int skip = (pageIndex - 1) * pageSize;
            if (isAsc)
            {
                temp = temp.OrderBy<UserInfo, s>(orderByLambda).Skip<UserInfo>(skip).Take<UserInfo>(pageSize);
            }
            else
            {
                temp = temp.OrderByDescending<UserInfo, s>(orderByLambda).Skip<UserInfo>(skip).Take<UserInfo>(pageSize);
            }
            return temp;
        }

        public bool Update(UserInfo entity)
        {
            db.Entry<UserInfo>(entity).State = EntityState.Modified;
            return db.SaveChanges() > 0;
        }
    }
}
  1. 由于每個數(shù)據(jù)操作類都需要實現(xiàn)基礎(chǔ)操作,因此使用繼承的方式添加公共的數(shù)據(jù)操作父類。

基類:無需實現(xiàn)IBaseDal

$ vim OA.DAL/BaseDal.cs
using OA.Model;
using System;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace OA.DAL
{
    public class BaseDal<T> where T: class, new()
    {
        OAModel db = new OAModel();
        public T Create(T entity)
        {
            db.Set<T>().Add(entity);
            db.SaveChanges();
            return entity;
        }

        public bool Delete(T entity)
        {
            db.Entry<T>(entity).State = EntityState.Deleted;
            return db.SaveChanges() > 0;
        }

        public IQueryable<T> Get(Expression<Func<T, bool>> whereLambda)
        {
            return db.Set<T>().Where<T>(whereLambda);
        }

        public IQueryable<T> Page<s>(int pageIndex, int pageSize, out int totalCount, Expression<Func<T, bool>> whereLambda, Expression<Func<T, s>> orderByLambda, bool isAsc)
        {

            var temp = db.Set<T>().Where<T>(whereLambda);
            totalCount = temp.Count();
            int skip = (pageIndex - 1) * pageSize;
            if (isAsc)
            {
                temp = temp.OrderBy<T, s>(orderByLambda).Skip<T>(skip).Take<T>(pageSize);
            }
            else
            {
                temp = temp.OrderByDescending<T, s>(orderByLambda).Skip<T>(skip).Take<T>(pageSize);
            }
            return temp;
        }

        public bool Update(T entity)
        {
            db.Entry<T>(entity).State = EntityState.Modified;
            return db.SaveChanges() > 0;
        }
    }
}

子類:注意需要先繼承后實現(xiàn)

$ OA.DAL/UserInfoDal.cs
using OA.IDAL;
using OA.Model;

namespace OA.DAL
{
    public class UserInfoDal : BaseDal<UserInfo>, IUserInfoDal
    {

    }
}

數(shù)據(jù)會話層

  • 數(shù)據(jù)會話層位于數(shù)據(jù)操作層與業(yè)務(wù)處理層之間
  • 數(shù)據(jù)會話層封裝了所有數(shù)據(jù)操作類實例的創(chuàng)建
  • 業(yè)務(wù)處理層通過數(shù)據(jù)會話層來獲取要操作數(shù)據(jù)持久層中操作類的實例
  • 數(shù)據(jù)會話層本質(zhì)就是一個工廠類,負責(zé)對象的創(chuàng)建。
  • 將業(yè)務(wù)處理層與數(shù)據(jù)持久層進行解耦,提供一個數(shù)據(jù)訪問的統(tǒng)一訪問點。
數(shù)據(jù)會話層
  1. 添加引用
  • 添加對數(shù)據(jù)模型OA.Model的引用
  • 添加對數(shù)據(jù)操作層接口OA.IDAL的引用
  • 添加對數(shù)據(jù)操作層OA.DAL的引用
添加引用
  1. 創(chuàng)建數(shù)據(jù)會話層類
  • 數(shù)據(jù)會話層封裝了所有數(shù)據(jù)操作類的實例的創(chuàng)建,本質(zhì)是一個工廠類。
  • 將業(yè)務(wù)層與數(shù)據(jù)層解耦
  • 提供數(shù)據(jù)訪問的統(tǒng)一訪問點
$ vim OA.DALFactory/DBSession.cs
using OA.DAL;
using OA.IDAL;
using OA.Model;

namespace OA.DALFactory
{
    /**
     * 數(shù)據(jù)會話層
     * 1.本質(zhì)是一個工廠類
     * 2.負責(zé)完成所有數(shù)據(jù)操作類實例的創(chuàng)建
     * 3.業(yè)務(wù)處理層通過數(shù)據(jù)會話層來獲取操作數(shù)據(jù)類的實例
     * 4.數(shù)據(jù)會話層將業(yè)務(wù)層和數(shù)據(jù)層解耦
     */
    public class DBSession
    {
        /*負責(zé)完成所有數(shù)據(jù)操作類實例的創(chuàng)建*/
        private IUserInfoDal _UserInfoDal;
        public IUserInfoDal UserInfoDal
        {
            get
            {
                if(_UserInfoDal == null)
                {
                    _UserInfoDal = new UserInfoDal();
                }
                return _UserInfoDal;
            }                                    ,
            set
            {
                _UserInfoDal = value;
            }
        }
}
  1. 在數(shù)據(jù)會話層中添加對所有數(shù)據(jù)保存的方法

數(shù)據(jù)會話層OA.DALFactory中添加對EF的引用

數(shù)據(jù)會話層OA.DALFactory中添加對EF的引用
$ vim OA.DALFactory/DbSession.cs
using OA.DAL;
using OA.IDAL;
using OA.Model;

namespace OA.DALFactory
{
    /**
     * 數(shù)據(jù)會話層
     * 1.本質(zhì)是一個工廠類
     * 2.負責(zé)完成所有數(shù)據(jù)操作類實例的創(chuàng)建
     * 3.業(yè)務(wù)處理層通過數(shù)據(jù)會話層來獲取操作數(shù)據(jù)類的實例
     * 4.數(shù)據(jù)會話層將業(yè)務(wù)層和數(shù)據(jù)層解耦
     * 5.完成所有數(shù)據(jù)的保存
     */
    public class DbSession
    {
        /*負責(zé)完成所有數(shù)據(jù)操作類實例的創(chuàng)建*/
        private IUserInfoDal _UserInfoDal;
        public IUserInfoDal UserInfoDal
        {
            get
            {
                if(_UserInfoDal == null)
                {
                    _UserInfoDal = new UserInfoDal();
                }
                return _UserInfoDal;
            } 
            set
            {
                _UserInfoDal = value;
            }
        }
        /**
         * 工作單元設(shè)計模式 - 完成所有數(shù)據(jù)的保存
         * 一個業(yè)務(wù)中涉及到對多張表的操作
         * 連接一次數(shù)據(jù)庫完成對多張表的數(shù)據(jù)操作
         */
        OAModel db = new OAModel();
        public bool SaveChanges()
        {
            return db.SaveChanges() > 0;
        }
    }
}
  1. 將數(shù)據(jù)持久層中公共的基礎(chǔ)數(shù)據(jù)持久類中所有的SaveChanges()取消
$ vim OA.DAL/BaseDal.cs
using OA.Model;
using System;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace OA.DAL
{
    public class BaseDal<T> where T: class, new()
    {
        OAModel db = new OAModel();
        public T Create(T entity)
        {
            db.Set<T>().Add(entity);
            //db.SaveChanges();
            return entity;
        }

        public bool Delete(T entity)
        {
            db.Entry<T>(entity).State = EntityState.Deleted;
            //return db.SaveChanges() > 0;
            return true;
        }

        public IQueryable<T> Get(Expression<Func<T, bool>> whereLambda)
        {
            return db.Set<T>().Where<T>(whereLambda);
        }

        public IQueryable<T> Page<s>(int pageIndex, int pageSize, out int totalCount, Expression<Func<T, bool>> whereLambda, Expression<Func<T, s>> orderByLambda, bool isAsc)
        {

            var temp = db.Set<T>().Where<T>(whereLambda);
            totalCount = temp.Count();
            int skip = (pageIndex - 1) * pageSize;
            if (isAsc)
            {
                temp = temp.OrderBy<T, s>(orderByLambda).Skip<T>(skip).Take<T>(pageSize);
            }
            else
            {
                temp = temp.OrderByDescending<T, s>(orderByLambda).Skip<T>(skip).Take<T>(pageSize);
            }
            return temp;
        }

        public bool Update(T entity)
        {
            db.Entry<T>(entity).State = EntityState.Modified;
            //return db.SaveChanges() > 0;
            return true;
        }
    }
}
  1. EF線程內(nèi)唯一

目前的問題是,EF在數(shù)據(jù)操作層中會使用,EF在數(shù)據(jù)會話層中也會使用到,這是兩個不同的對象。而在一個請求中只能創(chuàng)建一個EF實例,也就是線程內(nèi)唯一。對此,因采用工廠模式。

如果在DALFactory中創(chuàng)建一個工廠類封裝EF實例的創(chuàng)建,會出現(xiàn)一個問題。由于DALFactory已經(jīng)引入了DAL,EF若在DALFactory中創(chuàng)建,那么DAL也必須引用DALFactory,此時也就成了相互循環(huán)引用。所以,應(yīng)該將負責(zé)EF實例創(chuàng)建的工廠類DbContextFactory放到DAL中。

$ OA.DAL/DbContextFactory.cs
using OA.Model;
using System.Data.Entity;
using System.Runtime.Remoting.Messaging;

namespace OA.DAL
{
    /**
     * 負責(zé)創(chuàng)建EF數(shù)據(jù)操作上下文實例
     * 使用工廠模式且必須保證線程內(nèi)唯一
     */
    public class DbContextFactory
    {
        public static DbContext CreateDbContext()
        {
            DbContext dbContext = (DbContext)CallContext.GetData("dbContext");
            if (dbContext == null)
            {
                dbContext = new OAModel();
                CallContext.SetData("dbContext", dbContext);
            }
            return dbContext;
        }
    }
}

在數(shù)據(jù)會話層DbSession和數(shù)據(jù)處理層中公共基類BaseDal中調(diào)用CreateDbContext方法,完成EF實例的創(chuàng)建。

OAModel db = new OAModel();

將原來db的實例化的方式修改為通過DbContextFactory創(chuàng)建

public DbContext db
{
    get
    {
        return DbContextFactory.CreateDbContext();
    }
} 

簡化方式

DbContext db = DbContextFactory.CreateDbContext();

在DbSession中獲取db實例

$ vim OA.DALFactory/DbSession.cs
using OA.DAL;
using OA.IDAL;
using System.Data.Entity;

namespace OA.DALFactory
{
    /**
     * 數(shù)據(jù)會話層
     * 1.本質(zhì)是一個工廠類
     * 2.負責(zé)完成所有數(shù)據(jù)操作類實例的創(chuàng)建
     * 3.業(yè)務(wù)處理層通過數(shù)據(jù)會話層來獲取操作數(shù)據(jù)類的實例
     * 4.數(shù)據(jù)會話層將業(yè)務(wù)層和數(shù)據(jù)層解耦
     * 5.完成所有數(shù)據(jù)的保存
     */
    public class DbSession
    {
        /*負責(zé)完成所有數(shù)據(jù)操作類實例的創(chuàng)建*/
        private IUserInfoDal _UserInfoDal;
        public IUserInfoDal UserInfoDal
        {
            get
            {
                if(_UserInfoDal == null)
                {
                    _UserInfoDal = new UserInfoDal();
                }
                return _UserInfoDal;
            }                                    
            set
            {
                _UserInfoDal = value;
            }
        }
        /**
         * 工作單元 設(shè)計模式
         * 完成所有數(shù)據(jù)的保存
         * 一個業(yè)務(wù)中涉及到對多張表的操作
         * 連接一次數(shù)據(jù)庫完成對多張表的數(shù)據(jù)操作
         */
        //OAModel db = new OAModel();
        DbContext db = DbContextFactory.CreateDbContext();
        public bool SaveChanges()
        {
            return db.SaveChanges() > 0;
        }
    }
}

在BaseDal中獲取db實例

$ vim OA.DAL/BaseDal.cs
using OA.Model;
using System;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace OA.DAL
{
    public class BaseDal<T> where T: class, new()
    {
        //OAModel db = new OAModel();
        //public DbContext db
        //{
        //    get
        //    {
        //        return DbContextFactory.CreateDbContext();
        //    }
        //} 
        DbContext db = DbContextFactory.CreateDbContext();

        public T Create(T entity)
        {
            db.Set<T>().Add(entity);
            //db.SaveChanges();
            return entity;
        }

        public bool Delete(T entity)
        {
            db.Entry<T>(entity).State = EntityState.Deleted;
            //return db.SaveChanges() > 0;
            return true;
        }

        public IQueryable<T> Get(Expression<Func<T, bool>> whereLambda)
        {
            return db.Set<T>().Where<T>(whereLambda);
        }

        public IQueryable<T> Page<s>(int pageIndex, int pageSize, out int totalCount, Expression<Func<T, bool>> whereLambda, Expression<Func<T, s>> orderByLambda, bool isAsc)
        {

            var temp = db.Set<T>().Where<T>(whereLambda);
            totalCount = temp.Count();
            int skip = (pageIndex - 1) * pageSize;
            if (isAsc)
            {
                temp = temp.OrderBy<T, s>(orderByLambda).Skip<T>(skip).Take<T>(pageSize);
            }
            else
            {
                temp = temp.OrderByDescending<T, s>(orderByLambda).Skip<T>(skip).Take<T>(pageSize);
            }
            return temp;
        }

        public bool Update(T entity)
        {
            db.Entry<T>(entity).State = EntityState.Modified;
            //return db.SaveChanges() > 0;
            return true;
        }
    }
}
  1. 將DbSession工廠類中實例化數(shù)據(jù)操作類的方式修改使用抽象工廠來完成

在表現(xiàn)層WebApp的配置文件Web.config中設(shè)置程序集與命名空間。

$ vim OA.WebApp/Web.conifg
<appSettings>
  <add key="webpages:Version" value="3.0.0.0"/>
  <add key="webpages:Enabled" value="false"/>
  <add key="ClientValidationEnabled" value="true"/>
  <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
  <!--配置程序集與命名空間-->
  <add key="AssemblyPath" value="OA.DAL"/>
  <add key="NameSpace" value="OA.DAL"/>
</appSettings>

在數(shù)據(jù)會話層中引入框架中的System.Configuration程序集

引入Configuration程序集

為了進一步將數(shù)據(jù)會話層與話劇操作層解耦,在數(shù)據(jù)會話層DALFactory中創(chuàng)建抽象工廠,以完成數(shù)據(jù)操作類的實例化。

$ vim OA.DALFactory/AbstractFactory.cs

using OA.IDAL;
using System.Configuration;
using System.Reflection;

namespace OA.DALFactory
{
    /**
     * 抽象工廠
     * 通過反射的方式創(chuàng)建類的實例
     */
    public class AbstractFactory
    {
        private static readonly string AssemblyPath = ConfigurationManager.AppSettings["AssemblyPath"];
        private static readonly string NameSpace = ConfigurationManager.AppSettings["NameSpace"];

        private static object CreateInstance(string className)
        {
            var assembly = Assembly.Load(AssemblyPath);
            return assembly.CreateInstance(className);
        }
        public static IUserInfoDal CreateUserInfoDal()
        {
            string fullClassName = NameSpace + ".UserInfoDal";
            return CreateInstance(fullClassName) as IUserInfoDal;
        }
    }
}

數(shù)據(jù)會話層調(diào)用抽象工廠完成實例化

$ vim OA.DALFactory/DbSession.cs
using OA.DAL;
using OA.IDAL;
using System.Data.Entity;

namespace OA.DALFactory
{
    /**
     * 數(shù)據(jù)會話層
     * 1.本質(zhì)是一個工廠類
     * 2.負責(zé)完成所有數(shù)據(jù)操作類實例的創(chuàng)建
     * 3.業(yè)務(wù)處理層通過數(shù)據(jù)會話層來獲取操作數(shù)據(jù)類的實例
     * 4.數(shù)據(jù)會話層將業(yè)務(wù)層和數(shù)據(jù)層解耦
     * 5.完成所有數(shù)據(jù)的保存
     */
    public class DbSession
    {
        /*負責(zé)完成所有數(shù)據(jù)操作類實例的創(chuàng)建*/
        private IUserInfoDal _UserInfoDal;
        public IUserInfoDal UserInfoDal
        {
            get
            {
                if(_UserInfoDal == null)
                {
                    //_UserInfoDal = new UserInfoDal();
                    //使用抽象工廠來封裝了類的實例的創(chuàng)建(解耦)
                    _UserInfoDal = AbstractFactory.CreateUserInfoDal();
                }
                return _UserInfoDal;
            }                                    
            set
            {
                _UserInfoDal = value;
            }
        }
        /**
         * 工作單元 設(shè)計模式
         * 完成所有數(shù)據(jù)的保存
         * 一個業(yè)務(wù)中涉及到對多張表的操作
         * 連接一次數(shù)據(jù)庫完成對多張表的數(shù)據(jù)操作
         */
        //OAModel db = new OAModel();
        DbContext db = DbContextFactory.CreateDbContext();
        public bool SaveChanges()
        {
            return db.SaveChanges() > 0;
        }
    }
}

核心在于

//_UserInfoDal = new UserInfoDal();
//使用抽象工廠來封裝了類的實例的創(chuàng)建(解耦)
_UserInfoDal = AbstractFactory.CreateUserInfoDal();

數(shù)據(jù)會話層DbSession調(diào)用數(shù)據(jù)操作層DAL使用的是數(shù)據(jù)操作層所提供的接口IUserInfoDal,業(yè)務(wù)層BLL調(diào)用數(shù)據(jù)會話層DbSession同樣采用接口的方式,因此數(shù)據(jù)會話層DbSession必須提供對業(yè)務(wù)層的接口。

為便于引用及使用,在OA.IDAL定義IDbSession接口。

$vim OA.IDAL/IDbSession.cs
using System.Data.Entity;

namespace OA.IDAL
{
    /**
     * 業(yè)務(wù)層BLL調(diào)用的是數(shù)據(jù)會話層的接口
     */
    public interface IDbSession
    {
        DbContext db{get;}
        bool SaveChanges();
        IUserInfoDal UserInfoDal { get; set; }
    }
}

在DbSession中實現(xiàn)IDbSession接口

$ vim OA.DALFactory/DbSession.cs
using OA.DAL;
using OA.IDAL;
using System.Data.Entity;

namespace OA.DALFactory
{
    /**
     * 數(shù)據(jù)會話層
     * 1.本質(zhì)是一個工廠類
     * 2.負責(zé)完成所有數(shù)據(jù)操作類實例的創(chuàng)建
     * 3.業(yè)務(wù)處理層通過數(shù)據(jù)會話層來獲取操作數(shù)據(jù)類的實例
     * 4.數(shù)據(jù)會話層將業(yè)務(wù)層和數(shù)據(jù)層解耦
     * 5.完成所有數(shù)據(jù)的保存
     */
    public class DbSession:IDbSession
    {
        /*負責(zé)完成所有數(shù)據(jù)操作類實例的創(chuàng)建*/
        private IUserInfoDal _UserInfoDal;
        public IUserInfoDal UserInfoDal
        {
            get
            {
                if(_UserInfoDal == null)
                {
                    //_UserInfoDal = new UserInfoDal();
                    //使用抽象工廠來封裝了類的實例的創(chuàng)建(解耦)
                    _UserInfoDal = AbstractFactory.CreateUserInfoDal();
                }
                return _UserInfoDal;
            }                                    
            set
            {
                _UserInfoDal = value;
            }
        }
        /**
         * 工作單元 設(shè)計模式
         * 完成所有數(shù)據(jù)的保存
         * 一個業(yè)務(wù)中涉及到對多張表的操作
         * 連接一次數(shù)據(jù)庫完成對多張表的數(shù)據(jù)操作
         */
        //OAModel db = new OAModel();
        //DbContext db = DbContextFactory.CreateDbContext();
        public DbContext db
        {
            get
            {
                return DbContextFactory.CreateDbContext();
            }
        }
        public bool SaveChanges()
        {
            return db.SaveChanges() > 0;
        }
    }
}

業(yè)務(wù)邏輯層

任何復(fù)雜的軟件都可以通過分層來組織,每層表示系統(tǒng)中的一個邏輯部分,一般來說,業(yè)務(wù)邏輯層中的模塊包含了系統(tǒng)所需的所有功能上的算法和計算過程,并于數(shù)據(jù)層和表現(xiàn)層交互。抽象的說,業(yè)務(wù)邏輯層是軟件中專門處理業(yè)務(wù)相關(guān)任務(wù)性能的部分。

業(yè)務(wù)邏輯層表示了系統(tǒng)的邏輯,此處的代碼將要進行必要的決斷并執(zhí)行操作。在業(yè)務(wù)邏輯層的安全性意味著使用基于角色的安全原則,僅允許認證用戶訪問特定的業(yè)務(wù)對象。從外界看,業(yè)務(wù)邏輯層可看作是一個操作業(yè)務(wù)對象的機制。一般來說,業(yè)務(wù)對象不過是某個領(lǐng)域?qū)崿F(xiàn)的實現(xiàn),或是某類輔助類型,用來執(zhí)行一些計算。業(yè)務(wù)邏輯層處于分層系統(tǒng)的中間位置,業(yè)務(wù)邏輯層的輸入和輸出不一定是業(yè)務(wù)對象。很多時候,架構(gòu)師更加傾向于數(shù)據(jù)遷移對象在層之間交換數(shù)據(jù)。

數(shù)據(jù)遷移對象和業(yè)務(wù)對象之間的取舍一直是團隊中爭議的話題,建議使用數(shù)據(jù)遷移對象的理論認為,數(shù)據(jù)遷移能減少層之間的耦合,使系統(tǒng)更加整潔干凈。不過在現(xiàn)實中,人們都會說復(fù)雜性已經(jīng)很高,因此應(yīng)該避免增加任何不必要的對象。一條使用的原則是當(dāng)已經(jīng)有了數(shù)百個業(yè)務(wù)對象時,或許并不應(yīng)該僅僅為了設(shè)計的干凈而讓這個數(shù)字加倍。在這種情況下,數(shù)據(jù)遷移對象通常就是業(yè)務(wù)對象。業(yè)務(wù)對象同時包含了數(shù)據(jù)和行為,是一個可以參與到領(lǐng)域邏輯的完整對象。而數(shù)據(jù)遷移對象更像是一種值。即一系列數(shù)據(jù)的容器而沒有相關(guān)的行為。為了序列化,業(yè)務(wù)對象中的數(shù)據(jù)會復(fù)制到數(shù)據(jù)遷移對象中。除了get/set訪問器以外,數(shù)據(jù)遷移對象沒有邏輯行為。數(shù)據(jù)遷移對象并不僅僅是領(lǐng)域?qū)ο笕サ袅诵袨椋憩F(xiàn)了特定領(lǐng)域?qū)ο蟮囊粋€子集,用于專門的上下文中。一般來說或領(lǐng)域?qū)ο笫且粋€對象圖,而數(shù)據(jù)遷移對象僅僅是所需部分?jǐn)?shù)據(jù)的投射而已。

業(yè)務(wù)對象的屬性來自于其映射的實體的屬性,業(yè)務(wù)對象的方法來自于自身的職責(zé)以及應(yīng)用到該實體上的部分業(yè)務(wù)規(guī)則。業(yè)務(wù)規(guī)則在很大程度上是對數(shù)據(jù)的驗證。換句話說,很多業(yè)務(wù)規(guī)則說到底就是驗證某個業(yè)務(wù)對象的當(dāng)前內(nèi)容。按照這樣的理解,若有專門的驗證層,并讓業(yè)務(wù)對象可選擇的支持,這個設(shè)計將會非常不錯。

業(yè)務(wù)邏輯層不應(yīng)該看作是一個整體的組件,或是一些不相干模塊的組合。多年的實踐經(jīng)驗告訴我們,業(yè)務(wù)邏輯層在其他層中適當(dāng)?shù)闹貜?fù)是可以接受的,也是很多程序的做法。不過這種做法是有一定的限度,且不應(yīng)該受到鼓勵。

業(yè)務(wù)邏輯是系統(tǒng)的核心,不過并不是整個系統(tǒng)。業(yè)務(wù)邏輯設(shè)計上的選擇將會影響到其他層,特別是持久化和數(shù)據(jù)訪問層,這兩層加起來,對項目的成敗產(chǎn)生了決定性的影響。

業(yè)務(wù)邏輯層
  1. 添加引用
    在業(yè)務(wù)邏輯層BLL添加對模型層Model、數(shù)據(jù)訪問層接口IDAL、數(shù)據(jù)訪問層工廠類DALFactory的引用。


    BLL添加引用

2.創(chuàng)建基礎(chǔ)業(yè)務(wù)邏輯的抽象類

using OA.DALFactory;
using OA.IDAL;
using System;
using System.Linq;
using System.Linq.Expressions;

namespace OA.BLL
{
    public abstract class BaseService<T> where T:class,new()
    {
        /*使用多態(tài)完成子類對父類中當(dāng)前數(shù)據(jù)訪問類的實例化*/
        public abstract void SetDbSession();
        public BaseService()
        {
            SetDbSession();//子類必須要實現(xiàn)抽象方法
        }
        /*獲取當(dāng)前數(shù)據(jù)會話類*/
        public IDbSession CurrentDbSession
        {
            get
            {
                return new DbSession();                     
            }
        }
        /*獲取當(dāng)前數(shù)據(jù)訪問類*/
        public IBaseDal<T> CurrentDal { get; set; }
        /*公共的基礎(chǔ)操作與DAL保持一致*/

        public IQueryable<T> Get(Expression<Func<T, bool>> whereLambda)
        {
            return CurrentDal.Get(whereLambda);
        }
        public IQueryable<T> Page<s>(int pageIndex, int pageSize, out int totalCount, Expression<Func<T,bool>> whereLambda, Expression<Func<T,s>> orderByLambda, bool isAsc)
        {
            return CurrentDal.Page(pageIndex, pageSize, out totalCount, whereLambda, orderByLambda, isAsc);
        }
        public T Create(T entity)
        {
            CurrentDal.Create(entity);
            CurrentDbSession.SaveChanges();
            return entity;
        }
        public bool Update(T entity)
        {
            CurrentDal.Update(entity);
            return CurrentDbSession.SaveChanges();
        }
        public bool Delete(T entity)
        {
            CurrentDal.Delete(entity);
            return CurrentDbSession.SaveChanges();
        }
    }
}

  1. 創(chuàng)建具體的業(yè)務(wù)邏輯類并實現(xiàn)抽象父類中的方法
$ vim OA.BLL/UserInfoService.cs
using OA.Model;

namespace OA.BLL
{
    public class UserInfoService : BaseService<UserInfo>
    {
        public override void SetDbSession()
        {
            //根據(jù)當(dāng)前數(shù)據(jù)會話類獲取當(dāng)前數(shù)據(jù)訪問類
            CurrentDal = this.CurrentDbSession.UserInfoDal;
        }
    }
}

在業(yè)務(wù)基類BaseService中完成數(shù)據(jù)會話層類DbSession的調(diào)用,將業(yè)務(wù)層中公共的方法定義在業(yè)務(wù)基類BaseService中。公共的方法并不知道通過DbSession來獲取那個數(shù)據(jù)操作類的實例。因此,將該業(yè)務(wù)基類定義成抽象類Abstract class,并加上一個抽象方法public abstract void SetDbSession()和一個IBaseDal的屬性public IBaseDal<T> CurrentDal { get; set; },并且讓基類的構(gòu)造方法調(diào)用抽象方法CurrentDal = this.CurrentDbSession.UserInfoDal,目的是在表現(xiàn)層new具體的業(yè)務(wù)子類時,父類的構(gòu)造方法被調(diào)用,此時執(zhí)行抽象方法(執(zhí)行的是子類中具體的實現(xiàn)),業(yè)務(wù)子類通過DbSession獲取哪個數(shù)據(jù)操作類的實例。

  1. 為展示層添加業(yè)務(wù)邏輯層接口
    為業(yè)務(wù)邏輯層接口IBLL添加對模型層Model和數(shù)據(jù)訪問層接口IDAL的引用。
    IBLL添加引用

    創(chuàng)建業(yè)務(wù)邏輯層基類接口
$ vim OA.IBLL/IBaseService.cs
using OA.IDAL;
using System;
using System.Linq;
using System.Linq.Expressions;

namespace OA.IBLL
{
    public interface IBaseService<T> where T : class, new()
    {
        IDbSession CurrentDbSession { get; }
        IBaseDal<T> CurrentDal { get; set; }
        IQueryable<T> Get(Expression<Func<T, bool>> whereLambda);
        IQueryable<T> Page<s>(int pageIndex, int pageSize, out int totalCount, Expression<Func<T, bool>> whereLambda, Expression<Func<T, s>> orderByLambda, bool isAsc);
        T Create(T entity);
        bool Update(T entity);
        bool Delete(T entity);
    }
}

創(chuàng)建具體業(yè)務(wù)邏輯層接口

$ vim OA.IBLL/IUserInfoService.cs
using OA.Model;

namespace OA.IBLL
{
    public interface IUserInfoService:IBaseService<UserInfo>
    {
    }
}
  1. 為業(yè)務(wù)邏輯層BLL基類添加引用和接口
    在業(yè)務(wù)邏輯層BLL中添加對業(yè)務(wù)邏輯層接口IBLL的引用
    添加引用

    在具體業(yè)務(wù)邏輯層中添加對應(yīng)的接口實現(xiàn),原則是“先繼承后實現(xiàn)”。
$ vim OA.BLL/UserInfoService.cs
using OA.IBLL;
using OA.Model;

namespace OA.BLL
{
    public class UserInfoService : BaseService<UserInfo>,IUserInfoService
    {
        public override void SetDbSession()
        {
            //根據(jù)當(dāng)前數(shù)據(jù)會話類獲取當(dāng)前數(shù)據(jù)訪問類
            CurrentDal = this.CurrentDbSession.UserInfoDal;
        }
    }
}

業(yè)務(wù)邏輯層與數(shù)據(jù)訪問層思路是一樣的

  1. 為防止連續(xù)多次的實例化DbSession,改造成工廠模式以保證線程內(nèi)唯一。

DALFactory中創(chuàng)建DbSessionFactory工廠類

$ vim OA.DALFactory/DbSessionFactory.cs
using OA.IDAL;
using System.Runtime.Remoting.Messaging;

namespace OA.DALFactory
{
    public class DbSessionFactory
    {
        public static IDbSession CreateDbSession()
        {
            IDbSession dbSession = (IDbSession)CallContext.GetData("dbSession");
            if (dbSession == null)
            {
                dbSession = new DbSession();
                CallContext.SetData("dbSession", dbSession);
            }
            return dbSession;
        }
    }
}

改造BaseService

$ vim OA.BLL/BaseService.cs
using OA.DALFactory;
using OA.IDAL;
using System;
using System.Linq;
using System.Linq.Expressions;

namespace OA.BLL
{
    public abstract class BaseService<T> where T:class,new()
    {
        /*使用多態(tài)完成子類對父類中當(dāng)前數(shù)據(jù)訪問類的實例化*/
        public abstract void SetDbSession();
        public BaseService()
        {
            SetDbSession();//子類必須要實現(xiàn)抽象方法
        }
        /*獲取當(dāng)前數(shù)據(jù)會話類*/
        public IDbSession CurrentDbSession
        {
            get
            {
                //return new DbSession();                     
                return DbSessionFactory.CreateDbSession();
            }
        }
        /*獲取當(dāng)前數(shù)據(jù)訪問類*/
        public IBaseDal<T> CurrentDal { get; set; }
        /*公共的基礎(chǔ)操作與DAL保持一致*/

        public IQueryable<T> Get(Expression<Func<T, bool>> whereLambda)
        {
            return CurrentDal.Get(whereLambda);
        }
        public IQueryable<T> Page<s>(int pageIndex, int pageSize, out int totalCount, Expression<Func<T,bool>> whereLambda, Expression<Func<T,s>> orderByLambda, bool isAsc)
        {
            return CurrentDal.Page(pageIndex, pageSize, out totalCount, whereLambda, orderByLambda, isAsc);
        }
        public T Create(T entity)
        {
            CurrentDal.Create(entity);
            CurrentDbSession.SaveChanges();
            return entity;
        }
        public bool Update(T entity)
        {
            CurrentDal.Update(entity);
            return CurrentDbSession.SaveChanges();
        }
        public bool Delete(T entity)
        {
            CurrentDal.Delete(entity);
            return CurrentDbSession.SaveChanges();
        }
    }
}

重點

/*獲取當(dāng)前數(shù)據(jù)會話類*/
public IDbSession CurrentDbSession
{
    get
    {
        //return new DbSession();                     
        return DbSessionFactory.CreateDbSession();
    }
}

服務(wù)層與門面層

由于展現(xiàn)層中與業(yè)務(wù)層產(chǎn)生緊密的耦合關(guān)系,在分布式的環(huán)境中,無法分割部署。因此有必要將展現(xiàn)層與業(yè)務(wù)層進行解耦,在其間添加服務(wù)層或門面層,典型如WebService、WCF、WebAPI等技術(shù)。實際上,服務(wù)層主要完成的是對業(yè)務(wù)層實例化的操作,可采用之前的抽象工廠的方式,推薦的方式是使用IoC控制反轉(zhuǎn)的容器來實現(xiàn)。使用第三方的IoC容器,除了能夠統(tǒng)一進行實例化對象外還可對其進行依賴注入DI,即對實例化后對象進行一些初始化的操作。在此,推薦的第三方組建如Sprint.Net、Unity等,都是功能強大的控制反轉(zhuǎn)的容器。

服務(wù)層

在領(lǐng)域模型模式中,大多將服務(wù)層看作業(yè)務(wù)層的一部分,通常來說,服務(wù)層為表現(xiàn)層定義了一個接口,從而允許表現(xiàn)層出發(fā)一些預(yù)定義的系統(tǒng)操作。服務(wù)層可看作是表現(xiàn)層結(jié)束,業(yè)務(wù)邏輯層開始的一個邊界,服務(wù)層用來盡可能降低表現(xiàn)層和義務(wù)邏輯層之間的耦合。讓表現(xiàn)層無需關(guān)注業(yè)務(wù)邏輯層中具體實現(xiàn)組織方式。因此,無論采用任何一種業(yè)務(wù)邏輯模式(表模式、活動記錄、領(lǐng)域模型等),系統(tǒng)都可以提供一個服務(wù)。

實際上服務(wù)層不執(zhí)行任何具體的工作,其功能在于組織各個業(yè)務(wù)對象。服務(wù)層非常了解業(yè)務(wù)邏輯(包括工作流、組件、服務(wù)),進而也非常了解領(lǐng)域模型。服務(wù)層不僅組織業(yè)務(wù)邏輯,還組織應(yīng)用程序?qū)S械姆?wù)、工作流以及其他任何在業(yè)務(wù)邏輯層中的特殊組件。

用服務(wù)作為表現(xiàn)層和業(yè)務(wù)層之間的名字是存在爭議的,這一層可通過Web服務(wù)或WCF服務(wù)實現(xiàn),也可選擇其他技術(shù)。雖然服務(wù)層有服務(wù)一詞,但要將其理解為一個與技術(shù)無關(guān)的詞匯。

服務(wù)層位于系統(tǒng)中兩個相互通信的邏輯層之間,使兩個層能夠在松散耦合并又沒彼此離開的同時,仍舊可完美地相互通信。

每個用戶驅(qū)動的交互的核心都包括兩個參與者:表現(xiàn)層的用戶界面和服務(wù)層實現(xiàn)的用以響應(yīng)用戶操作的模塊。也就是說服務(wù)層不僅用來i組織業(yè)務(wù)邏輯,也許要與持久化層進行交互。所有的交互都源自于表現(xiàn)層,并從服務(wù)層獲取響應(yīng),根據(jù)接收的輸入,服務(wù)層將組織業(yè)務(wù)邏輯層中的組件,包括服務(wù)、工作流、領(lǐng)域模型中的對象,并根據(jù)需要調(diào)用數(shù)據(jù)訪問層。

不僅僅只有服務(wù)層會發(fā)送數(shù)據(jù)操作請求,業(yè)務(wù)還有其他情況,業(yè)務(wù)邏輯層也可能包含一些工作流或業(yè)務(wù)服務(wù)需要使用的數(shù)據(jù)訪問層。業(yè)務(wù)邏輯層唯一需要完全和數(shù)據(jù)庫細節(jié)分離的部分就是領(lǐng)域模型。

服務(wù)從廣義上來講,只要是使用別人的東西那么就是在使用別人提供的服務(wù)。在這里,服務(wù)是指可能被一個或多個系統(tǒng)使用的核心的業(yè)務(wù)邏輯,可見其簡單的想象成一些可供調(diào)用的API。

如何將業(yè)務(wù)邏輯層提供給其他層來調(diào)用呢?

在很多系統(tǒng)中,不是直接將業(yè)務(wù)層的組件引用就可以的,特別是在分布式的系統(tǒng)中,往往在服務(wù)端暴露一些服務(wù)接口,讓其他子系統(tǒng)或外部系統(tǒng)來調(diào)用提供的服務(wù)。

一般來說,服務(wù)層位于業(yè)務(wù)層和表現(xiàn)層之間,當(dāng)前服務(wù)層也可以處于系統(tǒng)與系統(tǒng)之間。服務(wù)層往往提供一些供外部調(diào)用的服務(wù)接口。這些接口是一些粗粒度即提供一些簡單易用功能強大的接口。當(dāng)客戶端調(diào)用接口服務(wù)后,服務(wù)層就開始處理比較復(fù)雜的業(yè)務(wù)邏輯、驗證規(guī)則、持久化數(shù)據(jù)等。

服務(wù)層內(nèi)的邏輯的組織形式類似于Transaction Script模式,可簡單地把服務(wù)層看作一個中介,從客戶端接收請求,通過一系列的步驟后,請求到達服務(wù)層,服務(wù)層開始協(xié)調(diào)和組織所需的業(yè)務(wù)類,把請求的具體處理交給業(yè)務(wù)類來處理,最后將結(jié)果返回給客戶端。

在服務(wù)層的邏輯組織往往是比較過程化的,與Transaction Script不同的是,Transaction Script的每個方法處理一個比較細小而具體的業(yè)務(wù)流程和邏輯。而服務(wù)層的接口往往處理的是一個較大的流程。

Spring.Net

.NET中創(chuàng)建對象最常用的方式是通過new實例化創(chuàng)建對象,這樣的做法違背了“層與層之間松散耦合”的原則。Spring.Net使用了IoC、DI等概念,提供了一種全新的創(chuàng)建對象的方式。

控制反轉(zhuǎn)IoC,指原來創(chuàng)建對象的權(quán)力由程序來控制即new實例化,IoC則改由容器來創(chuàng)建,相當(dāng)于一個工廠。
依賴注入DI,沒有IoC就沒有DI,依賴注入指的是容器在創(chuàng)建對象時,通過讀取配置文件設(shè)置的默認值,使其在創(chuàng)建時就擁有某些注入的值,用以初始化。

Spring.Net是一個依賴注入的設(shè)計框架,使項目的層與層之間解耦達到更加靈活。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 32,010評論 2 89
  • 很多人參加各種的學(xué)習(xí),各種課程,最后會下個結(jié)論,這個東西沒用。 做一個學(xué)習(xí)教練,可以很負責(zé)任的告訴大家,任何人敢收...
    學(xué)習(xí)教練蘇仲平閱讀 220評論 0 2
  • 昨晚自己居然會出奇的看關(guān)于圣經(jīng)的故事的視頻,現(xiàn)在想想也是因為最近迷戀的一本達芬奇密碼,主要是這本書就寫了關(guān)于圣經(jīng)...
    deerDennis閱讀 233評論 0 0
  • ??今年算是我閱讀的豐收年,根據(jù)豆瓣上的記錄,新書讀了163本,雖然讀得多消化不了并沒有什么用,但是還是寫下來,作...
    Thirtiseven閱讀 1,233評論 4 31
  • 一句話概括: 不可管理時間,只可管理自己。 三點感悟: 1. 承認你很平凡,你有不足,成功需要積累。 2. 智慧與...
    MollyU閱讀 473評論 0 0