Entity Framework 學習

Entity Framework 學習初級篇1--EF基本概況... 2html

Entity Framework 學習初級篇2--ObjectContext、ObjectQuery、ObjectStateEntry、ObjectStateManager類的介紹... 7sql

Entity Framework 學習初級篇3-- LINQ TOEntities. 10數據庫

Entity Framework 學習初級篇4--EntitySQL. 17編程

Entity Framework 學習初級篇5--ObjectQuery查詢及方法... 23c#

Entity Framework 學習初級篇6--EntityClient. 31緩存

Entity Framework 學習初級篇7--基本操做:增長、更新、刪除、事務... 37服務器

Entity Framework 學習中級篇1—EF支持複雜類型的實現... 41閉包

Entity Framework 學習中級篇2—存儲過程(上). 47架構

Entity Framework 學習中級篇3—存儲過程(中). 54併發

Entity Framework 學習中級篇4—存儲過程(下). 61

Entity Framework 學習中級篇5—使EF支持Oracle9i67

Entity Framework 學習高級篇1—改善EF代碼的方法(上)... 75

Entity Framework 學習高級篇2—改善EF代碼的方法(下)... 81

Entity Framework 學習結束語... 84

 

 

 

Entity Framework 學習初級篇1--EF基本概況

最近在學習研究微軟的EF,經過這時間的學習研究,感受這個EF目前來講還不是很完善,半成品。不過,聽說在.Net4.0中,微軟將推薦使用此框架,並會有所改善。並且,如今基本上全部數據庫均提供了對EF的支持。所以,爲之後作技術準備能夠學習研究如下。可是,我我的以爲就目前來講,在實際項目慎用此框架。

下面簡單的介紹如下這個EF。

在.Net Framework SP1微軟包含一個實體框架(Entity Framework),此框架能夠理解成微軟的一個ORM產品。用於支持開發人員經過對概念性應用程序模型編程(而不是直接對關係存儲架構編程)來建立數據訪問應用程序。目標是下降面向數據的應用程序所需的代碼量並減輕維護工做。Entity Framework 應用程序有如下優勢:

·                應用程序能夠經過更加以應用程序爲中心的概念性模型(包括具備繼承性、複雜成員和關係的類型)來工做。

·                應用程序再也不對特定的數據引擎或存儲架構具備硬編碼依賴性。

·                能夠在不更改應用程序代碼的狀況下更改概念性模型與特定於存儲的架構之間的映射。

·                開發人員可使用可映射到各類存儲架構(可能在不一樣的數據庫管理系統中實現)的一致的應用程序對象模型。

·                多個概念性模型能夠映射到同一個存儲架構。

·                語言集成查詢支持可爲查詢提供針對概念性模型的編譯時語法驗證。

實體框架Entity Framework 是 ADO.NET 中的一組支持開發面向數據的軟件應用程序的技術。在EF中的實體數據模型(EDM)由如下三種模型和具備相應文件擴展名的映射文件進行定義。

·                概念架構定義語言文件 (.csdl) -- 定義概念模型。

·                存儲架構定義語言文件 (.ssdl) -- 定義存儲模型(又稱邏輯模型)。

·                映射規範語言文件 (.msl) -- 定義存儲模型與概念模型之間的映射。

實體框架 使用這些基於 XML 的模型和映射文件將對概念模型中的實體和關係的建立、讀取、更新和刪除操做轉換爲數據源中的等效操做。EDM 甚至支持將概念模型中的實體映射到數據源中的存儲過程。它提供如下方式用於查詢EDM 並返回對象:

·                LINQ to Entities-- 提供語言集成查詢 (LINQ) 支持用於查詢在概念模型中定義的實體類型。

·                Entity SQL -- 與存儲無關的 SQL 方言,直接使用概念模型中的實體並支持諸如繼承和關係等 EDM 功能。

·                查詢生成器方法 --可使用 LINQ 風格的查詢方法構造 Entity SQL 查詢。

下圖演示用於訪問數據的實體框架體系結構:

 

 

 

 

下面,來學習EF的基本使用方法。軟件環境::

·                Visual Studio 2008 +SP1

·                SQL Server2005/2008

首先,創建一個名爲「EFProject」的解決方案,而後添加一個名爲「EFModel」的類庫項目。以下圖所示。

 

 

接着,在EFModel項目中,添加「ADO.NETEntity Data Model」項目,以下圖所示:

 

 

名稱取爲「NorthWindEF.edmx」,而後點擊「添加」。而後,在後面的步驟中,數據庫選擇「NorthWind」後,在選擇影射對象是,把表、試圖、存儲過程所有都選上,其餘的均保存默認的便可。最終生成的結果以下圖所示。

 

 

 

好了,數據模型生成完畢。

最後,談談我認爲的一些缺點:

·                Edmx包含了全部對象的csdl,ssdl,msl文件,過於龐大,若是要手動修改這個文件,一不當心,眼睛看花了,就改錯了。(和數據集同樣的毛病)。

·                目前EF支持表、試圖、存儲過程,其餘的對象不支持,並且對使用存儲過程有不少限制(目前有EFExtension提供了更多對象的支持)。

·                除了MSSQL Server可直接提供這種可視化的設計界面外,其餘的數據庫目前尚未提供可視化設計界面(但能夠本身來實現,後面介紹)。

·                性能問題。(網上看到有說比ADO.Net慢,又有人說比ADO.net快的,具體狀況我還沒測試過, 但我以爲像這個些類型的框架,性能確定是比上原生態的ADO.net慢)

好了,接下來,學習如下簡單的各類操做。

 

Entity Framework 學習初級篇2--ObjectContextObjectQueryObjectStateEntryObjectStateManager類的介紹

本節,簡單的介紹EF中的ObjectContext、ObjectQuery、ObjectStateEntryObjectStateManager這個幾個比較重要的類,它們都位於System.Data.Entity.dll下的System.Data.Objects命名空間下。在後續的章節中,咱們常常會用到它們的某些方法,以便完成咱們的某些操做或目的。本節,簡單的說明一下之後咱們可能會用到的各個類的方法,以方便咱們後續的學習。

ObjectContext封裝 .NET Framework 和數據庫之間的鏈接。此類用做「建立」、「讀取」、「更新」和「刪除」操做的網關。

ObjectContext 類爲主類,用於與做爲對象(這些對象爲 EDM 中定義的實體類型的實例)的數據進行交互。

ObjectContext 類的實例封裝如下內容:

l          到數據庫的鏈接,以 EntityConnection 對象的形式封裝。

l          描述該模型的元數據,以 MetadataWorkspace 對象的形式封裝。

l          用於管理緩存中持久保存的對象的 ObjectStateManager對象。

ObjectContext類的成員方法以說明以下所示:

l          AcceptAllChanges()

接受全部對該實體對象的更改

l          AddObject(string,object)

將實體對象添加到制定的實體容器中

l          ApplyPropertyChanges(string,object)

將以指派的實體對象屬性的更改應用到容器中對應的原對象。

l          Attach(System.Data.Objects.DataClasses.IEntityWithKeyentity)

將帶主鍵的實體對象附加到默認的容器中

l          Attach(string,object)

將實體對象附加到指定的實體容器中

l          CreateEntityKey(string,object)

給指定的實體對象建立實體主鍵或若是已存在實體主鍵,則直接返回該實體的主鍵

l          CreateQuery<T>(string,paramsObjectParameter[])

從給定的查詢字符串建立ObjectQuery對象。

l          DeleteObject(object)

刪除指定的實體對象

l          Detach(object)

移除指定的實體對象

l          ExecuteFunction<TElement>(string,paramsObjectParameter[])

對默認容器執行給定的函數。

l          GetObjectByKey(System.Data.EntityKeykey)

經過主鍵KEY從 ObjectStateManager中檢索對象(若是存在);不然從存儲區中檢索。

l          Refresh(System.Data.Objects.RefreshMode refreshMode,object entity)

按指定持久更新模式,使用指定實體的存儲區數據更新ObjectStateManager。。

l          Refresh(System.Data.Objects.RefreshModerefreshMode, System.Collections.IEnumerable collection)

按指定持久處理模式,使用指定實體集的存儲區數據更新ObjectStateManager

l          SaveChanges(bool)

將全部更新持久保存到存儲區中。參數是客戶端事務支持所需的參數。參數爲true則在更新後自動將更改應用到ObjectStateManager中的實體。若是爲false,則在更新後還須要調用AcceptAllChanges()以便更新ObjectStateManager中的實體。

l          SaveChanges()

將全部更新持久保存到存儲區中

l          TryGetObjectByKey(System.Data.EntityKey,outobject)

嘗試從指定實體主鍵返回該實體

以上各個方法的具體用法,將在後面介紹。

接着,再看看有用的類ObjectQuery。

ObjectQuery有個有用的方法ToTraceString(),這個方法用於追蹤所執行的SQL語句,經過此方法咱們能夠獲取所執行的SQL語句,以便咱們查看、分析具體執行的SQL語句。(相似Nhibernate配置文件中的showsql節)

再瞭解一下ObjectStateEntry

ObjectStateEntry維護實體實例或關係實例的狀態(已添加、已刪除、已分離、已修改或未更改)、鍵值和原始值。還管理已修改屬性的列表。其包含一下方法:

l          AcceptChanges

接受當前值做爲原始值,並將實體標記爲 Unchanged()。

l           Delete

將實體標記爲 Deleted()。若是實體處於 Added()()() 狀態,它將爲 Detached()。

l          GetModifiedProperties

返回標記爲 Modified()的屬性名稱。

l           SetModified

將狀態設置爲 Modified()。

l           SetModifiedProperty

將指定的屬性標記爲 Modified()。

接着,再看看ObjectStateManager

ObjectStateManager用於維護對象映射、對象狀態/標識管理以及實體實例或關係實例的持久性。

l          GetObjectStateEntries

獲取給定EntityState的ObjectStateEntry集合。

l          GetObjectStateEntry

獲取給定的 EntityKey 對應的 ObjectStateEntry

如今,幾個重要的類簡單介紹完畢。後面,咱們將具體學習它們的使用。

 

Entity Framework 學習初級篇3-- LINQ TO Entities

 

LINQ 技術(即 LINQ to Entities)使開發人員可以經過使用 LINQ 表達式和 LINQ 標準查詢運算符,直接從開發環境中針對 實體框架對象上下文建立靈活的強類型查詢。LINQ to Entities 查詢使用對象服務基礎結構。ObjectContext 類是做爲 CLR 對象與 實體數據模型 進行交互的主要類。開發人員經過 ObjectContext 構造泛型 ObjectQuery 實例。ObjectQuery 泛型類表示一個查詢,此查詢返回一個由類型化實體組成的實例或集合。返回的實體對象可供更新並位於對象上下文中。如下是建立和執行  LINQ to Entities 查詢的過程:

1.     從 ObjectContext 構造 ObjectQuery 實例。

2.     經過使用 ObjectQuery 實例在 C# 或 Visual Basic 中編寫 LINQ to Entities 查詢。

3.     將 LINQ 標準查詢運算符和表達式將轉換爲命令目錄樹。

4.     對數據源執行命令目錄樹表示形式的查詢。執行過程當中在數據源上引起的任何異常都將直接向上傳遞到客戶端。

5.     將查詢結果返回到客戶端。

1、Linq To Entities簡單查詢

下面將介紹簡單的Linq To Entities查詢,相關的查詢語法可使用基於表達式或基於方法的語法。本節使用的TestDriver.Net配合Nunit2.4進行測試。

1,  投影

代碼以下:

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Linq;

usingSystem.Data.Objects;

usingNUnit.Framework;

namespaceNorthWindModel

{

   [TestFixture]

   public classTestEFModel

   {

        [Test]

        public void Select()

        {

            using(var edm = new NorthwindEntities())

            {

                //基於表達式的查詢語法

                ObjectQuery<Customers> customers = edm.Customers;

                IQueryable<Customers> cust1 = fromc in customers

                                              select c;

                Assert.Greater(cust1.Count(),0);

               //使用ObjectQuery類的ToTraceString()方法顯示查詢SQL語句

                Console.WriteLine(customers.ToTraceString());

 

 

            }

        }

   }

}

輸出:

SELECT

[Extent1].[CustomerID] AS[CustomerID],

[Extent1].[CompanyName] AS[CompanyName],

[Extent1].[ContactName] AS[ContactName],

[Extent1].[ContactTitle] AS[ContactTitle],

[Extent1].[Address] AS [Address],

[Extent1].[City] AS [City],

[Extent1].[Region] AS [Region],

[Extent1].[PostalCode] AS[PostalCode],

[Extent1].[Country] AS [Country],

[Extent1].[Phone] AS [Phone],

[Extent1].[Fax] AS [Fax]

FROM [dbo].[Customers] AS [Extent1]

 

1 passed, 0 failed, 0 skipped, took 11.00 seconds (NUnit2.4).

在上面的輸出內容中,能夠看到使用了ToTraceString()方法來輸出具體的SQL語句。同時Nunit也輸出相關的測試狀況,請注意查詢所花費的時間,以便咱們進行查詢速度的分析比較。

2,  條件限制

using(var edm = new NorthwindEntities())

{

   //基於表達式的查詢語法

   ObjectQuery<Customers> customers = edm.Customers;

   IQueryable<Customers> cust1 = from c in customers

                                  where c.CustomerID == "ALFKI"

                                  select c;

  

   Assert.AreEqual(cust1.Count(), 1);

   foreach (varc in cust1)

        Console.WriteLine("CustomerID={0}", c.CustomerID);

              

   //基於方法的查詢語法

   var cust2 = edm.Customers.Where(c =>c.CustomerID == "ALFKI");

   Assert.AreEqual(cust2.Count(), 1);

   foreach (varc in cust2)

          Console.WriteLine("CustomerID={0}", c.CustomerID);

 

}

3,  排序和分頁

在使用Skip和Take方法實現分頁時,必須先對數據進行排序,不然將會拋異常。

using(var edm = new NorthwindEntities())

 {

//基於表達式的查詢語法

         ObjectQuery<Customers> customers = edm.Customers;

         IQueryable<Customers> cust10 = (fromc in customers

                                         orderby c.CustomerID

                                         select c).Skip(0).Take(10);

 

          Assert.AreEqual(cust10.Count(),10);

          foreach(var c incust10)

              Console.WriteLine("CustomerID={0}", c.CustomerID);

   

//基於方法的查詢語法

    var cust =edm.Customers.OrderBy(c => c.CustomerID).Skip(0).Take(10);

    Assert.AreEqual(cust.Count(),10);

    foreach (var c in cust)

        Console.WriteLine("CustomerID={0}", c.CustomerID);

 }

4,  聚合

可以使用的聚合運算符有Average、Count、Max、Min 和 Sum。

using (varedm = new NorthwindEntities())

   {

       varmaxuprice = edm.Products.Max(p => p.UnitPrice);

       Console.WriteLine(maxuprice.Value);

 }

5,  鏈接

能夠的鏈接有Join 和 GroupJoin 方法。GroupJoin組聯接等效於左外部聯接,它返回第一個(左側)數據源的每一個元素(即便其餘數據源中沒有關聯元素)。

using(var edm = new NorthwindEntities())

    {

       var query = from d in edm.Order_Details

                   joinorder in edm.Orders

                   ond.OrderID equals order.OrderID

                   selectnew

                    {

                        OrderId =order.OrderID,

                        ProductId = d.ProductID,

                        UnitPrice = d.UnitPrice

                     };

        foreach(var q inquery)

         Console.WriteLine("{0},{1},{2}",q.OrderId,q.ProductId,q.UnitPrice);

}

其餘一些方法等就很少說了,和Linq to SQL基本上是同樣的。

2、LINQ to Entities 查詢注意事項

l          排序信息丟失

若是在排序操做以後執行了任何其餘操做,則不能保證這些附加操做中會保留排序結果。這些操做包括 Select 和 Where 等。另外,採用表達式做爲輸入參數的 FirstFirstOrDefault方法不保留順序。

以下代碼:並不能達到反序排序的效果

using (varedm = new NorthwindEntities())

{

     IQueryable<Customers>cc = edm.Customers.OrderByDescending(c => c.CustomerID).Where(c =>c.Region != null).Select(c => c);

     foreach (var c in cc)

          Console.WriteLine(c.CustomerID);

}

l          不支持無符號整數

因爲 實體框架不支持無符號整數,所以不支持在 LINQ to Entities 查詢中指定無符號整數類型。若是指定無符號整數,則在查詢表達式轉換過程當中會引起NotSupportedException異常,並顯示沒法建立類型爲「結束類型」的常量值。此上下文僅支持基元類型(「例如  Int3二、String 和 Guid」)。

以下將會報異常的代碼:

using(var edm = new NorthwindEntities())

 {

      uint id = UInt32.Parse("123");

      IQueryable<string>produt = from p inedm.Products

                                  where p.UnitPrice == id

                                  select p.ProductName;

      foreach (string name in produt)

            Console.WriteLine(name);

}

上面的代碼中,因爲id是uint而不是Int32,String,Guid的標量類型,因此在執行到where p.UnitPrice ==id這個地方時,會報異常。

l          不支持引用非標量閉包

不支持在查詢中引用非標量閉包(如實體)。在執行這類查詢時,會引起 NotSupportedException 異常,並顯示消息「沒法建立類型爲「結束類型」的常量值。此上下文中僅支持基元類型(‘如 Int3二、String 和 Guid’)

以下將會報異常的代碼:

using (var edm = new NorthwindEntities())

 {

        Customers customer = edm.Customers.FirstOrDefault();

        IQueryable<string>cc = from c inedm.Customers

                                where c == customer

                                select c.ContactName;

         foreach (string name in cc)

               Console.WriteLine(name);

}

上面的代碼中,因爲customer是引用類型而不是Int32,String,Guid的標量類型,因此在執行到where c==customer這個地方時,會報異常。

好,本節介紹完畢。後面將繼續學習EF.

 

Entity Framework 學習初級篇4--Entity SQL

Entity SQL 是 ADO.NET 實體框架 提供的 SQL 類語言,用於支持 實體數據模型 (EDM)。Entity SQL 可用於對象查詢和使用 EntityClient 提供程序執行的查詢。

l          關鍵字

Value關鍵字

 ESQL 提供了 SELECT VALUE 子句以跳過隱式行構造。SELECT VALUE 子句中只能指定一項。在使用這樣的子句時,將不會對 SELECT 子句中的項構造行包裝器,而且可生成所要形狀的集合,例如:SELECTVALUE it FROM NorthwindEntities.Customers as it

it關鍵字

it 出如今 ESQL 中, 查詢對象的別名默認值"it" 改爲其餘字符串,例如:

"SELECT VALUE it FROM NorthwindEntities.Customersas it "

l          註釋:

Entity SQL 查詢能夠包含註釋。註釋行以兩個短劃線 (--) 開頭。

"SELECT VALUE it FROMNorthwindEntities.Customers as it  --this a comment "

l          Select查詢

例如:

SELECT VALUE it FROMNorthwindEntities.Customers as it

l          參數

參數是在esql以外定義的變量,每一個參數都有名稱和類型,參數名稱在查詢表達式中定義,並以@符號做爲前綴。例如:

Select VALUE c fromNorthwindEntities.Customers as c where c.CustomerID=@customerID

l          聚合

Enity SQL不支持 * ,因此esql不支持count(*),而是使用count(0),例如:

Select count(0) fromNorthwindEntities.Customers

l          分頁SKIP/LIMIT

能夠經過在 ORDER BY 子句中使用 SKIP 和 LIMIT 子子句執行物理分頁。若要以肯定的方式執行物理分頁,應使用 SKIP 和 LIMIT。若是您只是但願以非肯定的方式限制結果中的行數,則應使用 TOP。TOP 和 SKIP/LIMIT 是互斥的

使用SKIP/LIMIT分頁,esql代碼以下:

Select value c fromNorthwindEntities.Customers as c order by c.CustomerID skip 0 limit 10

l          TOP

SELECT 子句能夠在可選的 ALL/DISTINCT 修飾符以後具備可選的 TOP 子子句。TOP 子子句指定查詢結果中將只返回第一組行。esql代碼以下:

Select top(10) c.CustomerID fromNorthwindEntities.Customers as c order by c.CustomerID

l          NULL處理

Null 文本與 Entity SQL 類型系統中的任何類型都兼容,可使用cast進行類型轉換,例如:

select cast(c.region as string) fromNorthwindEntities.Customers as c order by c.CustomerID limit 10

其中, Nvarchar等能夠成string,數字類型能夠轉成int32,其餘的類型轉換相似。若是沒法完成轉換,則將報異常。還有能夠處理的方法有treat。

l          標識符

Entity SQL 提供兩種標識符:簡單標識符和帶引號的標識符

簡單標識符:Entity SQL 中的簡單標識符是字母數字和下劃線字符的序列。標識符的第一個字符必須是字母字符(a-z 或 A-Z)。

帶引號的標識符:帶引號的標識符是括在方括號 ([]) 中的任何字符序列。帶中文的部分,請使用方括號包括起來,不然會報以下異常信息:「簡單標識符「中文」只能包含基本拉丁字符。若要使用UNICODE 字符,請使用轉義標識符」

正確的代碼以下:

Select c.CustomerID as [中文字符] from NorthwindEntities.Customers as c order by c.CustomerIDskip 0 limit 10

l          ROW

Esql可以使用row來構建匿名的結構類型的紀錄。例如:

SELECT VALUE row(p.ProductID asProductID,p.ProductName as ProductName) FROM NorthwindEntities.Products as porder by p.ProductID LIMIT 10

l          Key

提取引用或實體表達式的鍵。以下esql語句,直接返回Customer表的主鍵:

string esql = "SELECT value key(c)FROM NorthwindEntities.Customers as c order by c.CustomerID LIMIT 10"

l          CreateRef/ref/deref

CreateRef建立對實體集中的實體的引用。

ref返回對實體實例的引用,以後就能夠看成實體來訪問其屬性,esql語句以下:

SELECT ref(c).CustomerID FROMNorthwindEntities.Customers as c order by c.CustomerID LIMIT 10

deref運算符取消引用一個引用值,並生成該取消引用的結果。

l          CASE語句:

 string esql = "using SqlServer;select case whenlen(trim(c.CustomerID))==0 then true else false end  from NorthwindEntities.Customers as c orderby c.CustomerID limit 10";

l          運算符

Esql支持的運算符有:加+、減-、乘*、除/、取模%、-負號。Esql語句以下:

select 100/2 as OP fromNorthwindEntities.Customers as c order by c.CustomerID limit 10

l          比較運算符

Esql支持的比較運算符有:=,>,>=,IS [NOT]NULL,<,[NOT] BETWEEN,!=,<>,[NOT] LIKE。Esql語句以下:

select value p fromNorthwindEntities.Products as p where p.UnitPrice > 20 order by p.ProductIDlimit 10

l          邏輯運算符

Esql支持的邏輯運算符有:and(&&),not(!),or(||)。Esql語句以下:

select value p fromNorthwindEntities.Products as p where p.UnitPrice > 20 and p.UnitPrice<100order by p.ProductID limit 10

select value p fromNorthwindEntities.Products as p where p.UnitPrice > 20 &&p.UnitPrice<100 order by p.ProductID limit 10

l          字符串鏈接運算符。

加號 (+) 是 Entity SQL 中可將字符串串聯起來的惟一運算符。Esql語句以下:

select c.CustomerID + c.ContactName fromNorthwindEntities.Customers as c  orderby c.CustomerID limit 10

l          嵌套查詢

在 Entity SQL 中,嵌套查詢必須括在括號中,將不保留嵌套查詢的順序

select c1.CustomerID from( select valuec from NorthwindEntities.Customers as c order by c.CustomerID limit 10) as c1

l          日期時間函數

Esql提供的日期時間函數有:CurrentDateTime()獲取當前服務器的日期時間,還有month,day,year,second,Minute ,Hour等。例如:

select CurrentDateTime()  from NorthwindEntities.Customers as c  order by c.CustomerID limit 10

l          字符串函數

Esql提供的字符串函數有:Concat,IndexOf,Left,Length,Ltrim,Replace,Reverse,Rtrim,SubString,Trim,ToLower,ToUpper.例如:

select Reverse(p.ProductName) asProductName from NorthwindEntities.Products as p  order by p.ProductID limit 10

l          GUID

Esql提供newguid()函數,產生一個新的Guid。例如:

select newguid()  from NorthwindEntities.Customers as c  order by c.CustomerID limit 10

l          數學函數:

Abs,Ceiling,Floor,Round

l          統計函數:

Avg,BigCount,Count,Max,Min,StDev,Sum

l          位計算函數

若是提供 Null 輸入,則這些函數返回 Null。這些函數的返回類型與參數類型相同。若是函數採用多個參數,則這些參數必須具備相同的類型。若要對不一樣類型執行位運算,則須要顯式強制轉換爲相同類型.

BitWiseAnd,BitWiseNot,BitWiseOr,BitWiseXor

l          命名空間

Entity SQL 引入命名空間以免全局標識符(如類型名稱、實體集、函數等)出現名稱衝突。Entity SQL 中的命名空間支持與 .NET Framework 中的命名空間支持相似。

Entity SQL 提供兩種形式的 USING 子句:限定命名空間(其中,提供較短的別名以表示命名空間)和非限定命名空間,以下例所示:

USING System.Data;

USING tsql =System.Data;

例如:

string esql = "using System; select cast(p.UnitPrice asInt32)  from NorthwindEntities.Productsas p  order by p.ProductID limit 10";

string esql = "using System;using SqlServer; select(cast(p.UnitPrice as Int32)),SqlServer.ltrim(p.ProductName) as nameLen fromNorthwindEntities.Products as p  order byp.ProductID limit 10 ";

 

最後,簡單說一下Esql與T-Sql的某些差別:

l          Entity SQL 中的全部列引用都必須用表別名限定.

l          Esql不支持Any,all限定運算符以及*運算

l          Entity SQL 當前未提供對 DML 語句(insert、update、delete)的支持。

l           Entity SQL 的當前版本未提供對 DDL 的支持。

 

Entity Framework 學習初級篇5--ObjectQuery查詢及方法

 

ObjectQuery 類支持對 實體數據模型 (EDM) 執行 LINQ to Entities 和 Entity SQL 查詢。ObjectQuery 還實現了一組查詢生成器方法,這些方法可用於按順序構造等效於 Entity SQL 的查詢命令。下面是ObjectQuery 的查詢生成器方法以及等效的 Entity SQL 語句:

Distinct,Except,GroupBy,Intersect,OfType,OrderBy,Select,SelectValue,Skip,Top,Union,UnionAll,Where

每一個查詢生成器方法返回 ObjectQuery 的一個新實例。使用這些方法能夠構造查詢,而查詢的結果集基於前面 ObjectQuery 實例序列的操做。下面來看具體的代碼片段:

l          Execute方法:

using(var edm = new NorthwindEntities())

        {

                stringesql = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";

                ObjectQuery<Customers> query = edm.CreateQuery<Customers>(esql);

                ObjectResult<Customers> results = query.Execute(MergeOption.NoTracking);

                Assert.AreEqual(results.Count(),10);

                foreach(Customers c inquery)

                    Console.WriteLine(c.CustomerID);

            }

其中須要說明的是: MergeOption這個枚舉類型的參數項,MergeOption有四種值分別是:

l          AppendOnly: 只追加新實體,不修改之前獲取的現有實體。這是默認行爲。

l          OverwriteChanges: 將 ObjectStateEntry 中的當前值替換爲存儲區中的值。這將使用服務器上的數據重寫在本地所作的更改。

l          PreserveChanges: 將替換原始值,而不修改當前值。這對於在發生開放式併發異常以後強制成功保存本地值很是有用。

l          NoTracking: 將不修改ObjectStateManager,不會獲取與其餘對象相關聯的關係,能夠改善性能。

l          GetResultType方法:返回查詢結果的類型信息.例如:

using(var edm = new NorthwindEntities())

            {

                stringesql = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";

                ObjectQuery<Customers> query = edm.CreateQuery<Customers>(esql);

                Console.WriteLine(query.GetResultType().ToString());

                //輸出結果爲:

                //NorthWindModel.Customers

            }

l          ToTraceString方法:獲取當前執行的SQL語句。

l          Where

實例代碼以下:

using(var edm = new NorthwindEntities())

     {

                stringesql = "select value c fromNorthwindEntities.Customers as c ";

                ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql);

               //使用ObjectParameter的寫法               

                query1 = query1.Where("it.CustomerId=@customerid");

                query1.Parameters.Add(new ObjectParameter("customerid", "ALFKI"));

               //也能夠這樣寫

                //ObjectQuery<Customers>query2 = edm.Customers.Where("it.CustomerID='ALFKI'");

                foreach(var c inquery1)

                    Console.WriteLine(c.CustomerID);

                //顯示查詢執行的SQL語句

                Console.WriteLine(query1.ToTraceString());

              

            }

l          First/ FirstOrDefault

實例代碼以下:

using(var edm = new NorthwindEntities())

     {

                stringesql = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";

                ObjectQuery<Customers> query = edm.CreateQuery<Customers>(esql);

                Customersc1 = query.First();

                Customersc2 = query.FirstOrDefault();

                Console.WriteLine(c1.CustomerID);

                Assert.IsNotNull(c2);

                Console.WriteLine(c2.CustomerID);

      }

l          Distinct

實例代碼以下:

using (varedm = new NorthwindEntities())

    {

                stringesql = "select value c.City fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";

                ObjectQuery<string> query = edm.CreateQuery<string>(esql);

                query = query.Distinct();

                foreach(string c inquery)

                {

                    Console.WriteLine("City {0}", c);

                }

     }

l          Except:返回兩個查詢的差集。實例代碼以下:

using (varedm = new NorthwindEntities())

     {

                stringesql1 = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";

                ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);

                stringesql2 = "select value c fromNorthwindEntities.Customers as c where c.Country='UK' order by c.CustomerID limit10";

                ObjectQuery<Customers> query2 = edm.CreateQuery<Customers>(esql2);

                query1 = query1.Except(query2);

                foreach (Customers c inquery1)

                {

                    Console.WriteLine(c.Country);

                    //輸出:UK

                }

        }

l          Intersect:返回兩個查詢的交集。實例代碼以下:

using (varedm = new NorthwindEntities())

     {

                stringesql1 = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";

                ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);

                stringesql2 = "select value c from NorthwindEntities.Customersas c where c.Country='UK'order by c.CustomerID limit 10";

                ObjectQuery<Customers> query2 = edm.CreateQuery<Customers>(esql2);

                query1 =query1.Intersect(query2);

                foreach(Customers c inquery1)

                {

                    Console.WriteLine(c.Country);

                }

      }

l          Union/UnionAll:返回兩個查詢的合集,包括重複項。其中UnionAll必須是相同類型或者是能夠相互轉換的。

l          Include:可經過此方法查詢出與相關的實體對象。實例代碼以下:

using (varedm = new NorthwindEntities())

     {

                stringesql1 = "select value c fromNorthwindEntities.Customers as c WHERE c.CustomerID ='HANAR'";

                ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);

                query1 = query1.Include("Orders");

                foreach(Customers c inquery1)

                {

                    Console.WriteLine("{0},{1}", c.CustomerID,c.Orders.Count);

                    //輸出:HANAR,14

                }

 

     }

l          OfType: 根據制定類篩選元素建立一個新的類型。此類型是要在實體模型中已定義過的。

l          OrderBy

實例代碼以下:

using (varedm = new NorthwindEntities())

    {

                stringesql1 = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";

                ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);

                query1.OrderBy("it.country asc,it.city asc");

//也能夠這樣寫               

//query1.OrderBy("it.countryasc");

                //query1.OrderBy("it.cityasc");

                foreach(Customers c inquery1)

                {

                    Console.WriteLine("{0},{1}", c.Country, c.City);

                }

      }

l          Select

實例代碼以下:

using (varedm = new NorthwindEntities())

    {

                stringesql1 = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";

                ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);

                ObjectQuery<DbDataRecord> records = query1.Select("it.customerid,it.country");

                foreach(DbDataRecord c inrecords)

                {

                    Console.WriteLine("{0},{1}", c[0], c[1]);

                }

                Console.WriteLine(records.ToTraceString());

                //SQL輸出:

                //SELECTTOP (10)

                //1AS [C1],

                //[Extent1].[CustomerID]AS [CustomerID],

                //[Extent1].[Country]AS [Country]

                //FROM[dbo].[Customers] AS [Extent1]

                //ORDERBY [Extent1].[CustomerID] ASC

      }

l          SelectValue

實例代碼以下:

using (varedm = new NorthwindEntities())

    {

                stringesql1 = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";

                ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);

                ObjectQuery<string> records = query1.SelectValue<string>("it.customerid");

                foreach(string c inrecords)

                {

                    Console.WriteLine("{0}", c);

                }

                Console.WriteLine(records.ToTraceString());

                //SQL輸出:

                //SELECTTOP (10)

                //[Extent1].[CustomerID]AS [CustomerID]

                //FROM[dbo].[Customers] AS [Extent1]

                //ORDERBY [Extent1].[CustomerID] ASC

       }

l          Skip/Top

實例代碼以下:

using (varedm = new NorthwindEntities())

    {

                stringesql1 = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID ";

                ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);

                query1 = query1.Skip("it.customerid asc", "10");

                query1 = query1.Top("10");

                foreach(Customers c inquery1)

                {

                    Console.WriteLine("{0}", c.CustomerID);

                }

                Console.WriteLine(query1.ToTraceString());

                //SQL輸出:

                //SELECTTOP (10)

                //[Extent1].[CustomerID]AS [CustomerID]

                //FROM[dbo].[Customers] AS [Extent1]

                //ORDERBY [Extent1].[CustomerID] ASC

}

   本節,簡單的介紹一下與ObjectQuery查詢相關的語法,我我的以爲查詢寫法比較多,須要在平常的編程中去發現,在這裏就不一一複述了。下節,將介紹EntityClient相關的內容。

 

Entity Framework 學習初級篇6--EntityClient

 

System.Data.EntityClient 命名空間是 實體框架的 .NET Framework 數據提供程序。EntityClient 提供程序使用存儲特定的 ADO.NET 數據提供程序類和映射元數據與實體數據模型進行交互。EntityClient 首先將對概念性實體執行的操做轉換爲對物理數據源執行的操做。而後再將物理數據源返回的結果集轉換爲概念性實體。

EntityClient下的類有如下幾個:

l          EntityConnection

l          EntityCommand

l          EntityConnectionStringBuilder

l          EntityParameter

l          EntityDataReader 

l          EntityParameterCollection

l          EntityProviderFactory

l          EntityTransaction

從類的名字上看,咱們就知道它們的做用是什麼了。在此,就再也不一一解釋了。直接經過實例代碼來學習它們。

l        EntityConnection:

實例代碼1:

stringcon = "name = NorthwindEntities";

            using(EntityConnection econn = new EntityConnection(con))

            {

                stringesql = "Select VALUE c fromNorthwindEntities.Customers as c where c.CustomerID='ALFKI'";

                econn.Open();

                EntityCommandecmd = new EntityCommand(esql,econn);

                EntityDataReaderereader = ecmd.ExecuteReader(CommandBehavior.SequentialAccess);

                if(ereader.Read())

                {

                    Console.WriteLine(ereader["CustomerID"]);

                }

                Console.WriteLine(ecmd.ToTraceString());

            }

上述代碼中,須要注意的是EntityConnection的構造方法。其中,鏈接字符串寫法有多很,以下:

寫法1:

string con ="name = NorthwindEntities" ;其中的」NorthwindEntities」是配置文件中的鏈接字符串名稱

寫法2:

string con =System.Configuration.ConfigurationManager.ConnectionStrings["NorthwindEntities"].ConnectionString;其中的」NorthwindEntities」是配置文件中的鏈接字符串名稱

寫法3:

string con = @"metadata=res://*/NorthWind.csdl|res://*/NorthWind.ssdl|res://*/NorthWind.msl;provider=System.Data.SqlClient;providerconnection string='Data Source=.\SQLEXPRESS;InitialCatalog=Northwind;Integrated Security=True;MultipleActiveResultSets=True'";其中的這些字符串是配置文件中的鏈接字符串的值

寫法4:

NorthwindEntitiesedm = new NorthwindEntities();

string con =edm.Connection.ConnectionString;

上述寫法中,基於寫法簡單、方便我比較推薦使用第1種或者第2種寫法。

l        EntityCommand

它具備的方法有:ExecuteDbDataReader、 ExecuteNonQuery 、 ExecuteReader 、 ExecuteScalar等。

實例代碼2:

stringcon = "name = NorthwindEntities";

            using(EntityConnection econn = new EntityConnection(con))

            {

                stringesql = "Select VALUE c fromNorthwindEntities.Customers as c where c.CustomerID='ALFKI'";

                econn.Open();

                EntityCommandecmd = econn.CreateCommand();

                ecmd.CommandText = esql;

                EntityDataReaderereader = ecmd.ExecuteReader(CommandBehavior.SequentialAccess);

                if(ereader.Read())

                {

                    Console.WriteLine(ereader["CustomerID"]);

                }

                Console.WriteLine(ecmd.ToTraceString());

            }

代碼中,EntityCommand建立方式和實例代碼1中的稍有不一樣,相信你們都明白,就很少說了。

l        EntityConnectionStringBuilder

實例代碼3:

           EntityConnectionStringBuilder esb = newEntityConnectionStringBuilder();

            esb.Provider = "System.Data.SqlClient";

            esb.Metadata = @"res://*/NorthWind.csdl|res://*/NorthWind.ssdl|res://*/NorthWind.msl";

            esb.ProviderConnectionString = @"Data Source=.\SQLEXPRESS;InitialCatalog=Northwind;Integrated Security=True;MultipleActiveResultSets=True";

           EntityConnectioneconn = new EntityConnection(esb.ConnectionString)//建立鏈接

l        EntityParameter

代碼實例4:

stringcon = "name = NorthwindEntities";

            using(EntityConnection econn = new EntityConnection(con))

            {

                stringesql = "Select value c fromNorthwindEntities.Customers as c order by c.CustomerID skip @start limit@end";

                econn.Open();

                EntityCommandecmd = new EntityCommand(esql,econn);

                EntityParameterp1 = new EntityParameter("start", DbType.Int32);

                p1.Value = 0;

                EntityParameterp2 = new EntityParameter("end", DbType.Int32);

                p2.Value = 10;

                ecmd.Parameters.Add(p1);

                ecmd.Parameters.Add(p2);

                 EntityDataReaderereader = ecmd.ExecuteReader(CommandBehavior.SequentialAccess);

                while(ereader.Read())

                {

                    Console.WriteLine(ereader["CustomerID"]);

                }

                Console.WriteLine(ecmd.ToTraceString());

            }

其中,參數是以@符號前綴的,EntityParameter實體參數類,除了能夠直接構造出實例來。爲實體命令對象添加參數,咱們還能夠直接調用Parameters.AddWithValue方法。以下代碼:

     ecmd.Parameters.AddWithValue("start",0);

    ecmd.Parameters.AddWithValue("end", 10);

    我比較喜歡用上面的代碼,簡單、方便。

l        EntityDataReader

stringcon = "name = NorthwindEntities";

            using(EntityConnection econn = new EntityConnection(con))

            {

                stringesql = "Select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10 ";

                econn.Open();

                EntityCommandecmd = new EntityCommand(esql,econn);

                EntityDataReaderereader = ecmd.ExecuteReader(CommandBehavior.SequentialAccess);

                while(ereader.Read())

                {

                    Console.WriteLine("{0},{1},{2},{3},{4}", ereader[0],ereader[1], ereader[2], ereader[3], ereader[4]);              

                }

                Console.WriteLine(ecmd.ToTraceString());

            }

須要注意的是:CommandBehavior.SequentialAccess;這個地方。不一樣的枚舉項,對查詢會有不一樣影響。枚舉以下:

l          Default 此查詢可能返回多個結果集。在功能上等效於調用 ExecuteReader()。 

l          SingleResult 查詢返回一個結果集。 

l          SchemaOnly 查詢僅返回列信息。

l          KeyInfo 此查詢返回列和主鍵信息。 

l          SingleRow 查詢應返回一行。

l          SequentialAccess 提供一種方法,以便 DataReader處理包含帶有大二進制值的列的行。

l          CloseConnection 在執行該命令時,若是關閉關聯的DataReader 對象,則關聯的 Connection 對象也將關閉。

須要說明的是,若是使用SequentialAccess則需按順序訪問列,不然將拋異常。以下代碼,將會拋異常:

while (ereader.Read())

    {

          //異常信息:從列序列號「1」開始讀取的嘗試無效。經過CommandBehavior.SequentialAccess,只能從列序列號「5」或更大值處開始讀取

          Console.WriteLine("{0},{1},{2},{3},{4}", ereader[4],ereader[1], ereader[2], ereader[3], ereader[0]);    

}

l        EntityTransaction

事務類。目前因爲ESQL僅提供查詢的命令,沒有提供對Insert、Update、Delete等的支持。因此,我以爲目前這個類基本沒有用,(不可能我作查詢還使用事務吧!)。

從上述簡單的介紹,咱們能夠看到,EntityClient和SqlClient下的類基本上是一致的。因此很容易掌握。其餘就很少說了。

 

Entity Framework 學習初級篇7--基本操做:增長、更新、刪除、事務

 

本節,直接寫經過代碼來學習。這些基本操做都比較簡單,與這些基本操做相關的內容在以前的1至6節基本介紹完畢。

l          增長:

方法1:使用AddToXXX(xxx)方法:實例代碼以下:

           using (var edm = new NorthwindEntities())

            {

                Customersc = new Customers{ CustomerID = "c#", City = "成都市",Address = "中國四川省", CompanyName= "cnblogs", Country = "中國",Fax = "10086", Phone = "1008611", PostalCode = "610000", Region = "天府廣場",ContactName = "風車車.Net" };

                edm.AddToCustomers(c);

                intresult = edm.SaveChanges();

                Assert.AreEqual(result,1);

                Customersaddc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c#");

                Console.WriteLine("CustomerId={0},City={1}",addc.CustomerID, addc.City);

            }

方法2:使用ObjectContext的AddObject(string entitySetName, object entity)方法。實例代碼以下:

using (varedm = new NorthwindEntities())

            {

                    Customersc = new Customers{ CustomerID = "c2", City = "成都市2",Address = "中國四川省2", CompanyName= "cnblogs", Country = "中國",Fax = "10086", Phone = "1008611", PostalCode = "610000", Region = "天府廣場",ContactName = "風車車.Net" };

                    edm.AddObject("Customers", c);

                    intresult = edm.SaveChanges();

                    Assert.AreEqual(result,1);

                    Customersaddc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");

                    Console.WriteLine("CustomerId={0},City={1}",addc.CustomerID, addc.City);

             }

其中,在代碼中,須要注意的是:AddObject方法中參數「entitySetName 」就是指對應實體名稱,應該是:「Customers」,而不是「NorthwindEntities.Customers」;

l          更新:

using (varedm = new NorthwindEntities())

            {

                    Customersaddc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");

                    addc.City = "CD";

                    addc.ContactName = "cnblogs";

                    addc.Country = "CN";

                    intresult = edm.SaveChanges();

                    Assert.AreEqual(result,1);

                    Customersupdatec = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");

                    Console.WriteLine("CustomerId={0},City={1}",updatec.CustomerID, updatec.City);

              }

其中,須要注意的是:不能去更新主鍵,不然會報「System.InvalidOperationException : 屬性「xxx」是對象的鍵信息的一部分,不能修改。」

l          刪除:

實例代碼以下:

using (varedm = new NorthwindEntities())

        {

                    Customersdeletec = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");

                    edm.DeleteObject(deletec);

                    intresult = edm.SaveChanges();

                    Assert.AreEqual(result,1);

                    Customersc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");

                    Assert.AreEqual(c,null);

                

         }

l          事務:

實例代碼以下:

NorthwindEntitiesedm = null;

            System.Data.Common.DbTransaction tran = null;

            try

            {

                edm = newNorthwindEntities();

                edm.Connection.Open();

                tran = edm.Connection.BeginTransaction();

                Customerscst = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c#");

                cst.Country = "CN";

                cst.City = "CD";

                edm.SaveChanges();

                tran.Commit();

            }

            catch(Exception ex)

            {

                if(tran != null)

                    tran.Rollback();

                throwex;

            }

            finally

            {

                if(edm != null && edm.Connection.State !=System.Data.ConnectionState.Closed)

                    edm.Connection.Close();

            }

至此,初級篇基本介紹完畢。後面,打算寫點,中級篇的東西。

 

Entity Framework 學習中級篇1—EF支持複雜類型的實現

 

本節,將介紹如何手動構造複雜類型(ComplexType)以及複雜類型的簡單操做。

一般,複雜類型是指那些由幾個簡單的類型組合而成的類型。好比:一張Customer表,其中有FristName和LastName字段,那麼對應的Customer實體類將會有FristName和LastName這兩個屬性。當咱們想把FirstName和LastName合成一個名爲CustomerName屬性時,此時,若是要在EF中實現這個目的,那麼咱們就須要用到複雜類型。

目前,因爲EF不能顯示支持複雜類型,因此咱們沒法在VS裏的可視化設計器裏面來設計咱們須要的複雜類型。因此,咱們須要手動修改實體模型,以便使其支持複雜類型的屬性。修改的主要步驟有如下幾步:

l         產生實體模型

l         修改CSDL文件

l         修改msl文件

l         從新生成模型實體類

在後續的介紹,我使用數據庫使用的是NorthWind,並針對Customer表對應的實體類來增長複雜屬性Address,其中複雜屬性Address由Address,City,Region,Country和PostalCode這個幾個組合而成。

下面,介紹具體的操做步驟:

第一步:產生實體模型

實體模型的產生咱們能夠直接經過在VS可視化設計器來產生(若是不會,請參考《Entity Framework 學習初級篇1--EF基本概況》)。或者使用EdmGen工具來產生(EdmGen工具位於:系統盤符:\WINDOWS\Microsoft.NET\Framework\v3.5下面)。具體步驟就不復述了。

我產生的實體模型文件是:NorthwindEnites.edmx

第二步:修改csdl文件

產生了實體模型後,咱們使用記事本或其餘文本編輯工具打開實體模型,(小技巧:能夠把實體模型後綴.edmx改成.xml,而後把實體模型文件直接拖到VS裏面進行修改,這樣修改起來比較方便,待修改完畢後,將後綴改回來便可。)

接着,開始手動修改csdl文件,找到模型文件中關於csdl定義的部分,而後找到實體類型名爲Customers的定義節,刪除原來的Address,City,Region,Country,PostalCode屬性定義,而後添加一個名爲Address的屬性,以下代碼所示:

       <EntityTypeName="Customers">

          <Key>

            <PropertyRefName="CustomerID" />

          </Key>

          <PropertyName="CustomerID"Type="String"Nullable="false"MaxLength="5"Unicode="true"FixedLength="true" />

          <PropertyName="CompanyName"Type="String"Nullable="false"MaxLength="40"Unicode="true"FixedLength="false" />

          <PropertyName="ContactName"Type="String"MaxLength="30"Unicode="true"FixedLength="false" />

          <PropertyName="ContactTitle"Type="String"MaxLength="30"Unicode="true"FixedLength="false" />

          <PropertyName="Address"Type="NorthwindModel.CommonAddress"Nullable="false"></Property>

          <PropertyName="Phone"Type="String"MaxLength="24"Unicode="true"FixedLength="false" />

          <PropertyName="Fax"Type="String"MaxLength="24"Unicode="true"FixedLength="false" />

          <NavigationPropertyName="Orders"Relationship="NorthwindModel.FK_Orders_Customers"FromRole="Customers"ToRole="Orders" />

          <NavigationPropertyName="CustomerDemographics"Relationship="NorthwindModel.CustomerCustomerDemo"FromRole="Customers"ToRole="CustomerDemographics" />

        </EntityType>

接着,須要添加一個名爲CommonAddress複雜類型的定義,具體以下代碼:

<ComplexTypeName="CommonAddress">

          <PropertyName="Address"Type="String"MaxLength="60"Unicode="true"FixedLength="false" />

          <PropertyName="City"Type="String"MaxLength="15"Unicode="true"FixedLength="false" />

          <PropertyName="Region"Type="String"MaxLength="15"Unicode="true"FixedLength="false" />

          <PropertyName="PostalCode"Type="String"MaxLength="10"Unicode="true"FixedLength="false" />

          <PropertyName="Country"Type="String"MaxLength="15"Unicode="true"FixedLength="false" />

        </ComplexType>

至此,csdl部分修改完畢。

第三步,修改msl文件:

找到msl部分的定義,修改Customers部分的影射定義。具體代碼以下(請注意ComplexProperty節):

<EntitySetMappingName="Customers">

            <EntityTypeMappingTypeName="IsTypeOf(NorthwindModel.Customers)">

              <MappingFragmentStoreEntitySet="Customers">

                <ScalarPropertyName="CustomerID"ColumnName="CustomerID" />

                <ScalarPropertyName="CompanyName"ColumnName="CompanyName" />

                <ScalarPropertyName="ContactName"ColumnName="ContactName" />

                <ScalarPropertyName="ContactTitle"ColumnName="ContactTitle" />

                <ComplexPropertyName="Address"TypeName="NorthwindModel.CommonAddress">

                  <ScalarPropertyName="Address"ColumnName="Address" />

                  <ScalarPropertyName="City"ColumnName="City" />

                  <ScalarPropertyName="Region"ColumnName="Region" />

                  <ScalarPropertyName="PostalCode"ColumnName="PostalCode" />

                  <ScalarPropertyName="Country"ColumnName="Country" />

                </ComplexProperty>

                <ScalarPropertyName="Phone"ColumnName="Phone" />

                <ScalarPropertyName="Fax"ColumnName="Fax" />

              </MappingFragment>

            </EntityTypeMapping>

  </EntitySetMapping>

至此,msl部分修改完畢

第四步:從新產生實體類文件。

咱們可使用EmdGen2工具來從新實體類.cs文件。具體操做以下:

將修改好的模型文件(edmx),拷貝到使用edmgen2.exe同目錄下,而後在命令行中輸入:

Edmgen2 /codegen cs NorthwindEnites.edmx

執行此命令後,會在當前的文件夾下生成一個NorthwindEnites.cs代碼文件,也就是實體類的代碼文件。將改文件更名爲:NorthwindEnites.Designer.cs(這步主要是和edmx對應起來)。

而後,將NorthwindEnites.edmx和NorthwindEnites.Designer.cs文件添加到項目中。

至此,複合類型的修改完畢。

按照一樣的修改過程,咱們能夠給Employees也增長一個Address的複雜類型屬性。

接下來,咱們看看具體使用代碼:

l        查詢:

[Test]

        publicvoid TestAddress()

        {

            using(var db = newNorthwindModel.NorthwindEntities1())

            {

                Console.WriteLine("GetFive customer addresss :");

                var cts = db.Customers.Take(5);

                foreach (var c in cts)

                {

                    Console.WriteLine("Address:{0},Country:{1},City:{2},PostalCode:{3}",c.Address.Address, c.Address.Country, c.Address.City, c.Address.PostalCode);

                }

                Console.WriteLine("GetFive Employess address:");

                var emp = db.Customers.Take(5);

                foreach(var c in emp)

                {

                    Console.WriteLine("Address:{0},Country:{1},City:{2},PostalCode:{3}",c.Address.Address, c.Address.Country, c.Address.City, c.Address.PostalCode);

                }

            }

        }

l        添加:

[Test]

        publicvoid AddTest()

        {

            using(var db = newNorthwindModel.NorthwindEntities1())

            {

                var customer = newNorthwindModel.Customers

                {

                    CustomerID = "2009",

                    CompanyName = "Complex Company",

                    ContactName = "xray2005",

                    Address = new NorthwindModel.CommonAddress

                    {

                        Address = "SiChuan,China",

                        City = "ChengDou",

                        Country = "China",

                        PostalCode ="610041",

                        Region = "Chenghua"

                    }

                };

               db.AddToCustomers(customer);

                db.SaveChanges();

                var cst = db.Customers.FirstOrDefault(c =>c.CustomerID == "2009");

                Assert.IsNotNull(cst);            

                Console.WriteLine("CustomerID:{0},CompanyName:{1},ContactName:{2},City:{3},Country:{4}",cst.CustomerID, cst.CompanyName, cst.ContactName, cst.Address.City,cst.Address.Country);

            }

        }

l        條件查詢:

[Test]

        publicvoid QueryTest()

        {

            using(var db = newNorthwindModel.NorthwindEntities1())

            {

                var cst = db.Customers.FirstOrDefault(c =>c.Address.City == "ChengDou");

                Assert.IsNotNull(cst);      

                Console.WriteLine("CustomerID:{0},CompanyName:{1},ContactName:{2},City:{3},Country:{4}",cst.CustomerID, cst.CompanyName, cst.ContactName, cst.Address.City,cst.Address.Country);

            }

        }

最後,補充說明:

1,  在VS的可視化設計器裏,不支持複雜類型,因此修改後沒法再在可視化設計器裏修改模型(edmx文件)。

2,  複雜類型不能單獨存在,它必須和某一實體相關起來。

3,  複雜類型不能包含導航屬性,如導航到實體或實體集。

4,  複雜類型具備內部結構但沒有 Key(主鍵) 屬性的數據類型

下面是示例代碼和EdmGen2工具的鏈接。

示例代碼  EdmGen2

 

Entity Framework 學習中級篇2—存儲過程()

 

目前,EF對存儲過程的支持並不完善。存在如下問題:

l        EF不支持存儲過程返回多表聯合查詢的結果集。

l        EF僅支持返回返回某個表的所有字段,以便轉換成對應的實體。沒法支持返回部分字段的狀況。

l        雖然能夠正常導入返回標量值的存儲過程,可是卻沒有爲咱們自動生成相應的實體.cs代碼,咱們仍是沒法在代碼中直接調用或使用標量存儲過程

l        EF不能直接支持存儲過程當中Output類型的參數。

l        其餘一些問題。

下面,主要針對如何使用存儲過程,以及存儲返回實體、表的部分字段這個幾個問題,作具體介紹。

l         導入存儲過程及返回實體

在VS可視化設計器中,打開實體模型(emdx文件)。而後,鼠標右鍵點擊「Customers」à 「添加」à「函數導入」,而後選擇「存儲過程名稱」,並輸入函數導入名稱,選擇返回類型爲實體並選擇Customers。以下圖所示:

 

以後,點擊「肯定」。以後,存儲過程導入。在代碼咱們就可使用改存儲過程了。以下代碼所示:

[Test]

        public voidGetEntityBySP()

        {

           using (var db =new NorthwindEntities())

           {

               var cst = db.GetCustomerById("ALFKI").FirstOrDefault();

               Assert.IsNotNull(cst);

               Console.WriteLine("CustomerId:{0},ContactName:{1}",cst.CustomerID, cst.ContactName);

           }

        }

l         聯合查詢結果集的問題

在此版本的EF中,是不支持存儲過程的多張表聯合查詢的,也就是說查詢的結果集中,一部分字段來自表A,另外一部分字段來自表B,像這種狀況,目前EF沒法直接進行處理。爲此,能夠經過寫兩個或多個存儲過程來實現。好比:第一個存儲過程返回表A的全部字段,第二存儲過程返回表B的全部字段;而後,咱們在代碼中來實現聯合的查詢。

按照前面的思路,增長一個返回全部的Orders表字段的存儲過程GetOrdersByCustomerId,再增長一個返回Order Details表所有字段的存儲過程GetDetailsByCustomerId,並將它們導入到實體模型中。

其中,GetOrdersByCustomerId存儲過程以下:

CREATE PROCEDURE GetOrdersByCustomerId

        @CustomerId varchar(5)

AS

BEGIN

        SET NOCOUNT ON;

        SELECT * FROM orders WHEREorders.CustomerID=@CustomerId;

END

GetDetailsByCustomerId存儲過程以下:

CREATE PROCEDURE GetDetailsByCustomerId

@CustomerIdvarchar(5)       

AS

BEGIN

        SET NOCOUNT ON;

        SELECT d.*

     FROM  Orders o,[Order Details] d

        WHERE o.OrderId=d.OrderId  AND o.CustomerId=@CustomerId;

END

以後,在咱們的代碼來實現聯合查詢。代碼以下:

[Test]

        public voidGetOrderBySp()

        {

           using (var db =new NorthwindEntities())

           {

               var orders = db.GetOrdersByCustomerId("VINET").ToList();

               var details = db.GetDetailsByCustomerId("VINET").ToList();

               orders.ForEach(o =>o.Order_Details.Attach(details.Where(d => d.OrderID == o.OrderID)));

               foreach (varorder in orders)

               {

                   Console.WriteLine(order.OrderID);

                   foreach (var d in order.Order_Details)

                       Console.WriteLine(d.ProductID);

               }

           }

        }

其中,須要注意的,因爲是分別執行了兩個存儲,在內存中是以兩個對立的對象存在,它們以前是沒有創建聯繫的。爲此,咱們須要使用Attach方法來把他們聯繫起來(紅色代碼段),這樣咱們就能夠經過導航來訪問對象的實體了,也就是foreach (var d in order.Order_Details)中的order.Order_Details。

l         返回部分字段的問題

默認狀況,目前此版本的EF在使用存儲過程返回實體的時候,必須返回全部的字段,以即是EF可以自動將返回的結果轉換成對應的實體。不然會報「數據讀取器與指定的XXX類型不兼容的異常,….」。

接下來,咱們經過創建一個存儲過程,並創建新創建一個實體來解決此問題。首先,咱們在數據庫中創建一個名爲GetCustomerAndOrders的存儲過程,其定義以下:

CREATE PROCEDURE GetCustomerAndOrders

    AS

BEGIN

    SET NOCOUNT ON;

    SELECTc.CustomerID,c.CompanyName,o.OrderID,o.OrderDate,d.Quantity

    FROMCustomers c,Orders o,[OrderDetails] d

    WHEREc.CustomerID=o.CustomerID AND o.OrderID=d.OrderID;

END

GO

而後,添加一個實體CustomerOders,並設置屬性以下圖所示:

 

而後,在VS可視化設計器中,打開實體模型(emdx文件),經過添加à函數導入,導入存儲過程GetCustomerAndOrders,並取名爲GetCustomerAndOrders,返回類型設置爲實體CustomerOrders。

最後,咱們就能夠代碼實體此實體和存儲過程了。以下代碼:

[Test]

        public voidGetOrderCountByCustomerId()

        {

           using (vardb = new NorthwindEntities())

           {

               var co =db.GetCustomerAndOrders().Take(10).Skip(0);

               foreach(var c in co)

                     Console.WriteLine(c.CustomerID);

           }

        }

l         返回標量值問題

目前,EF對存儲過程返回標量值的支持並無徹底。雖然,咱們能夠按照前面的步驟導入函數,並設置返回標量值的類型,同時EF會在實體模型文件爲咱們自動生成此存儲過程相關的映射配置等。可是,EF卻沒有爲咱們生成在實體模型cs文件中,自動生成相應的.cs代碼,因此咱們根本沒法直接調用此存儲過程。爲解決此問題,咱們須要手動添加代碼到實體模型的.cs代碼文件中。

首先,在數據庫中創建存儲存儲過程GetOrderCntByCustomerId,代碼以下:

CREATE PROCEDUREGetOrderCntByCustomerId

        @CustomerId varchar(5)

AS

BEGIN

        SET NOCOUNT ON;

        SELECT count(*) FROM Orders WHERE Orders.CustomerId=@CustomerId;

END

接着,按照正常的步驟,導入此存儲過程並設置返回類型爲標量類型的Int32。

而後,咱們須要添加一個泛型的方法和一個執行存儲過程的方法,代碼以下:

public partial class NorthwindEntities

{

   //泛型方法用於執行標量存儲過程

        private T ExecuteFunction<T>(string functionName, System.Data.EntityClient.EntityParameter[] parameters) where T : struct

        {

           System.Data.EntityClient.EntityCommand cmd =((System.Data.EntityClient.EntityConnection)this.Connection).CreateCommand();

           cmd.CommandType = System.Data.CommandType.StoredProcedure;

           cmd.Parameters.AddRange(parameters);

           cmd.CommandText = this.DefaultContainerName + "." + functionName;

           try

           {

               if (cmd.Connection.State != System.Data.ConnectionState.Open)

                   cmd.Connection.Open();

               var obj = cmd.ExecuteScalar();

               return (T)obj;

           }

           catch (System.Exception)

           {

               throw;

           }

           finally

           {

               cmd.Connection.Close();

           }

        }

        //執行數據庫中GetOrderCntByCustomerId存儲過程的方法

        public intGetOrderCountByCustomerId(string CustomerId)

        {

           var param = newSystem.Data.EntityClient.EntityParameter("CustomerId", System.Data.DbType.String);

           param.Value = CustomerId;

           return ExecuteFunction<int>("GetOrderCountByCustomerId",new[] { param });

        }

}

最後,經過以上修改,咱們就能夠直接使用返回標量值的存儲過程,代碼以下:

[Test]

        public voidGetOrderCountByCustomerId()

        {

           using (var db =new NorthwindEntities())

           {

               var result = db.GetOrderCountByCustomerId("VINET");

               Assert.Greater(result, 0);

               Console.WriteLine(result.ToString());

           }

        }

至此,咱們就解決了EF存儲過程返回標量的問題。

 

Entity Framework 學習中級篇3—存儲過程()

 

目前,EF對存儲過程的支持並不完善。存在如下問題:

l        EF不支持存儲過程返回多表聯合查詢的結果集。

l        EF僅支持返回返回某個表的所有字段,以便轉換成對應的實體。沒法支持返回部分字段的狀況。

l        雖然能夠正常導入返回標量值的存儲過程,可是卻沒有爲咱們自動生成相應的實體.cs代碼,咱們仍是沒法在代碼中直接調用或使用標量存儲過程

l        EF不能直接支持存儲過程當中Output類型的參數。

l        其餘一些問題。

本節,咱們將學習如何手動添加/修改存儲過程,如何使EF可以支持Output類型的參數。

l         添加/修改存儲過程

有時候,某個SQL語句比較複雜,可是數據庫中又沒有定義相應的存儲過程。這個時候,咱們又想使上層代碼比較簡單、方便的方式來完成此項任務。那麼,此時,咱們即可以手工在實體模型(.edmx文件)中添加本身須要的存儲過程了。這樣既方便上層調用又方便後期的修改。

以手動修改實體模型edmx文件,添加名爲CustomerByCommandText的存儲過程爲例。具體步驟以下:

修改實體模型文件,找到ssdl部分,添加以下代碼:

        <Function Name="CustomerByCommandText" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo" >

         <CommandText>

           select c.* from Customers c,Orders o where c.CustomerID=o.CustomerID

         </CommandText>

       </Function>

而後,再找到csdl部分,添加以下代碼:

<FunctionImport Name="CustomerByCommandText" EntitySet="Customers" ReturnType="Collection(NorthwindModel.Customers)"></FunctionImport>

接着,找到msl部分,添加以下代碼:

<FunctionImportMapping FunctionImportName="CustomerByCommandText" FunctionName="NorthwindModel.Store.CustomerByCommandText"/>

最後,在實體模型的.cs文件裏面,添加一個執行此存儲過程的方法,代碼以下:

        public global::System.Data.Objects.ObjectResult<Customers>GetCustomerByCommandText()

        {

           return base.ExecuteFunction<Customers>("CustomerByCommandText");

        }

至此,修改完畢。

如今,咱們就能夠在代碼使用剛纔手工定義的存儲過程了。以下代碼所示:

[Test]

        public voidGetCustomerByCmdText()

        {

           using (var db =new NorthwindEntities())

           {

               var csts =db.GetCustomerByCommandText().Take(10).Skip(0);

               foreach (var c in csts)

                   Console.WriteLine(c.CustomerID);

           }

        }

其實,關鍵的地方就是CommandText這個部分的內容,它裏面就是要執行的SQL語句。另外,咱們能夠在修改實體模型emdx文件的同時,咱們能夠看到全部的實體類查詢的SQL語句命令均可以在edmx文件裏找到,咱們均可以進行相應的修改。

l         Output類型參數

在實際應用當中,不少時候,咱們須要使用output類型的存儲過程參數,以便返回咱們須要的值。可是,目前,EF不能直接支持output類型的存儲過程參數。爲此,咱們須要對實體模型進行修改,以便使其支持output類型的輸出參數。具體過程以下:

在數據庫中創建一個爲名的GetNameByCustomerId存儲過程,代碼以下:

CREATE PROCEDURE GetNameByCustomerId

 @CustomerId varchar(5),

 @ContactName varchar(30) output

AS

BEGIN

 SET NOCOUNT ON;

 SELECT @ContactName=ContactName

 FROM Customers

 WHERE CustomerID=@CustomerId;

END

而後,開始修改實體模型edmx文件。

先找到ssdl定義的部分,添加以下代碼:

      <Function Name="GetNameByCustomerId" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">

         <Parameter Name="CustomerId" Type="varchar" Mode="In" MaxLength="5"></Parameter>

         <Parameter Name="ContactName" Type="varchar" Mode="Out" MaxLength="30"></Parameter>

       </Function>

接着,在找到csdl定義的部分,添加以下代碼:

      <FunctionImport Name="GetNameByCustomerId">

           <Parameter Name="CustomerId" Mode="In" Type="String" MaxLength="5"></Parameter>

           <Parameter Name="ContactName" Mode="Out" Type="String" MaxLength="30"></Parameter>

      </FunctionImport>

最後,找到msl定義的部分,添加以下代碼:

<FunctionImportMapping FunctionImportName="GetNameByCustomerId" FunctionName="NorthwindModel.Store.GetNameByCustomerId"></FunctionImportMapping>

至此,實體模型emdx文件修改完畢。

接下來,咱們須要在實體模型的.cs文件中,增長相應的調用方法。代碼以下:

    public partial class NorthwindEntities1

{

 

//執行GetNameByCustomerId的方法

        public voidGetNameByCustomerId(string CustomerId, out stringContactName)

        {

           ContactName= string.Empty;

           var Pars = newSystem.Data.EntityClient.EntityParameter[]

           {

               new System.Data.EntityClient.EntityParameter{ ParameterName="CustomerId", DbType=System.Data.DbType.String,Value=CustomerId},

               new System.Data.EntityClient.EntityParameter{ParameterName="ContactName", DbType=System.Data.DbType.String, Direction=System.Data.ParameterDirection.Output}

           };

           this.ExecuteNonQuery("GetNameByCustomerId",Pars);

           ContactName = Pars[1].Value.ToString();

 

        }

     

      //輔助方法,執行SQL命令

        private voidExecuteNonQuery(string functionName,System.Data.EntityClient.EntityParameter[]parameters)

        {

           System.Data.EntityClient.EntityCommand cmd =((System.Data.EntityClient.EntityConnection)this.Connection).CreateCommand();

           cmd.CommandType = System.Data.CommandType.StoredProcedure;

           cmd.Parameters.AddRange(parameters);

           cmd.CommandText = this.DefaultContainerName + "." + functionName;

           try

           {

               if (cmd.Connection.State != System.Data.ConnectionState.Open)

                   cmd.Connection.Open();

               cmd.ExecuteNonQuery();

           }

           catch (System.Exception)

           {

               throw;

           }

           finally

           {

               cmd.Connection.Close();

           }

        }

}

如今,全部的修改工做都作完了。接下來,咱們就能夠在代碼中直接調用此存儲過程了。示例代碼以下:

[Test]

        public voidOutputTest()

        {

           using (var db =new NorthwindModel.NorthwindEntities1())

           {

               string contactname = string.Empty;

               db.GetNameByCustomerId("ALFKI", out contactname);

               Assert.IsTrue(!string.IsNullOrEmpty(contactname));

               Console.WriteLine(contactname);

           }

        }

至此,咱們即可以使用Output類型的輸出參數了。

 

Entity Framework 學習中級篇4—存儲過程()

 

在EF中,各個實體的插入、更新和刪除也都經過使用存儲過程來完成,以便提升點性能。這個相似於數據集。其步驟是:先定義存儲過程,而後在VS的可視化設計器,設置存儲過程映射便可。

下面,覺得Supplier實體映射存儲過程爲例。

分別創建插入、更新和刪除存儲過程。

InsertSuppliers存儲過程定義以下:

CREATE PROCEDURE [dbo].[InsertSuppliers]

    -- Add theparameters for the stored procedure here

@CompanyName nvarchar(40),

@ContactName nvarchar(30),

@ContactTitle nvarchar(30),

@Address nvarchar(60),

@City nvarchar(15),

@Region nvarchar(15),

@PostalCode nvarchar(10),

@Country nvarchar(15),

@Phone nvarchar(24),

@Fax nvarchar(24),

@HomePage ntext

AS

BEGIN

    -- SETNOCOUNT ON added to prevent extra result sets from

    --interfering with SELECT statements.

    SET NOCOUNT ON;

    INSERTINTO Suppliers(CompanyName,ContactName,ContactTitle,Address,City,Region,PostalCode,Country,Phone,Fax,HomePage)

    VALUES(@CompanyName,@ContactName,@ContactTitle,@Address,@City,@Region,@PostalCode,@Country,@Phone,@Fax,@HomePage);

    SELECTSCOPE_IDENTITY()AS SupplierID;

END

DeleteSuppliers存儲過程定義以下:

CREATE PROCEDURE [dbo].[DeleteSuppliers]

    -- Add theparameters for the stored procedure here

@SupplierID int

AS

BEGIN

    -- SETNOCOUNT ON added to prevent extra result sets from

    --interfering with SELECT statements.

    SET NOCOUNT ON;

    DELETESuppliers WHERE SupplierID=@SupplierID

END

UpdateSuppliers存儲過程定義以下:

CREATE PROCEDURE [dbo].[UpdateSuppliers]

 -- Add the parameters forthe stored procedure here

@SupplierID int,

@CompanyName nvarchar(40),

@ContactName nvarchar(30),

@ContactTitle nvarchar(30),

@Address nvarchar(60),

@City nvarchar(15),

@Region nvarchar(15),

@PostalCode nvarchar(10),

@Country nvarchar(15),

@Phone nvarchar(24),

@Fax nvarchar(24),

@HomePage ntext

AS

BEGIN

 -- SET NOCOUNT ON addedto prevent extra result sets from

 -- interfering withSELECT statements.

 SET NOCOUNT ON;

    UPDATESuppliers SET CompanyName=@CompanyName,ContactName=@ContactName,ContactTitle=@ContactTitle,Address=@Address,City=@City,Region=@Region,PostalCode=@PostalCode,Country=@Country,Phone=@Phone,Fax=@Fax,HomePage=@HomePage

 WHERE SupplierID=@SupplierID

END

而後,在VS可視化設計器中,打開實體模式edmx文件,而後在Suppliers實體上鼠標右鍵à「存儲過程映射」,而後在在分別設置存儲過程便可。以下圖所示:

 

至此,存儲過程映射完畢。咱們下面來具體的使用,代碼以下:

使用存儲過程Insert:

[Test]

        public voidSPInsert()

        {

           using (var db =new NorthwindEntities1())

           {

               var supplier = newSuppliers();

               supplier.CompanyName = "cnblogs";

               supplier.ContactName = "xray2005";

               supplier.ContactTitle = "net";

               supplier.City = "成都";

               supplier.Region = "四川";

               supplier.Country = "中國";

               supplier.PostalCode = "600041";

               supplier.Phone = "028-8001";

               supplier.Fax = "028-8002";

               supplier.HomePage = "http://xray2005.cnblogs.com";

               db.AddToSuppliers(supplier);

               db.SaveChanges();

 

           }

        }

使用存儲過程Update:

        [Test]

        public voidSPUpdate()

        {

           using (var db =new NorthwindEntities1())

           {

               var supplier = db.Suppliers.FirstOrDefault(s=> s.SupplierID == 30);

               Assert.IsNotNull(supplier);

               supplier.CompanyName = "CNBLOGS";

               supplier.ContactName = "xray2005";

               supplier.ContactTitle= "♂風風車.net";

               supplier.City = "成都";

               supplier.Region = "四川";

               supplier.Country = "China";

               supplier.PostalCode = "600040";

               supplier.Phone = "028-1008611";

               supplier.Fax = "028-10086";

               supplier.HomePage = "http://www.cnblogs.com/xray2005";

               db.SaveChanges();

 

           }

        }

使用存儲過程Delete:

        [Test]

        public voidSPDelete()

        {

           using(var db = new NorthwindEntities1())

           {

               var supplier = db.Suppliers.FirstOrDefault(s=> s.SupplierID == 31);

               Assert.IsNotNull(supplier);

               db.DeleteObject(supplier);

               db.SaveChanges();

               var supplier1 = db.Suppliers.FirstOrDefault(s=> s.SupplierID == 31);

               Assert.IsNull(supplier1);

           }

        }

至此,實體存儲過程映射介紹完畢。本節,內容比較簡單。

 

Entity Framework 學習中級篇5—使EF支持Oracle9i

 

從Code MSDN上下載下來的EFOracleProvider不支持Oracle9i.可是,目前我所使用的仍是Oracle9i。爲此,對EFOracleProvider修改瞭如下,以便使其支持Oracle9i.

下面說說具體修改地方.(紅色部分爲添加或修改的代碼部分)

一,修改EFOracleProvider

1,修改EFOracleProviderManifest.cs類文件,

 

internal const string TokenOracle9i ="9i";//addby xray2005

        internal const string TokenOracle10g = "10g";

        internal const string TokenOracle11g = "11g";

如下兩個地方,不修改也是能夠的.但考慮目前我主要是使用9i,因此也就修改爲9i了.

        private EFOracleVersion_version = EFOracleVersion.Oracle9i; //EFOracleVersion.Oracle11g;

        private string _token= TokenOracle9i; //TokenOracle10g;

 

2,修改EFOracleVersion.cs類文件,以下代碼所示:

namespace EFOracleProvider

{

    usingSystem;

    ///<summary>

    /// This enum describes the current storage version

    ///</summary>

    internalenum EFOracleVersion

    {

         Oracle9i = 9, //addby xray2005

        /// < summary>

        /// Oracle10g

        /// < /summary>

        Oracle10g = 10,

 

        /// < summary>

        /// Oracle 11g

        /// < /summary>

        Oracle11g = 11,

        // higher versions go here

    }

    ///<summary>

    /// This class is a simple utility class that determines theversion from the

    /// connection

    ///</summary>

    internalstatic class EFOracleVersionUtils

    {

        /// < summary>

        /// Get the version fromthe connection.

        /// < /summary>

        /// < paramname="connection">currentconnection</param>

        /// < returns>version for the current connection</returns>

        internal static EFOracleVersion GetStorageVersion(EFOracleConnection connection)

        {

           string serverVersion =connection.ServerVersion;

           if (serverVersion.StartsWith("9."))

           {

               return EFOracleVersion.Oracle9i; //add by xray2005

           }

           else if(serverVersion.StartsWith("10."))

           {

               return EFOracleVersion.Oracle10g;

           }

           else if(serverVersion.StartsWith("11."))

           {

               return EFOracleVersion.Oracle11g;

           }

           throw new ArgumentException("Couldnot determine storage version; " +

                   "a valid storage connection or a version hintis required.");

        }

        internal static string GetVersionHint(EFOracleVersionversion)

        {

           switch (version)

           {

               case EFOracleVersion.Oracle9i:

                   return EFOracleProviderManifest.TokenOracle9i; //add by xray2005

               case EFOracleVersion.Oracle10g:

                   return EFOracleProviderManifest.TokenOracle10g;

               case EFOracleVersion.Oracle11g:

                   return EFOracleProviderManifest.TokenOracle11g;

               default:

                   throw new ArgumentException("Couldnot determine storage version; " +

                           "a valid storage connection or a version hintis required.");

           }

        }

        internal static EFOracleVersion GetStorageVersion(string versionHint)

        {

           if (!string.IsNullOrEmpty(versionHint))

           {

               switch (versionHint)

               {

                   case EFOracleProviderManifest.TokenOracle9i:

                       return EFOracleVersion.Oracle9i; //add by xray2005

                   case EFOracleProviderManifest.TokenOracle10g:

                       return EFOracleVersion.Oracle10g;

                   case EFOracleProviderManifest.TokenOracle11g:

                       return EFOracleVersion.Oracle11g;

               }

           }

           throw new ArgumentException("Couldnot determine storage version; " +

                   "a valid storage connection or a version hintis required.");

        }

 

        internal static bool IsVersionX(EFOracleVersionstorageVersion)

        {

           return storageVersion == EFOracleVersion.Oracle9i ||storageVersion == EFOracleVersion.Oracle10g ||

               storageVersion == EFOracleVersion.Oracle11g; //add by xray2005

        }

    }

}

二,使用EFOracleProvider

修改完畢後,編譯一下.若是是本身下載的源代碼編譯的,那麼編譯後的EFOracleProvider自動已經在GAC註冊了.若是是手動註冊EFOracleProvider到GAC,那麼命令以下:

gacutil –I 「EFOracleProvider.dll」

其中gacutil.exe位於:系統盤符號:\Program Files\Microsoft SDKs\Windows\v6.0A\bin下面。

接下來,咱們須要作的就是,把這個EFOracleProvider添加到Machine.config中.

第一步,找到Machine.config文件.該文件的位置在:

系統盤符號:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG下面.

第二步,用打開Machine.config文件,在DbProviderFactories配置節點,增長EFOracleProvider的配置,以下所示:

<DbProviderFactories>

      <add name="OdbcData Provider" invariant="System.Data.Odbc"description=".Net Framework Data Provider for Odbc"type="System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />

      <addname="OleDb Data Provider" invariant="System.Data.OleDb"description=".Net Framework Data Provider for OleDb"type="System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />

      <addname="OracleClient Data Provider"invariant="System.Data.OracleClient" description=".Net FrameworkData Provider for Oracle"type="System.Data.OracleClient.OracleClientFactory,System.Data.OracleClient, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />

      <add name="SqlClientData Provider" invariant="System.Data.SqlClient"description=".Net Framework Data Provider for SqlServer"type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />

      <addname="Microsoft SQL Server Compact Data Provider"invariant="System.Data.SqlServerCe.3.5" description=".NETFramework Data Provider for Microsoft SQL Server Compact"type="System.Data.SqlServerCe.SqlCeProviderFactory,System.Data.SqlServerCe, Version=3.5.0.0,Culture=neutral, PublicKeyToken=89845dcd8080cc91" />

     <addname="EF Oracle Data Provider" invariant="EFOracleProvider"description="EF Provider for Oracle"type="EFOracleProvider.EFOracleProviderFactory,EFOracleProvider, Version=1.0.0.0,Culture=neutral, PublicKeyToken=def642f226e0e59b"/>

</DbProviderFactories>

第三步,保存便可.

 

接下來,簡單的介紹一下,如何使用這個EFOralceProvider.

第1步:在命令行窗口,將目錄定位到提示符, 系統盤符:\WINDOWS\Microsoft.NET\Framework\v3.5.以下所示(是我電腦上的目錄):

 

第2步,輸入相應的生成參數.以下圖所示:

 

「data source=test;user id=xray;password=123」成你本身的對應的參數便可.

肯定以後,就能夠看到生成的結果了,同時會有寫信息出來,以下示:

 

至此,EdmGen就爲咱們生成須要的文件.生成的文件以下所示:

l           TestEFModel.csdl

l           TestEFModel.msl

l           TestEFModel.ssdl

l           TestEFModel.ObjectLayer.cs

l           TestEFModel.Views.cs

而後,經過EdmGen2工具, 使用剛剛生成的TestEFModel .csdl, TestEFModel .msl, TestEFModel .ssdl三個文件來生成一個模型.

命令以下:

Edmgen2.exe /toedmx TestEFModel.csdlTestEFModel.msl TestEFModel.ssdl

肯定以後,該工具就會爲咱們生成一個TestEFModel.edmx文件了.

而後,把這個文件加入到咱們的項目中,同時修改項目的App.Config文件鏈接字符串,以下所示:

<?xmlversion="1.0" encoding="utf-8" ?>

<configuration>

 <connectionStrings>

   <add name="NorthwindEFModelContext"

         connectionString="provider=EFOracleProvider;

                          metadata=res://*/TestEFModel.csdl|res://*/TestEFModel.ssdl|res://*/TestEFModel.msl;

                          Provider Connection String='data source=test;user id=xray;password=1111'"

         providerName="System.Data.EntityClient"/>

 </connectionStrings>

</configuration>

最後保存.

至此,修改EFOracleProvider並使用,介紹完畢。

最後提供幾個鏈接,以方便你們學習研究:

l           Code.MSDN上的EFOracleProvider

l           通過我修改後,支持Oracle9i的EFOracleProvider.dll

l           EdmGen2.exe 

 

Entity Framework 學習高級篇1—改善EF代碼的方法(上)

 

本節,咱們將介紹一些改善EF代碼的相關方法,如NoTracking,GetObjectByKey, Include等。

l         MergeOption.NoTracking

當咱們只須要讀取某些數據而不須要刪除、更新的時候,能夠指定使用MergeOption.NoTracking的方式來執行只讀查詢(EF默認的方式是AppendOnly)。當指定使用NoTracking來進行只讀查詢時,與實體相關的引用實體不會被返回,它們會被自動設置爲null。所以,使用NoTracking能夠提高查詢的性能。示例代碼以下:

[Test]

        public voidNoTrackingTest()

        {

           using (var db =new NorthwindEntities1())

           {

//針對Customers查詢將使用MergeOption.NoTracking

               db.Customers.MergeOption = MergeOption.NoTracking;

               var cust = db.Customers.Where(c => c.City =="London");

               foreach (var c in cust)

                   Console.WriteLine(c.CustomerID);

 

               //也能夠這樣寫

               //var cust1 =((ObjectQuery<Customers>)cust).Execute(MergeOption.NoTracking);

 

               //Esql寫法

               //string esql = "select value c from customersas c where c.CustomerID='ALFKI'";

               //db.CreateQuery<Customers>(esql).Execute(MergeOption.NoTracking).FirstOrDefault();

 

           }

        }

 

l         GetObjectByKey/First

GetObjectByKey

在EF中,使用GetObjectByKey方法獲取數據時,它首先會查詢是否有緩存,若是有緩存則從緩存中返回須要的實體。若是沒有則查詢數據庫,返回須要的實體,並添加在緩存中以便下次使用。

First: 總從數據庫中提取須要的實體。

所以,咱們應在合適的地方選擇GetObjectByKey方法來獲取數據,以減小對數據庫的訪問提高性能。示例代碼以下:

[Test]

        public voidGetByKeyTest()

        {

           using (var db =new NorthwindEntities1())

           {

               //從數據庫中提取數據

               var cst = db.Customers.First(c =>c.CustomerID == "ALFKI");

               Console.WriteLine(cst.CustomerID);

 

               //將從緩存中提取數據

               EntityKey key = newEntityKey("NorthwindEntities1.Customers","CustomerID", "ALFKI");

               var cst1 = db.GetObjectByKey(key) as Customers;

               Console.WriteLine(cst1.CustomerID);

 

 

           }

        }

此外,須要注意的是若是GetObjectByKey沒有獲取到符合條件的數據,那麼它會拋異常。爲了不此狀況發生,在有可能出現異常的地方,咱們應該使用TryGetObjectByKey方法。TryGetObjectByKey方法獲取數據的方式和GetObjectByKey相似,只是當沒有取到符合條件的數據時,TryGetObjectByKey會返回null而不是拋異常。示例代碼以下:

[Test]

        public voidTryGetByKeyTest()

        {

           using (var db =new NorthwindEntities1())

           {

 

               //沒有符合條件的數據會有異常拋出

               EntityKey key = newEntityKey("NorthwindEntities1.Customers","CustomerID", "♂風車車.Net");

               var cst = db.GetObjectByKey(key) as Customers;

               Console.WriteLine(cst.CustomerID);

 

               //沒有符合條件的數據會有返回null

               EntityKey key1 = newEntityKey("NorthwindEntities1.Customers","CustomerID", "♂風車車.Net");

               Object cst1 = null;

               db.TryGetObjectByKey(key1, out cst1);

               if (cst1 != null)

                   Console.WriteLine(((Customers)cst1).CustomerID);

 

           }

        }

 

l         First /FirstOrDefault

First: 當咱們使用First來獲取數據,若是沒有符合條件的數據,那麼咱們的代碼將會拋出異常。

FirstOrDefault: 當咱們使用FirstOrDefault來獲取的數據,若是沒有符合條件的數據,那麼它將返回null。

顯然,對於一個良好的代碼,是對能夠預見的異常進行處理,而不是等它本身拋出來。示例代碼以下:

[Test]

        public voidFirstTest()

        {

           using (var db =new NorthwindEntities1())

           {

 

               //拋異常的代碼

               var cst = db.Customers.First(c =>c.CustomerID == "♂風車車.Net");

               Console.WriteLine(cst.CustomerID);//此處將出拋異常

               

               //推薦的使用以下代碼:

               var cst1 = db.Customers.FirstOrDefault(c =>c.CustomerID == "♂風車車.Net");

               if (cst1 != null)

                   Console.WriteLine(cst1.CustomerID);

           }

        }

l         延遲加載/Include

EF不支持實體的部分屬性延遲加載,但它支持實體關係的延遲加載。默認狀況,實體的關係是不會加載。以下代碼:

[Test]

        public voidIncludeTest()

        {

           using(var db = new NorthwindEntities1())

           {

              var csts = db.Customers;

               foreach (var c in csts)

               {

                   Console.WriteLine(c.CustomerID);

                   foreach (var o in c.Orders)

                       Console.WriteLine("  " + o.OrderID);

               }

           }

        }

上述代碼中,由於Orders沒有被加載,因此在輸出Orders的時候,是不會有任何輸出的。

當咱們須要加載某些關聯的關係時,但是用Include方法,以下代碼所示:

[Test]

        public voidIncludeTest()

        {

           using (var db =new NorthwindEntities1())

           {

              var csts = db.Customers.Include("Orders");

               foreach (var c in csts)

               {

                   Console.WriteLine(c.CustomerID);

                   foreach (var o in c.Orders)

                       Console.WriteLine("  " + o.OrderID);

               }

           }

        }

上述代碼中,Customers關聯的Orders將被加載。

 

Entity Framework 學習高級篇2—改善EF代碼的方法(下)

 

本節,咱們將介紹一些改善EF代碼的方法,包括編譯查詢、存儲模型視圖以及衝突處理等內容。

l         CompiledQuery

提供對查詢的編譯和緩存以供從新使用。當相同的查詢須要執行不少遍的時候,那麼咱們可使用ComplieQuery將查詢的語句進行編譯以便下次使用,這樣能夠免去對同一語句的屢次處理,從而改善性能。

示例代碼以下:

[Test]

        public voidComplieTest()

        {

           using (var db =new NorthwindEntities1())

           {

               //對查詢進行編譯

               var customer = CompiledQuery.Compile<NorthwindEntities1, IQueryable<Customers>>(

                   (database) => database.Customers.Where(c => c.City == "London"));

               

               //執行20次相同的查詢

               for (int i = 0;i < 20; i++)

               {

                   DateTime dt = System.DateTime.Now;

                   foreach (var c in customer(db))

                       Console.WriteLine(c.CustomerID);

                   Console.WriteLine(DateTime.Now.Subtract(dt).TotalMilliseconds);

                   Console.WriteLine("---------------------------------------------------");

               }

 

            }

   }

l         存儲模型視圖

在EF中,當執行實體查詢的時候,運行時首先將實體模型轉換成ESQL視圖,而ESQL視圖則是根據msl文件來生成相應的代碼。此外,ESQL視圖包含了相應的查詢語句。ESQL視圖被建立後將在應用程序域中進行緩存以便下次使用。這個運行時生成存儲模型視圖是比較耗時的過程。

爲了,免去運行時生成存儲模型視圖,咱們能夠預先產生這個的存儲模型視圖。具體步驟以下:

首先,使用EdmGen2來產生存儲模型視圖,相應的命令以下:

Edmgen2 /ViewGen cs NorthwindEntites.edmx

執行此命令後,edmgen2會在當前目錄下生成一個名爲NorthwindEntites.GeneratedViews.cs這個文件,就是咱們要使用的存儲模型視圖文件。

將此文件添加到項目中就行,其餘的代碼不須要改變,EF會自動調用此視圖文件。以下示例代碼:

[Test]

        public voidViewTest()

        {

           using (var db =new NorthwindEntities1())

           {

               var suppliers = db.Suppliers;

               foreach (var s in suppliers)

                   Console.WriteLine(s.ContactName);

           }

        }

沒有使用存儲模型視圖的狀況是:

1 passed, 0 failed, 0 skipped, took 7.09seconds.

項目中添加了NorthwindEntites.GeneratedViews.cs文件,執行狀況是:

1 passed, 0 failed, 0 skipped, took 5.38seconds.

可見,使用了存儲模型視圖的確是提升了性能。

l         衝突處理

在EF中,默認狀況並不會檢查併發衝突。由於EF實現的是樂觀的併發模式,當有併發的衝突發生時,將會拋出Optimistic Concurrency Exception異常。咱們能夠經過使用RefreshMode這個枚舉來指定當發生衝突時如何處理。

RefreshMode有兩中枚舉值:

ClientsWins: 當提交修改,更新數據庫中的值。

StoreWins: 放棄修改,使用數據庫中的值。

示例代碼片斷以下:

var db2 = new NorthwindEntities1();

           var customer2 = db2.Customers.FirstOrDefault(c=> c.CustomerID == "2009");

           if (customer2 != null)

           {

               customer2.ContactName = "♂風車車.Net";

               customer2.City = "CD";

               customer2.Region = "GX";

           }

           try

           {

               db2.SaveChanges();

           }

           catch (OptimisticConcurrencyExceptionex) //捕獲到衝突,則進行相應的處理

           {

               db2.Refresh(RefreshMode.ClientWins,customer2);

               db2.SaveChanges();

           }

上述代碼片斷,只是說明怎麼處理併發衝突,不是具體的併發。(ps:原本是準備開個線程來模擬併發的,可是始終沒成功,沒明白什麼緣由,望高人指點呢!)

 

Entity Framework 學習結束語

 

前一段時間,一直在學習EF.對於這個EF多少算是有點了解吧. 目前,對於EF這個學習系列的文章,就暫時寫到這裏.固然還有一些相關的知識沒有介紹,請各位朋友見諒!很樂意和你們交換關於EF學習的心得或技巧.後續,關於EF塊東西,我仍是會隨時關注着,若是有什麼好的心得或文章,我也會盡可能拿出來給你們分享!每次看到一些朋友的相關留言討論,我都無比的興奮,有點小小成就感!

       最後,感謝各位朋友的熱情關注和支持!

 

     本文版權歸做者全部,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。

相關文章
相關標籤/搜索