到 2015 2015年 7 月

30 卷數 7

資料點-探索在命令列中使用 Scriptcs 的Entity Framework行為

Julie Lerman

Julie Lerman我花了很多時間試圖教育人們關於Entity Framework關係和斷開連接的資料的行為。EF 並不總是遵循你想像在你的頭,所以我總是有目標,説明你意識到什麼指望,為什麼有些事能以某種方式和如何處理或避免特定行為的規則。

當我想要證明一些東西,給開發商時,我有一些工作流選項。例如,我可以生成集成測試和運行它們一次,在討論每個行為。我不喜歡這條道路是我經常想要調試所以我可以深入到物件,並顯示怎麼回事的被窩。但調試自動的測試可能會很慢的因為所有的原始程式碼Visual Studio需要載入。

我用過的另一個工作流程涉及到建立一個主控台應用程式和調試通過它,在中斷點處停止檢查物件。這也是代碼的不一樣我會喜歡,因為所有要提前去那裡的互動式。

我真的想要能夠做的是鍵入一些代碼,執行它,所以我的觀眾可以看到其效果,然後修改該代碼並執行它再次突出顯示的不同回應。例如,我曾經在以下代碼中,此方法顯示這之間的差異:

using (var context = new AddressContext()) {
  aRegionFromDatabase=context.Regions.FirstOrDefault();
  context.Address.Add(newAddress);
  newAddress.Region=aRegionObjectFromDatabase;
}

這:

using (var context = new AddressContext()) {
  context.Address.Add(newAddress);
  newAddress.Region=aRegionObjectFromDatabase;
}

和這:

newAddress.Region=aRegionObjectFromDatabase;
using (var context = new AddressContext()) {
  context.Address.Add(newAddress);
}

差異,順便說一句,是在第一個示例中,在那裡 EF 檢索該區域在同一上下文實例中的,EF 將瞭解該地區預先存在,並且不會試圖將它重新添加到資料庫。在第二和第三的情況下,EF 規則將推斷此區域,其"父母"位址,比如是新,並將它添加到資料庫。長遠來說,我推薦使用外鍵值的值而不是實例。它是能夠證明因果,真的有用。

當然,我能證明這種效應的測試,但它已經有點費解。還有測試是不是證明的理論,而是驗證代碼的參數。我也不喜歡顯示差異與主控台應用程式,因為你必須在每次更改後運行應用程式的代碼。一個更好的替代方法是使用神奇的 LinqPad 應用程式,和我做過幾次。但現在我個人傾向于第四個方式去我快樂演示的地方,Scriptcs。

Scriptcs (scriptcs.net) 自 2013 年起已經存在並且是完全開源的。它被寫在羅斯林提供使您可以運行在Visual Studio的外部的 C# 代碼的命令列運行時。Scriptcs 還提供了非常羽量級的機制為腳本與 C# 中的任何文字編輯器創建你想要然後從命令列運行這些腳本。

我經常聽到的 Scriptcs,因為它背後的關鍵人物之一是葛籣塊,為誰我有巨大的敬意。但我永遠不會太地仔細打量它直到最近後, 聽力來阻止談插曲 DotNet 光明的未來的 Scriptcs­岩石 (bit.ly/1AA1m4z)。看到這個工具我第一個念頭是,它可以使我來執行權在命令列的互動式示範。並結合 EF,我可以使共用 Scriptcs 合理化與讀者的此資料集中的列。

Scriptcs 小簡介

有很多有用的資源,為 Scriptcs。我根本不是專家,所以我就給你們一些突出和指向你到 scriptcs.net 網站瞭解更多資訊。我還發現了短視頻電路 Sengal 在 bit.ly/1R6mF8s 是一個完美的第一次看看 Scriptcs。

首先,你需要使用 Chocolatey,這意味著你還需要在您的電腦上安裝的 Chocolatey 到開發電腦上安裝 Scriptcs。如果你不已經有 Chocolatey,它是令人難以置信的工具,安裝軟體工具。巧克力用途 NuGet 安裝工具 (和工具或取決於他們的 Api),NuGet 使用的方式相同的包來部署程式集。

Scriptcs 提供了讀取、 評價,戲劇,迴圈 (REPL) 環境,你能想到的像一個運行時環境。你可以使用 Scriptcs 來執行 C# 命令列,命令一個接一個,或執行在文字編輯器中創建的 Scriptcs 指令檔。有一些編輯甚至 ScriptcsIntelliSense擴展。看到 Scriptcs 的行動的最好方法是開始與-逐行執行的。讓我們從這開始。

一旦安裝了 Scriptcs,請導航到任何已保存的代碼要存儲的資料夾,然後執行 scriptcs 命令。這將啟動 REPL 環境並通知您正在運行 Scriptcs。它也會返回 > 提示符。

出現提示時,您可以鍵入任何 C# 的命令發現在幾個最常見的.NET 命名空間。這些程式都可以馬上蝙蝠是典型的.NET 主控台應用程式專案的同一程式集並在預設情況下。圖 1 顯示 Scriptcs 啟動命令提示符,然後執行行的 C# 代碼,以及顯示的結果。

在 Scriptcs REPL 環境從命令列運行 C# 代碼
圖 1 在 Scriptcs REPL 環境從命令列運行 C# 代碼

Scriptcs 擁有少量的讓你做事情像引用程式集 (#r) 或載入現有的指令檔 (#load) 的指令。

另一個很酷的功能是 Scriptcs 讓你輕鬆地安裝 NuGet 套裝程式。你這是在命令提示符下使用 Scriptcs 命令的-安裝參數。例如, 圖 2 顯示在安裝Entity Framework時,會發生什麼。

將 NuGet 套裝程式安裝到 Scriptcs
圖 2 將 NuGet 套裝程式安裝到 Scriptcs

還有更多的包安裝,不過,你可以從我的資料夾的截圖看到後我已經安裝了Entity Framework(見圖 3) — — Scriptcs 已經創建了一個 scriptcs_packages 資料夾以及有關包的內容。

Scriptcs NuGet 套裝程式安裝程式創建的熟悉的包資料夾和設定檔
圖 3 Scriptcs NuGet 套裝程式安裝程式創建的熟悉的包資料夾和設定檔

創建模型和一些常見的設置代碼

我會做我針對我已經建在Visual Studio第一次使用代碼的特定模型的實驗。我有一個程式集與我的班級 — — 猴子,香蕉和國家 — — 和另一個程式集,其中包含猴子­上下文從 EF DbCoNtext 繼承並公開 DbSets 的猴子,香蕉和國家。我驗證了我的模型成立了正確使用Entity Framework電動工具延伸檢視Entity Data Model特徵的Visual Studio(bit.ly/1K8qhkO)。這種方式,我知道我可以依賴我會用在我的測試中命令列編譯的程式集。

為了做這些實驗,還有我需要執行一些常見的設置。我需要參考我自訂的程式集,以及向 EF 大會,我需要具現化新的猴子,國家和 MonkeysCoNtext 物件的代碼。

而不是重複此不斷在命令提示符下安裝的程式,我將為 Scriptcs 創建一個指令檔。指令檔,其中有.csx 延伸,是利用 Scriptcs 的更常見方式。我第一次用 Notepad + + Scriptcs 外掛程式,但是鼓勵嘗試崇高文本 3 (sublimetext.com/3)。做所以允許我使用 Scriptcs 外掛程式,而且 OmniSharp,C# IDE 外掛程式為崇高的文本 3,提供了令人驚異的編碼經驗,考慮到你在一個文字編輯器,支援 OSX 和 Linux,以及 Windows。

OmniSharp 讓您建立 Scriptcs,以及許多其他系統生成。與崇高成立了,我準備好創建一個封裝的共同安裝程式碼,我想要測試出我的模型的.csx 檔。

在文字編輯器中,創建一個新的檔,SetupForMonkeyTests.csx,為引用所需的程式集,添加一些 Scriptcs 命令,然後添加 C# 代碼以創建一些物件,如下所示:

#r "..\DataModel Assemblies\DataModel.dll"
#r "..\DataModel Assemblies\DomainClasses.dll"
#r "..\scriptcs_packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll"
using DomainClasses;
using DataModel;
using System.Data.Entity;
var country=new Country{Id=1,Name="Indonesia"};
var monkey=Monkey.Create("scripting gal", 1);
Database.SetInitializer(new NullDatabaseInitializer<MonkeysContext>());
var ctx=new MonkeysContext();

記住,#r 命令 Scriptcs 命令來添加對所需程式集的引用。經常使用的系統程式集數目預設以便提供開箱體驗類似于你的時候你在Visual Studio中創建一個新的主控台專案的引用。所以我不需要指定那些,但是我需要引用我自訂的程式集,以及由 NuGet 安裝 EntityFramework 程式集。剩下的就只是 C# 代碼。請注意,我不定義類在這裡。這是一個腳本,因此 Scriptcs 將唯讀取和執行一行一行的無論是在檔中。與 OmniSharp 崇高文本組合,我甚至可以創造出以確保我的語法正確。

處理這個檔在手,就會返回到命令列並查閱使用我的模型和類一些 EF 行為。

給我命令列的實驗與 EF

回到在命令提示符下,我將開始 Scriptcs REPL 只是 scriptcs 命令和任何參數。

一旦 REPL 處於活動狀態,我想要它做的第一件事是載入.csx 檔。然後我會用: 引用和: vars 命令正確驗證 REPL 跑.csx 檔。(Scriptcs 有大量的 REPL 命令以冒號開頭。您可以通過鍵入看到清單: 説明.)圖 4 顯示我的會話為止; 你可以看到所有的引用,以及我所創建的物件的 Api。

圖 4 開始 Scriptcs; 載入一個.csx 的檔; 檢查引用和現有變數

D:\ScriptCS Demo>scriptcs
scriptcs (ctrl-c to exit or :help for help)
> #load "SetupForMonkeyTests.csx"
> :references
[
  "System",
  "System.Core",
  "System.Data",
  "System.Data.DataSetExtensions",
  "System.Xml",
  "System.Xml.Linq",
  "System.Net.Http",
  "C:\\Chocolatey\\lib\\scriptcs.0.14.1\\tools\\ScriptCs.Core.dll",
  "C:\\Chocolatey\\lib\\scriptcs.0.14.1\\tools\\ScriptCs.Contracts.dll",
  "D:\\ScriptCS Demo\\scriptcs_packages\\EntityFramework.6.1.3\\lib\\   
    net45\\EntityFramework.dll",
  "D:\\ScriptCS Demo\\scriptcs_packages\\EntityFramework.6.1.3\\lib\\
    net45\\EntityFramework.SqlServer.dll",
  "D:\\ScriptCS Demo\\DataModel Assemblies\\DataModel.dll",
  "D:\\ScriptCS Demo\\DataModel Assemblies\\DomainClasses.dll"
]
> :vars
[
  "DomainClasses.Country country = DomainClasses.Country",
  "DomainClasses.Monkey monkey = DomainClasses.Monkey",
  "DataModel.MonkeysContext ctx = DataModel.MonkeysContext"
]
>

我也可以通過只在提示符下鍵入變數名稱檢查物件。在這裡,例如,是我的猴子物件:

> monkey
{
  "Id": 0,
  "Name": "scripting gal",
  "Bananas": [],
  "CountryOfOrigin": null,
  "CountryOfOriginId": 1,
  "State": 1
}
>

現在準備開始探索一些 EF 行為。回到我前面的示例,我查一下 EF 如何回應附加該國物件對猴子當上下文是或不跟蹤猴子和當它是或不跟蹤該國。

在第一次測試中,我會將預先存在的國家附加到猴子的 CountryOfOrigin 屬性,然後將猴子添加到上下文。最後,我將使用 DbCoNtext.Entry()。狀態屬性檢查 EF 是如何理解的物件的狀態。它有道理猴子添加,但是請注意,EF 認為該國添加,以及。這是 EF 如何對待圖。因為我使用 Add 方法在圖 (猴子) 的根上,EF 將一切圖中標記為增加。當我調用 SaveChanges 時,該國將會插入到資料庫表,並作為你可以看到在圖 5,我會有印尼的兩行。國家已經有一個索引碼 (Id) 的事實將被忽略。

使用 Scriptcs 來立即看到 EF 如何回應 DbSet.Add Method 與圖
圖 5 使用 Scriptcs 來立即看到 EF 如何回應 DbSet.Add Method 與圖

接下來,我會使用: 重置命令再次清除 REPL 歷史,然後 #load.csx。在那之後,我會嘗試新的工作流 — — 將猴子添加到上下文,然後附加到已被跟蹤的猴子的國家。請注意,我不包括所有回應在下面的清單:

> :reset
> #load "SetupForMonkeyTests.csx"
> ctx.Monkeys.Add(monkey);
{...response...}
> monkey.CountryOfOrigin=country;
{...response...}
> ctx.Entry(monkey).State.ToString()
Added
> ctx.Entry(country).State.ToString()
Added

再次,上下文分配附加狀態到國家物件因為我將它附增值的另一個實體。

如果你真的想要使用的導覽屬性,將給您成功的模式是以確保上下文已經意識到中國。這可能是因為你已經檢索該國在同一個上下文實例,使用查詢結果將以未更改的狀態分配給物件的上下文,或者你自己手動分配的不變的狀態。

圖 6 舉例說明後者,它允許我這樣做沒有使用資料庫測試。

圖 6 手動分配的不變的狀態

> :reset
> #load "SetupForMonkeyTests.csx"
> ctx.Entry(country).State=EntityState.Unchanged;
2
> ctx.Monkeys.Add(monkey);
{...response...}
> monkey.CountryOfOrigin=country;
{...response...}
> ctx.Entry(monkey).State.ToString()
Added
> ctx.Entry(country).State.ToString()
Unchanged
>

上下文已在跟蹤該國,因為該國物件的狀態不會改變。EF 只更改相關物件的狀態時它的狀態是未知。

ScriptcsWill會不僅僅是一個非常棒的教學工具對我來說

就個人而言,我更願意避免周圍這些混淆規則完全和簡單地設置外鍵值 (聞名中外­OriginId = 國家。Id),而不必附加使用導覽屬性的引用。事實上,與這種模式,我可以更仔細地審視 CountryOfOrigin 導覽屬性然後考慮是否我甚至想那裡。展示的所有變化和 EF 如何回應每個方案都是很多人見過這一令人大開眼界課。

當試圖向開發者展示這些行為,我喜歡我可以得到從互動式視窗的即時和明顯反應。雖然 LINQPad 還可以説明我實現這一目標,但我喜歡命令列交互。更重要的是,使用這些實驗作為一種方式把自己介紹給 Scriptcs,我現在更加意識到它帶來超越時,只執行命令列測試,API 的巨大價值。


Julie Lerman 是 Microsoft MVP,.NET 的導師和顧問住在佛蒙特州的山。你可以找到她提出關於資料訪問和其他.NET 主題使用者組和世界各地的會議。她的博客 thedatafarm.com/blog ,是作者的"程式設計Entity Framework"(2010 年),以及代碼第一版 (2011 年) 和 DbCoNtext 版 (2012 年),所有從 O'Reilly 媒體。跟著她在 Twitter 上 twitter.com/julielerman ,看看她的 Pluralsight 課程 juliel.me/PS-視頻

感謝以下的微軟技術專家對本文的審閱:賈斯汀 Rusbatch