Entity Framework 實(shí)體框架的形成之旅--基于泛型的倉(cāng)儲(chǔ)模式的實(shí)體框架(1)

很久沒有寫博客了,一些讀者也經(jīng)常問問一些問題,不過最近我確實(shí)也很忙,除了處理日常工作外,平常主要的時(shí)間也花在了繼續(xù)研究微軟的實(shí)體框架(EntityFramework)方面了。這個(gè)實(shí)體框架加入了很多特性(例如LINQ等),目前也已經(jīng)應(yīng)用的比較成熟了,之所以一直沒有整理成一個(gè)符合自己開發(fā)模式的實(shí)體框架,是因?yàn)檫@個(gè)框架和原來我的基于EnterpriseLibrary的模式還是有很大的不同,不過實(shí)體框架推出來也很久了,目前也去到了EntityFramework6了,聽說7也快出來了。
隨著我自己參考閱讀了大量的項(xiàng)目源碼以及對(duì)實(shí)體框架各個(gè)技術(shù)點(diǎn)的學(xué)習(xí)深入,對(duì)其中很多的方面都有自己的一些見解和心得,希望通過這個(gè)系列,能夠和讀者一步步分析,一步步深入學(xué)習(xí)這個(gè)微軟目前最為流行的.NET開發(fā)框架。本篇主要從基礎(chǔ)開始一步步介紹基于泛型的倉(cāng)儲(chǔ)模式實(shí)體框架(The Entity Framework of Generic Repository Pattern ),希望大家耐心閱讀。

1、實(shí)體框架的初步印象

最簡(jiǎn)單的實(shí)體框架,你可以在Winform或者Web項(xiàng)目里面添加一個(gè)【ADO.NET實(shí)體數(shù)據(jù)模型】項(xiàng)開始,一步步創(chuàng)建一個(gè)基于SqlServer的實(shí)體框架項(xiàng)目。最開始,我們可以不考慮什么設(shè)計(jì)模式,能夠使用即可,因此我們可能創(chuàng)建一個(gè)比較簡(jiǎn)單的項(xiàng)目代碼,這個(gè)有助于我們了解實(shí)體框架的一些基礎(chǔ)工作原理。



為這個(gè)項(xiàng)目選定數(shù)據(jù)連接以及供測(cè)試使用的一兩個(gè)表的對(duì)象,然后完成創(chuàng)建工作,這個(gè)【ADO.NET實(shí)體數(shù)據(jù)模型】創(chuàng)建完成后,我們可以看到項(xiàng)目里面添加了一個(gè)Model1.edmx的文件,并且同時(shí)生成了幾個(gè)項(xiàng)目文件,其中包括了數(shù)據(jù)訪問對(duì)象SqlserverContext和幾個(gè)實(shí)體類(默認(rèn)為表名稱),我們也可以打開edmx的文件進(jìn)行實(shí)體類屬性的修改,如下所示。



默認(rèn)生成后,我們就可以使用這個(gè)數(shù)據(jù)訪問上下文對(duì)象SqlserverContext, 來進(jìn)行相關(guān)的數(shù)據(jù)處理操作了,簡(jiǎn)單的測(cè)試代碼如下所示。
private void GetIntData()
{
    //創(chuàng)建數(shù)據(jù)訪問對(duì)象
    var context = new SqlserverContext();
            
    //新建一個(gè)實(shí)體類并賦值    
    TB_Province info = new TB_Province();
    info.ID = 100001;
    info.ProvinceName = "測(cè)試省份";
    context.TB_Province.Add(info);
    context.SaveChanges();

    //根據(jù)主鍵判斷記錄是否存在
    TB_Province info2 = context.TB_Province.Find(info.ID);
    if (info2 != null)
    {
        Console.WriteLine("記錄已存在!");

        //如果存在對(duì)象,先刪除
        context.TB_Province.Remove(info2);
        context.SaveChanges();

        //檢查是否刪除對(duì)象
        info2 = context.TB_Province.Find(info.ID);
        if (info2 == null)
        {
            Console.WriteLine("記錄已刪除!");
        }
    }
    
    //把記錄全部獲取并綁定到列表上。
    var list = context.TB_Province.ToList();
    this.dataGridView1.DataSource = list;
}

最后獲得的界面效果就是能夠順利執(zhí)行各種操作后把記錄顯示出來到列表上了。


2、實(shí)體框架的工作原理

1)數(shù)據(jù)訪問上下文對(duì)象介紹
從上面的代碼我們可以看到,數(shù)據(jù)訪問上下文對(duì)象SqlserverContext已經(jīng)可以直接和數(shù)據(jù)庫(kù)交互了,能夠?qū)崿F(xiàn)表對(duì)象基本增刪改查的操作功能了,那么這個(gè)類是如何的呢?為什么具有這個(gè)功能呢?
我們先看看它的代碼,SqlserverContext的類代碼如下所示(代碼為自動(dòng)生成的)。

public partial class SqlserverContext : DbContext
{
    public SqlserverContext()
        : base("name=SqlserverContext")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }

    public virtual DbSet<TB_City> TB_City { get; set; }
    public virtual DbSet<TB_Province> TB_Province { get; set; }
    public virtual DbSet<TB_DictType> TB_DictType { get; set; }
}

其中代碼DbSet<TB_Province> TB_Province代表一個(gè)具體的數(shù)據(jù)訪問對(duì)象,對(duì)表TB_Province的數(shù)據(jù)訪問,其他的類似。我們查看.NET的內(nèi)置對(duì)象DbSet的已經(jīng)支持了一些常規(guī)的操作了。



而EMDX文件的本身是一個(gè)XML文件,它的內(nèi)容如下所示。



實(shí)體框架本身通過XML映射的方式(ORM方式),封裝了從數(shù)據(jù)庫(kù)到實(shí)體類,以及實(shí)體類到數(shù)據(jù)庫(kù)的交互過程,具體的過程我們可以參考下面的實(shí)體數(shù)據(jù)模型 (EDM)介紹。

2)實(shí)體數(shù)據(jù)模型 (EDM)介紹
Entity Framework 實(shí)體框架的要點(diǎn)是實(shí)體數(shù)據(jù)模型 (EDM),一個(gè)用于描述應(yīng)用程序域?qū)ο蟮母拍钅P汀?Entity Framework 實(shí)體框架讓開發(fā)人員可以針對(duì)實(shí)體數(shù)據(jù)模型提出查詢,而不必操心數(shù)據(jù)庫(kù)的具體操作。 實(shí)體數(shù)據(jù)模型的實(shí)體以及實(shí)體之間的關(guān)系以 XML 形式定義,而開發(fā)人員基于該模型的實(shí)體來處理強(qiáng)類型化類。
在運(yùn)行時(shí),利用特定于數(shù)據(jù)庫(kù)的 ADO.NET 提供程序,Entity Framework 實(shí)體框架將針對(duì)實(shí)體數(shù)據(jù)模型而創(chuàng)建的查詢轉(zhuǎn)換為存儲(chǔ)查詢(例如 T-SQL),然后送至數(shù)據(jù)庫(kù)。 Entity Framework 將查詢結(jié)果轉(zhuǎn)換為由強(qiáng)類型化實(shí)體類所定義的對(duì)象。


實(shí)體數(shù)據(jù)模型 (EDM),由三個(gè)概念組成。概念模型由概念架構(gòu)定義語言文件 (.csdl)來定義,映射由映射規(guī)范語言文件 (.msl),存儲(chǔ)模型(又稱邏輯模型)由存儲(chǔ)架構(gòu)定義語言文件 (.ssdl)來定義。這三者合在一起就是EDM模式。EDM模式在項(xiàng)目中的表現(xiàn)形式就是擴(kuò)展名為.edmx的文件。這個(gè)包含EDM的文件可以使用Visual Studio中的EDM設(shè)計(jì)器來設(shè)計(jì)。由于這個(gè)文件本質(zhì)是一個(gè)xml文件,可以手工編輯此文件來自定義CSDL、MSL與SSDL這三部分。

CSDL定義了EDM或者說是整個(gè)程序的靈魂部分 – 概念模型。這個(gè)文件完全以程序語言的角度來定義模型的概念。即其中定義的實(shí)體、主鍵、屬性、關(guān)聯(lián)等都是對(duì)應(yīng)于.NET Framework中的類型。
SSDL這個(gè)文件中描述了表、列、關(guān)系、主鍵及索引等數(shù)據(jù)庫(kù)中存在的概念。
MSL這個(gè)文件即上面所述的CSDL與SSDL的對(duì)應(yīng),主要包括CSDL中屬性與SSDL中列的對(duì)應(yīng)。

通過以上三個(gè)XML文件的映射關(guān)系,在程序里面,就主要是利用強(qiáng)類型數(shù)據(jù)的實(shí)體類進(jìn)行處理了,而對(duì)實(shí)體類的任何處理修改,最終會(huì)解析后得到相應(yīng)的數(shù)據(jù)庫(kù)執(zhí)行語句,然后進(jìn)行提交處理了。

3、基于泛型的倉(cāng)儲(chǔ)模式實(shí)體框架

如果基于第一點(diǎn)來構(gòu)建框架,雖然很快速,但是這樣的做法在中大型的項(xiàng)目里肯定不可取,因?yàn)樯珊蟮拇a還需要進(jìn)行多個(gè)步驟的修改調(diào)整,而且也沒有很好實(shí)現(xiàn)重用的目的,很多地方需要自己手動(dòng)編碼處理,結(jié)構(gòu)也不是很清晰,因此需要對(duì)框架進(jìn)行一步步的優(yōu)化和提煉。
在介紹基于泛型的倉(cāng)儲(chǔ)模式實(shí)體框架(The Entity Framework of Generic Repository Pattern )前,我們先來回顧一下我之前的Winform開發(fā)框架分層結(jié)構(gòu),這個(gè)基于Enterprise Library的框架,常見的分層模式,可以分為UI層、BLL層、DAL層、IDAL層、Entity層、公用類庫(kù)層等等。



這種分層可以在數(shù)據(jù)庫(kù)設(shè)計(jì)完成后,可以通過代碼生成工具,獲取到表對(duì)象的信息和關(guān)系,直接快速生成相應(yīng)的分層代碼,從而實(shí)現(xiàn)架構(gòu)、分層、命名規(guī)則等方面的一致化,并且是快速開發(fā)。
而且這種分層模式也是一種比較通用的分層結(jié)構(gòu)了,那么我們要介紹的實(shí)體框架是否也可以依照這種方式來構(gòu)建呢?是否可以結(jié)合代碼生成工具的生成模板來進(jìn)行整體性框架的開發(fā)呢?
下面我們來介紹一下泛型的倉(cāng)儲(chǔ)模式框架的具體實(shí)現(xiàn)過程。
1)實(shí)體類的代碼如下所示(先按表名生成)。

public partial class TB_City
{
    public long ID { get; set; }
    public string CityName { get; set; }
    public string ZipCode { get; set; }
    public Nullable<long> ProvinceID { get; set; }
}

2)數(shù)據(jù)訪問基類接口層(定義了幾個(gè)測(cè)試的基類接口)

/// <summary>
/// 數(shù)據(jù)訪問層基類接口
/// </summary>
/// <typeparam name="T">實(shí)體對(duì)象類型</typeparam>
public interface IBaseDAL<T> where T : class
{
    T Get(object id);

    IList<T> GetAll(Expression<Func<T, bool>> whereCondition);

    IList<T> GetAll();
}

3)數(shù)據(jù)訪問層基類實(shí)現(xiàn)層

/// <summary>
/// 數(shù)據(jù)訪問層基類實(shí)現(xiàn)層
/// </summary>
/// <typeparam name="T">實(shí)體對(duì)象類型</typeparam>
public abstract class BaseDAL<T> : IBaseDAL<T>  where T : class
{
    protected DbContext baseContext;
    protected IDbSet<T> objectSet;

    public BaseDAL(DbContext context)
    {
        this.baseContext = context;
        this.objectSet = this.baseContext.Set<T>();
    }

    public T Get(object id)
    {
        return objectSet.Find(id);
    }

    public IList<T> GetAll()
    {
        return objectSet.ToList<T>();;
    }

    public IList<T> GetAll(Expression<Func<T, bool>> whereCondition)
    {
        return objectSet.Where(whereCondition).ToList<T>();
    }
}

4)具體數(shù)據(jù)訪問對(duì)象接口定義(城市表為例)

/// <summary>
/// 城市數(shù)據(jù)訪問層接口
/// </summary>
public interface ICityDAL : IBaseDAL<City>
{
}

5)具體數(shù)據(jù)訪問對(duì)象實(shí)現(xiàn)層(城市表為例)

/// <summary>
/// 城市數(shù)據(jù)訪問對(duì)象
/// </summary>
public class CityDAL : BaseDAL<TB_City> 
{
    protected MyDataContext context;

    /// <summary>
    /// 構(gòu)造函數(shù)
    /// </summary>
    /// <param name="context"></param>
    public CityDAL(MyDataContext context) :base(context)
    {
        this.context = context;
    }

6)數(shù)據(jù)倉(cāng)儲(chǔ)對(duì)象(上下文對(duì)象)

public class MyDataContext : DbContext
{
    public MyDataContext() : base("name=sqlserver")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }

    public virtual DbSet<TB_City> City { get; set; }
}

BLL、IBLL的分層和數(shù)據(jù)訪問層的類似,主要是提高一份,方便做業(yè)務(wù)整合實(shí)現(xiàn)而已,在此不再贅述。
最終實(shí)現(xiàn)倉(cāng)儲(chǔ)模式框架的分層結(jié)構(gòu)如下所示。



以上就是我對(duì)基于泛型的倉(cāng)儲(chǔ)模式的實(shí)體框架的一個(gè)初探性的開端,下面會(huì)在這個(gè)系列里面繼續(xù)分析其中存在的問題,并繼續(xù)優(yōu)化改良這個(gè)基于泛型的倉(cāng)儲(chǔ)模式的實(shí)體框架。希望大家喜歡并繼續(xù)支持。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容