數據點 - 迎接 Entity Framework 7

對實體框架的下一版本的開發正在順利進行中。我在 2014 年度北美 TechEd 上第一次瞭解 EF 團隊的工做內容,當時項目經理 Rowan Miller 討論了 Entity Framework 7 (EF7) 的目標並展現了一些早期的信息。git

那是我撰寫此專欄的 5 個月以前,雖然 EF7 仍處於早期 Alpha 測試階段,但已經取得了很大的進展。在本專欄中,我但願您能瞭解到 EF7 會給開發人員帶來什麼、有關 EF7 的決定背後的動機以及此版本對使用 EF6 或更低版本的現有應用的意義。我還會向您展現其中的部分代碼。github

開放源代碼,可是如今位於 GitHub 上

首先請了解,EF7 和 EF6 同樣,是開放源代碼。但 EF7 以及 ASP.NET 的即將發佈版本的剩餘部分是在 GitHub 上而不是在 CodePlex 上開發的。EF7 開發的 URL 爲 github.com/aspnet/EntityFramework。和 EF6 同樣,您能夠查看 EF7 發展過程的詳細信息。您能夠經過分支和提交探索源代碼及其進度、關注討論、提出問題、派生源代碼以及提交團隊的 pull 請求以進行檢查並在可能時提交到代碼庫。算法

EF6 不會很快退出歷史舞臺

切勿擔憂,不會強制您遷移到 EF7。回想一下 ADO.NET DataSets 和 DataReaders。與仍受支持甚至從偶爾的調整中受益的 ASP.NET Web 窗體很是像,ADO.NET 仍然是 Microsoft .NET Framework 的一部分,即便多年以來 EF 已經成爲 .NET 的主要數據訪問技術。儘管已再也不爲加強這些技術採起任何措施,但它們仍然存在,而且支持不少舊代碼(包括個人代碼)。與其餘技術相比,EF6 的一個巨大優點在於它是開放源代碼,所以儘管 Microsoft 團隊已再也不對 EF6 作大型投資,但社區仍會進行投資。並且,EF 團隊仍在致力於 EF6。他們將繼續進行調整,仔細檢查 pull 請求,並更新 EF6。儘管他們在 2014 年的大部分時間裏全身心地致力於 EF7,但也更新了 EF6。2014 年 2 月發佈了版本 6.1.0;2014 年 6 月發佈了版本 6.1.1;我在撰寫本文時,版本 6.1.2 正處於測試中,即將發佈。我最初很擔憂舊版本的應用是否能繼續運行,但如今再也不有此顧慮。我如今只擔憂那些結合使用 EF 和 .NET Framework 3.五、ObjectContext 等程序的最低版本的應用。可是,若是您還沒有更新這些應用以使用這些年來對 EF 作出的全部改進,則無需過多擔憂 EF7。您能夠在 NuGet 上找到 EF 的全部較低版本的包,能夠一直追溯到 EF 4.1.10311。數據庫

EF7:簡短列表

EF7 新增功能的高級概述以下:架構

  • 支持非關係數據存儲,甚至內存中數據測試。
  • 支持未使用完整 .NET Framework 的計算機和設備。這表示,您能夠在 Windows Phone 和 Windows 應用商店應用以及運行 Mono 的 Linux 和 Macintosh 計算機上使用 EF7。
  • 支持開發人員已請求但沒法經過現有代碼庫實現的許多功能。
  • 繼續支持使用完整 .NET Framework 的應用程序,例如 Windows Presentation Foundation 和其餘客戶端應用程序。
  • EF7 的分發方式與 ASP.NET 5 同樣,而且能夠和 ASP.NET 5 應用配合使用。

熟悉的編碼界面,新的代碼庫

EF 的每一個版本都在不斷完善框架,添加新功能,並對性能和 API 進行微調。正如之前我在本專欄中以及在 2013 年 12 月發佈的概述文章《Entity Framework 6:The Ninja Edition》(bit.ly/1cwjyCD) 中寫道,最新版本的 EF 已經進入一個新的級別,框架新增了不少用戶求之不得的功能,例如異步數據庫執行、使用查詢管道以及自定義代碼優先約定等。在個人 Pluralsight 課程「Entity Framework 6, Ninja Edition:EF6 中的新功能」(bit.ly/PS-EF6) 中,我更深刻討論了這些功能。框架

還有更多功能是開發人員但願在 EF 中找到的且 Microsoft 但願實現的,但用於構建 EF 的已 10 多年的代碼庫仍繼續依賴 ObjectContext,且編碼模式不夠靈活,所以阻礙了該團隊開發更高級的功能。所以作出了一個艱難的決定,即從頭開始從新構建實體框架,而大部分人在面對本身的舊版本軟件時固然也會面臨一樣的難題。異步

EF7 並不是爲數據訪問建立新的框架。而是構建一個新的、可持續性更高的庫,該庫不只支持您多年來依賴於 EF 的功能和工做流,還支持不少新功能。團隊還在爭論該產品應該是下一代 EF 仍是新的數據訪問技術。我一度懷疑它是否應該是「EF 精簡版」。可是,EF 的核心功能仍然存在,所以通過幾番思考以後,我認爲將其視爲實體框架的下一個版本比較有意義。您能夠在團隊博文《EF7 – v1 或 v7?》(bit.ly/1EFEdRH) 中瞭解更多詳細信息。ide

精簡舊版,保留核心

對於一些開發人員而言,也有一些關於 EF7 的消息使人不安。雖然大部分經常使用的 EF 類、模式和工做流將被完整保留,可是一些較少使用的成員將被拋棄。可是,請勿驚慌;我將詳細地討論這一問題。函數

讓開發人員繼續使用熟悉的模式,甚至可以將大量的現有代碼移植到 EF7 中是關鍵目標。您仍可使用 DbContext、DbSet、LINQ 查詢、SaveChanges 和早已成爲 EF 一部分的不少交互方式。工具

我在 EF7 中定義的 DbContext 類以下:

public class BreweryContext : DbContext {
  public DbSet<Brewery> Breweries { get; set; }
  public DbSet<Beer> Beers { get; set; }
}

此外,如下是 EF7 中的一個簡單更新,和 EF6 同樣。我使用的是同步保存,但也提供全部異步方法:

public void StoreBeers(List<Beer> beers) {
  using (var context = new BreweryContext()) {
    context.Beers.AddRange(beers);
    context.SaveChanges();
  }
}

一個簡單查詢以下:

using (var context = new BreweryContext()) {
       return context.Breweries.Where(b=>b.Location.Contains("Vermont"));
}

我使用的 EF7 版本位於包含版本 beta2-11616 的包中。儘管如今 EF7 實際上還未處於測試階段,可是「beta2」與 NuGet 包命名決定有關。截至本文發佈時爲止,EF7 又獲得了進一步完善。所以,請注意上述內容只是大概介紹,並不是保證。

正如我一直所作的那樣,我仍然擁有 DbContext,且定義了 DbSets。OnModelCreating 也仍然在,只是我沒有在本例中使用。

EF4.1 中引入了 DbContext API,其更專一於 EF 的典型用法。實際上,它仍然依賴於原始 ObjectContext,由於 ObjectContext 可提供數據庫交互、管理事務並跟蹤對象狀態。從那時起,DbContext 成爲了使用的默認類,若是您但願與 ObjectContext 的交互較少,則能夠降低到較低級別的 API。EF7 將拋棄冗餘的 ObjectContext;只保留 DbContext。但部分依賴於 ObjectContext 的任務仍可訪問。

一些難以支持且不常使用的極其複雜的映射將在 EF7 中消失。正如上述博文中所述:「例如,您能夠擁有將 TPH、TPT 和 TPC 映射以及 Entity Splitting 組合在同一層次結構中的繼承層次結構。」若是您曾經嘗試直接使用 MetadataWorkspace API,並尖叫着跑開了,您將會發現它是一個錯綜複雜的怪物,可用於支持這種靈活性。可是這種複雜性將阻礙團隊支持用戶請求的其餘方案。經過簡化映射的可能性,MetadataWorkspace API 也變得更加簡單和靈活。您能夠輕鬆地從 EF7 中的 DbContext API 中獲取有關模型架構的元數據,其可賦予您執行高級技術的低級功能,而無需處理低級的 ObjectContext。

放棄 EDMX,但繼續實行數據庫優先

實體框架當前使用兩種方法來描述模型。一種使用設計器中的 EDMX;另外一種涉及類,即代碼優先 API 使用的 DbContext 和映射。若是您使用的是 EDMX 和設計器,則運行時 EF 會在 EDMX 後從 XML 中建立內存中模型。若是您選擇代碼優先路徑,則 EF 會經過讀取類(即您提供的 DbContext 和映射)來建立相同的內存中模型。從那之後 EF 的工做方式是相同的,不管您如何描述模型。請注意,經過 EDMX/設計器工做流,您還能夠獲取 POCO 類和 DbContext 供您在代碼中使用。可是因爲 EDMX 的存在,所以不會使用它們來建立該內存中模型。當您閱讀下文時,理解很重要:EF7 將不支持基於設計器的 EDMX 模型。它沒法在運行時讀取 EDMX XML 來建立內存中模型。它將只使用代碼優先工做流。

當團隊發表有關這方面的博文時,引發了不少開發人員的恐慌。部分緣由是不少開發人員還沒有意識到能夠將數據庫反向工程到 POCO 類、DbContext 和映射。換言之,您能夠從數據庫開始來獲取代碼優先模型。能夠經過 2011 年初首次發佈的 EF Power Tools Beta 來實現這一目的。該工具受 EF6.1 設計器支持,且確定也會受 EF7 支持。我已屢次提到「代碼優先」這一名稱有一些迷惑性和誤導性。最初它稱爲「僅限代碼」,後來這一名稱改成「代碼優先」以完美匹配「數據庫優先」和「模型優先」。

所以,要從現有數據庫開始,您無需設計器或 EDMX。

可是,若是您擁有現有 EDMX 模型,而且不想失去使用設計器的能力,會怎樣呢?有一些第三方設計器支持實體框架,例如早已支持 EF 代碼優先的 LLBLGen Pro Designer (bit.ly/11OLlN2) 以及 Devart Entity Developer (bit.ly/1yHWbB2)。查找可能提供支持 EF7 的設計器的工具以及其餘可能的軟件。

還要記住另外一個方法:堅持使用 EF6!

佔用空間較少,適用的設備和操做系統較多

此外,Microsoft 還致力於簡化 EF API 的分發。EF6.1.1 的 NuGet 包文件夾大約 22MB。包括 5.5MB 的 .NET Framework 4.5 程序集和其餘內容(若是您使用 .NET Framework 4)。對於 EF7,有一些較小的 DLL。您能夠僅組合支持工做流所需的 DLL。例如,若是針對 SQL Server,則使用核心 EntityFramework.dll、針對 SQL Server 的 DLL 以及包含關係數據存儲共用的 API 的 DLL。若是要使用遷移,則您能夠跳過該單獨程序集。不然,您可能要從包管理器控制檯建立並執行遷移。有一個適用於命令的 API。經過 NuGet 包管理器,能夠經過包依賴關係識別並下載適當的包,所以您無需過於擔憂其詳細信息。

其做用是儘可能減小 EF7 在最終用戶的計算機或設備上的佔用空間,這對全部設備而言都很是重要。ASP.NET 也採用此方法。這兩種技術都擺脫了對完整 .NET Framework 的依賴。相反,它們將只分發完成給定應用程序的任務所需的 DLL。這意味着 Windows Phone 和 Windows Store 應用商店應用使用的簡化版本的 .NET 可使用 EF7。

這還意味着,使用 Mono 而非完整 .NET Framework 的 OS X 和 Linux 等操做系統也能夠支持客戶端實體框架。

超越關係

第一次引入實體框架時,Microsoft 的願景是將它應用於多種數據存儲,儘管第一次是應用於關係數據庫。和如今廣受歡迎的 NoSQL 數據庫(尤爲是文檔數據庫)不一樣,當時也存在非關係數據庫,但並未普遍使用。

儘管 EF 是對象關係映射 (ORM),但使用它的開發人員但願可使用相同的架構與非關係數據庫交互。EF7 將爲此提供較高級別的支持,可是請記住較高級別的真正含義。關係數據庫和非關係數據庫之間有極大的差別,但 EF 並不會嘗試掩蓋這些差別。但對於基本查詢和更新,您可使用您已經熟悉的模式。

圖 1 顯示來自針對 Microsoft Azure Table Storage(一種非關係文檔數據庫)的示例應用的代碼。該示例來自 EF 項目經理 Rowan Miller,您能夠訪問 github.com/rowanmiller/Demo-EF7,瞭解詳細信息。請注意,該示例可針對 EF7 Alpha 每夜構建版本的 11514 版本運行。

圖 1 爲使用 Azure Table Storage 定義的 DbContext

public class WarrantyContext : DbContext
{
  public DbSet<WarrantyInfo> Warranties { get; set; }
  protected override void OnConfiguring(DbContextOptions options) {
    var connection =
      ConfigurationManager.ConnectionStrings["WarrantyConnection"]
                        .ConnectionString;
    options.UseAzureTableStorage(connection);
  }
  protected override void OnModelCreating(ModelBuilder builder) {
    builder.Entity<WarrantyInfo>()
           .ForAzureTableStorage()
           .PartitionAndRowKey(w => w.BikeModelNo, w => w.BikeSerialNo);
  }
}

OnConfiguring 方法是新方法。該方法可影響 EF 如何在運行時配置 DbContext,相似於您如今能夠對 DbConfiguration 類執行的操做。請注意 builder.UseAzureTableStorage 擴展方法,存在該方法是由於我還在項目中安裝了 EntityFramework.AzureTableStorage 包。

EF7 爲其不一樣的提供程序使用此模式。如下是針對 SQLite 的項目中 DbContext 類中的 OnConfiguring 方法:

protected override void OnConfiguring(DbContextOptions builder) {
  string dir = ApplicationData.Current.LocalFolder.Path;
  string connection = "Filename=" + Path.Combine(dir, "VermontBrewery.db");
  builder.UseSQLite(connection);
}

此項目已安裝了 EntityFramework.SQLite 包,所以如今我改成擁有 UseSQLite 擴展方法。

回到圖 1 中的 WarrantyContext 類,您能夠看到熟悉的 OnModelCreating 替代了 DbContext,而且我在其中進行了一些特殊的映射。一樣,我擁有 EntityFramework.AzureTableStorage NuGet 包提供的方法。我要基於我須要的功能選擇包。Azure Table Storage 依賴於鍵值對進行惟一標識以及支持表分區。爲了檢索或存儲數據,知道爲 PartitionKey 和 RowKey 使用什麼值很關鍵,所以 API 提供一個方法 PartitionAndRowKey,該方法能夠用於將屬性映射到適當的鍵。這一律念無異於您能夠如何使用 Fluent API 或數據註釋來指定映射到關係數據庫主鍵的屬性。

因爲此映射功能,我能夠編寫熟悉的 LINQ 查詢來檢索一些數據:

var warranty = _context.Warranties
          .Where(w =>
            w.BikeModelNo == modelNo
            && w.BikeSerialNo == serialNo)
          .SingleOrDefault();

所以您看到的是一個典型的 LINQ 查詢,但它是針對 Azure Table Storage 數據存儲執行的,就像您如今能夠對關係數據庫執行的操做同樣。

這一相同演示還能夠更新保證對象;使用 DbSet.Add 建立和插入新的對象;使用 DbContext.SaveChanges 將全部內容保留到數據存儲,就像如今使用 EF6 執行的操做以及在整個 EF 歷史中執行過的操做同樣。

此外,要考慮的比較有趣的一點是,實體框架如何始終支持映射到關係數據庫的規範功能集,但由數據庫提供程序指定這些功能集如何轉換爲其針對的數據庫。EF7 將包含可被關係數據存儲和非關係數據存儲理解的高級規範功能集。還有專一於關係數據庫的低級功能集,其密封在 EntityFramework.Relational 程序集中。全部關係數據庫提供程序將依賴於這些功能,而且像如今同樣,它們對數據庫交互的特定處理將在其本身的提供程序 API(例如我以前使用的 EntityFramework.SQLite)中進行。您將在衍生 AsRelational 方法(位於關係 API 中)的提供程序中找到擴展方法。它是 DbContext 的擴展方法。

甚至還有一個內存中數據存儲提供程序,可用於在您想要避免數據庫交互(在您測試的邏輯中可能涉及到)時進行單元測試。在這些狀況中,您一般可使用虛設或模擬框架來模擬數據庫交互。

若是您要設置一個測試來執行查詢或更新數據庫,則最好使用一些代碼來實例化數據庫,例如:

using (var context = new BreweryContext()) {
  // Perform some action against the context
}

經過先向測試項目中安裝 entityframework.InMemory 包,爲 InMemoryStore 定義 DbContextOption,而後指定上下文應使用該選項,您能夠輕鬆地切換到內存中存儲。一樣,這多是由於此 API 提供了擴展方法:

var options = new DbContextOptions().UseInMemoryStore();
using (var context = new BreweryContext(options)){
  // Perform some action against the context
}

功能更多,靈活性更高

您能夠在擴展方法提供的靈活性以及經過 OnConfiguring 重載影響實體框架管道的能力方面看到新代碼庫的優點。在整個新代碼庫中有擴展點,這些擴展點不只用於更改 EF7,還可讓您更容易地將本身的邏輯插入到 EF7。

經過新的核心代碼庫,EF 團隊能夠解決一些老問題。例如,我使用的版本已支持批量更新,這是關係數據庫的默認設置。我已使用容許我在 LINQ 查詢中內嵌使用我本身的方法的代碼,而不會收到使人討厭的「實體框架沒法將此方法轉化爲 SQL」的消息。相反,EF 和提供程序能夠解析查詢的哪一個部分將變成 SQL,哪一個部分將在客戶端本地運行。我相信確定會有相應的保護和指導,以免有關該特定功能的一些潛在的性能問題。

團隊能夠爲模型添加長久請求的惟一外鍵功能。他們也在仔細研究如何提供對錶值函數的支持以及處理斷開鏈接數據的更乾淨的方法,這是我多年以來一直關注實體框架的一點。這是斷開鏈接應用程序的常見問題,並不是僅當涉及到實體框架時纔有,而且很難建立在每種方案中均可以一致運行的算法。所以,確實須要一種新方法。

EF7 有不少使人興奮的新功能。我強烈建議您仔細閱讀 ADO.NET 團隊博客 (blogs.msdn.com/adonet) 上的博文。除了我以前連接的博文,Rowan Miller 還深度討論了有關在 EF7 中放棄支持設計器的決定;請參閱《EF7 -「代碼優先」的實際意義》(bit.ly/1sLM3Ur)。請關注該博客以及 GitHub 項目。GitHub (bit.ly/1viwqXu) 上的 wiki 包含指向如何訪問每夜構建版本、如何下載、編譯和調試源代碼以及一些演練和設計會議記錄的連接。團隊但願獲得您的反饋,對可以收到 Pull 請求感到很是高興。

謹慎的決定

對我而言,撰寫有關 EF7 的文章很重要,由於這有助於緩解有關這一巨大變革以及 EF7 中再也不提供對您的應用程序來講不可或缺的一部分現有 EF 功能所帶來的恐懼心理。這些恐懼並不是空穴來風,而團隊和我都會謹慎對待。可是 EF6 並不會離咱們而去,而是將在社區的幫助之下繼續完善,瞭解這一點很重要。若是您要利用這一發展,則要做出一些艱難的選擇。升級大型應用程序並不是易事,您必須仔細權衡相關選擇。或許您能夠分解應用程序,僅從新編寫其中一部分以便從 EF7 中受益。

一樣,當我撰寫此專欄時,EF7 仍處於其早期階段,我不肯定當您閱讀本文時 EF7 進展如何。但您能夠就當前可用的源代碼和 NuGet 包進行探索、實驗並提供反饋。請記住,團隊在完善核心 API 時可能不會一直保持全部提供程序 API(例如 Redis、SQLite 等)最新。根據 bit.ly/1ykagF0 上的博文《EF7 - 優先級、關注和首次發佈》,首版 EF7 將關注與 ASP.NET 5 的兼容性。後續版本將添加更多功能。儘管 EF7 還沒有足夠穩定到能夠用來構建應用程序,可是足以讓您開始提早進行規劃。

Julie Lerman是 Microsoft MVP、.NET 導師和顧問,住在佛蒙特州的山區。您能夠在全球的用戶組和會議中看到她對數據訪問和其餘 .NET 主題的演示。她是《Programming Entity Framework》(2010) 以及「代碼優先」版 (2011) 和 DbContext 版 (2012)(均出自 O’Reilly Media)的做者,博客網址爲thedatafarm.com/blog。經過她的 Twitter(網址爲 twitter.com/julielerman)關注她,並在 juliel.me/PS-Videos 上觀看其 Pluralsight 課程。

衷心感謝如下 Microsoft 技術專家對本文的審閱:Rowan Miller

本文基於 Entity Framework 7 的 Alpha 版本。文中的全部信息均有可能發生變動。

相關文章
相關標籤/搜索