文章出處

  如果你還沒有接觸過或者根本不了解什么是Entity Framework,那么請看這里http://www.entityframeworktutorial.net/EntityFramework-Architecture.aspx,其中的一系列文章以非常簡單易懂的形式一步步介紹了Entity Framework的一些基本概念和操作方法。一句話,Entity Framework是微軟新的數據操作框架,在項目中引入Entity Framework可以極大的方便開發人員完成程序與數據庫的各種操作。在早期的.NET應用程序中,我們直接通過SqlConnection,SqlDataAdapter,DataReader等一些基本對象來實現與數據庫的操作,與數據庫的每一次交互都需要專門編寫大量的代碼。為了將程序員的精力從這些看似無聊的代碼中釋放出來,讓更多的人去關注程序業務方面的操作,后來出現了各種與數據庫交互的類庫,有一些是開源的,也有一些是程序員自己編寫的通用類庫。這里面比較著名的有NHibernate.NET——一個基于Mapping的關系型數據庫框架。NHibernate功能強大,不過由于配置起來稍微有點復雜并且缺少默認的VS代碼生成工具,在快速小型項目中往往難以得心應手。Entity Framework (以下簡稱EF)的出現從根本上解決了這一問題,使得程序員可以徹底從與數據庫交互的底層代碼中釋放出來,與數據庫的交互變得前所未有的簡單。

  使用EF在與關系型數據庫的交互中不可避免地需要加載數據,如何加載數據變得至關重要。你可以設想在一個包含數十萬條數據的表中,你如何通過EF來加載數據呢?一次性將所有數據載入服務器內存或者在循環中一遍又一遍地分步加載數據?使用什么樣的數據加載方式需要具體問題具體分析,我們不能在這里籠統地下決定說哪種方式好哪種方式不好。但有一點是需要遵循的,那就是如何提高數據加載的效率。EF提供了幾種不同的數據加載方式,我們可以根據不同的需要靈活使用它們。

  先簡單說一下如何創建環境。如果你對這些步驟了如指掌,請直接跳過。

1. 在Visual Studio中創建一個示例工程。最簡單的莫過于ConsoleApplication

2. 在工程中添加ADO.NET Entity Data Model。如下圖所示,

  選擇其中的兩個表作為示例,表Teacher和表Course,關系如下,

  添加edmx之后,Visual Studio為自動幫我們生成/添加所有需要的文件和內容,然后我們就可以開始在代碼中操作數據庫了。來看看在EF中幾種不同的數據加載方式。

 

惰性加載(Lazy Loading)

  默認情況下,EF會使用惰性加載方式加載數據,即ctx.Configuration.LazyLoadingEnabled = true; 在下面的代碼中,外層循環會執行一次查詢,并將返回的結果存放在變量q中。而內層循環會在每一次循環過程中獨立進行查詢,所以,如果數據庫表Teacher中有100條記錄而Course有1000條記錄,那么整個過程將產生1001次查詢。

using (var ctx = new SchoolDBEntities())
{
    var q = from t in ctx.Teachers
            select t;

    foreach (var teacher in q)
    {
        Console.WriteLine("Teacher : {0}", teacher.TeacherName);
        Console.WriteLine("Respective Courses...");

        foreach (var course in teacher.Courses)
        {
            Console.WriteLine("Course name : {0}", course.CourseName);
        }

        Console.WriteLine();
        Console.ReadKey();
    }
}

  下面是程序執行的結果以及在SQL Server Profiler中的跟蹤記錄。可以清楚地看到,對表Teacher只進行了一次查詢,由于該表只有8條記錄,于是在內層循環中又分別產生了8次對表Course的查詢。

  在某些場合下,這種情況是可以接受的。你完全可以根據需要來控制內層循環何時顯示加載數據,或者根本不加載數據。但是,在分層結構的應用程序中,上述代碼結構并不適用,因為內層循環需要依賴于外層的Context,也就是說它們是在同一個數據庫上下文中完成的,如果嘗試將內層循環的代碼移到外面或者其它類中,則它將獲取不到任何數據。

 

顯式加載(Explicit Loading)

  如果你想人為控制惰性加載的行為,可以嘗試使用下面的代碼。首先需要手動關閉EF的惰性加載,通過代碼ctx.Configuration.LazyLoadingEnabled = false;來完成。

using (var ctx = new SchoolDBEntities())
{
    ctx.Configuration.LazyLoadingEnabled = false;

    var q = from t in ctx.Teachers
            select t;
    
    foreach (var teacher in q)
    {
        Console.WriteLine("Teacher : {0}", teacher.TeacherName);
        Console.WriteLine("Respective Courses...");

        // Conditionally load the child data
        if (true)
        {
            ctx.Entry(teacher).Collection(c => c.Courses).Load();
        }

        foreach (var course in teacher.Courses)
        {
            Console.WriteLine("Course name : {0}", course.CourseName);
        }

        Console.WriteLine();
        Console.ReadKey();
    }
}

  注意內層循環只有在上面高亮顯示部分的代碼執行之后才會獲取到數據,否則返回結果為0。通過添加判斷條件,我們可以對數據加載方式進行控制,從而有效地減少程序與數據庫交互的次數。大多數情況下,我們從數據庫獲取到的數據并不都是有用的,如果每次只有很少一部分數據有用,那么我們為什么不過濾掉那些無用的數據從而盡量較少數據交互的次數呢?

 

預先加載(Eager Loading)

  如果你想讓所有數據一次性全部加載到內存中,那么你需要使用.Include(Entity)方法。看下面的代碼,

using (var ctx = new SchoolDBEntities())
{
    var q = from t in ctx.Teachers.Include("Courses")
            select t;
    
    foreach (var teacher in q)
    {
        Console.WriteLine("Teacher : {0}", teacher.TeacherName);
        Console.WriteLine("Respective Courses...");

        foreach (var course in teacher.Courses)
        {
            Console.WriteLine("Course name : {0}", course.CourseName);
        }

        Console.WriteLine();
        Console.ReadKey();
    }
}

  如果你查看SQl Server Profiler中的跟蹤信息,你會發現只有一次數據交互過程,即程序只通過一次查詢便獲取到了所有需要的數據。在分層結構中,該方法是最容易的,我們可以將數據庫底層獲取到的結果返回給上層,它不具有任何依賴項。同時,它也可以減少程序與數據庫的交互次數。不過仍然有缺點,那就是如果數據量較大,一次性將所有數據載入內存往往并不是最明智的選擇。.Include(Entity)方法允許級聯使用,你可以預先加載具有多層級結構的數據。

  就像前面所說,選擇什么樣的數據加載方式需要因時而異,每一種數據加載方式都有它存在的意義,但目的只有一個,那就是以最少的代價獲取到需要的數據。

相關文章:Loading Related Objects (Entity Framework)


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()