<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      LINQ之路 9:LINQ to SQL 和 Entity Framework(上)

      在上一篇中,我們從理論和概念上詳細的了解了LINQ的第二種架構“解釋查詢”。在這接下來的二個篇章中,我們將使用LINQ to SQL和Entity Framework來實踐“解釋查詢”,學習這些技術的關鍵特性。在本系列文章中,我不準備事無巨細的討論LINQ to SQL和Entity Framework的方方面面,畢竟那樣需要太多的篇幅,也會讓我們從LINQ上面轉移注意力,況且,園子里也有不少介紹LINQ to SQL和Entity Framework的好文章。我們在此關注的是LINQ to SQL和Entity Framework中的”LINQ”部分,并會比較這兩種技術的相同和不同之處。通過我們之前介紹的LINQ知識還有將來會討論的更多LINQ Operators,相信閱者能針對LINQ to SQL和Entity Framework寫出優(yōu)雅高效的查詢。為了簡單清晰,文中有些地方對LINQ to SQL和Entity Framework進行了縮寫,分別為:L2S和EF。

      LINQ to SQL和Entity Framework之關聯

      LINQ to SQL和Entity Framework都是一種包含LINQ功能的對象關系映射技術。他們之間的本質區(qū)別在于EF對數據庫架構和我們查詢的類型實行了更好的解耦。使用EF,我們查詢的對象不再是完全對應數據庫架構的C#類,而是更高層的抽象:Entity Data Model。這為我們提供了額外的靈活性,但是在性能和簡單性上面也會有所損失。

      LINQ to SQL由C#團隊開發(fā)并在.NET Framework 3.5中發(fā)布,而Entity Framework由ADO.NET團隊開發(fā)并作為.NET Framework 3.5 Service Pack 1的一部分發(fā)布。此后,LINQ to SQL由ADO.NET團隊接手,其結果是:在.NET 4.0中,ADO.NET團隊更加專注于EF的改進,相對來說,LINQ to SQL的改進要小得多。

      LINQ to SQL和Entity Framework各有所長,LINQ to SQL是一個輕量級的ORM框架,旨在為Microsoft SQL Server數據庫提供快速的應用程序開發(fā),其優(yōu)點是易于使用、簡單、高性能。而Entity Framework的優(yōu)點在于:其為創(chuàng)建數據庫架構和實體類之間的映射提供了更好的靈活性,它還通過提供程序支持除了SQL Server之外的第三方數據庫。

      EF 4.0一個非常受歡迎的改進是它現在支持與LINQ to SQL幾乎同樣的查詢功能。這意味著我們在系列文章中的LINQ-to-db查詢可以同時適用于EF 4.0和L2S。而且,這也使得L2S成為我們學習使用LINQ查詢數據庫的理想技術,因為其保持了對象關系方面的簡單性,并且我們學習到的查詢原則和技術同樣適用于EF。

      LINQ to SQL實體類

      L2S 允許我們使用任何類來表示數據,只要我們?yōu)轭愄砑恿撕线m的Attribute(特性)裝飾,比如:

          [Table]
      public class Customer
      {
      [Column(IsPrimaryKey = true)]
      public int ID;

      [Column]
      public string Name;
      }

      [Table] 特性定義在System.Data.Linq.Mapping名字空間中,它告訴L2S該類型的對象代表了數據庫表里的一行數據。默認情況下,它假設表名和類名相同,當他們不同時,我們就可以指定具體的表名,如下:

          [Table (Name="Customers")]

      L2S把這種經過[Table]特性裝飾的類成為實體類。一個實體類的結構必須匹配它表示的數據庫表,這樣才能生成可以正確執(zhí)行的SQL腳本。

      [Column] 特性指定一個字段或屬性映射到數據庫表的一列,如果列名與字段名/屬性名不相同,我們可以指定具體的映射列名:

              [Column(Name = "FullName")]
      public string Name;

      我們可以在[Column]特性中指定IsPrimaryKey屬性表示該列為表的主鍵,這對于保持對象標識、往數據庫寫入更新是必須的。

      除了直接定義public字段,我們也可以定義private字段和public屬性,這樣我們就能在屬性存取時加入驗證邏輯。此時,為了性能考慮,我們可以告訴L2S當從數據庫存取數據時,繞過屬性存取器而直接將值寫入private字段。當然,前提是我們認為數據庫中的值是正確的,不需要經過屬性存取器中的驗證邏輯。

              private string name = string.Empty;

      // Column(Storage = "name") 告訴L2S當從數據庫生成實體時直接將數據寫入name字段,而不通過set訪問器
      [Column(Storage = "name")]
      public string Name
      {
      get { return name; }
      set { if(value.Length > 5) name = value; }
      }

      可以看到,在使用LINQ to SQL時,我們首先要參照數據庫的結構來創(chuàng)建各種必須的實體類,這當然不是一種令人愉快的事情。好在,我們可以通過Visual Studio(新增一個”LINQ to SQL Classes” Item)或SqlMetal命令行工具來自動生成實體類。

      Entity Framework實體類

      和LINQ to SQL一樣,Entity Framework允許我們使用任何類來表示數據(盡管我們必須實現特定的接口來完成諸如導航屬性等功能)。比如,下面的EF實體類表示一個customer,它被映射到數據庫的customer表:

          [EdmEntityType (NamespaceName="EFModel", Name="Customer")]
      public partial class Customer
      {
      [EdmScalarProperty(EntityKeyProperty = true, IsNullable= false )]
      public int ID { get; set; }

      [EdmScalarProperty(EntityKeyProperty = false, IsNullable = false)]
      public string Name { get; set; }
      }

      但和L2S不同的是,這樣的一個類并不能獨立工作。記住:在使用EF時,我們并不是直接查詢數據庫,而是查詢一個更高層的模型,該模型叫做Entity Data Model(EDM)。所以我們需要某種方法來描述EDM,這通常是由一個以.edmx為擴展名的XML文件來完成的,它包含了一下三個部分:

      • 概念模型,用來描述EDM并且和數據庫完全隔離
      • 存儲模型,用來描述數據庫架構
      • 映射規(guī)范,用來描述概念模型如何映射到存儲模型

      創(chuàng)建一個.edmx文件的最簡單方法是在Visual Studio中添加一個”ADO.NET Entity Data Model” 項目,然后按照向導提示來從數據庫生成EDM。這種方法不但生成了.edmx文件,還為我們生成了實體類,EF中的實體類對應EDM的概念模型。

      設計器為我們生成的EDM初始時包含了表和實體類之間簡單的1:1映射關系,當然,我們可以通過設計器或編輯.edmx文件來調整我們的EDM。下面就是我們可以完成的一些工作:

      • 映射多個表到一個實體
      • 映射一個表到多個實體
      • 通過ORM領域流行的三種標準策略來映射繼承的類型

      這三種繼承策略包括:

      • 表到層次類型(Table per hierarchy):單個表映射到一個完整的類繼承層次結構。表中的一個類型辨別列用來指示每一行數據應該映射到何種類型。
      • 表到類型(Table per type):單個表映射到單個類型,這意味著繼承類型會被映射到多個表。當我們查詢一個entity時,EF通過生成SQL JOIN來合并所有的基類型。
      • 表到具體類型(Table per concrete type):單獨的表映射到每個具體類型,這意味著一個基類型映射到多個表,當我們查詢基類型的entity時,EF會生成SQL UNION來合并數據。

      DataContext和ObjectContext

      一旦我們定義好了實體類(EF還需定義EDM),我們就可以開始使用LINQ進行查詢了。第一步就是通過制定連接字符串來初始化一個DataContext(L2S)或ObjectContext(EF)。

                  var l2sContext = new DataContext("database connection string");
      var efContext = new ObjectContext("entity connection string");

      需要了解的是,直接初始化DataContext/ObjectContext是一種底層的訪問方式,但它很好的說明了這些類的工作方式。通常情況下,我們會使用類型化的context(通過繼承DataContext/ObjectContext),詳細情況稍后就會討論。

      對于L2S,我們傳入數據庫連接字符串;而對于EF,我們需要傳入實體(entity)連接字符串,它同時包含了數據庫連接字符串和查找EDM的額外信息。如果你通過Visual Studio創(chuàng)建了EDM,你會在app.config文件中找到針對該EDM的實體連接字符串,比如:

        <connectionStrings>
      <add name="testEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=localhost;initial catalog=test;integrated security=True;multipleactiveresultsets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient"/>
      </connectionStrings>

      之后我們就可以通過調用GetTable(L2S)或CreateObjectSet(EF)來獲得查詢對象。下面的示例使用了我們上面創(chuàng)建的Customer實體類:

                  var context = new DataContext("Data Source=LUIS-MSFT; Initial Catalog=test; Integrated Security=SSPI;");
      Table<Customer> customers = context.GetTable<Customer>();

      Console.WriteLine(customers.Count()); // 表中的行數
      Customer cust = customers.Single(c => c.ID == 1); // 獲取ID為1的Customer

      下面是EF中的等價代碼,可以看到,除了Context的構建和查詢對象的獲取有所不同,后面的LINQ查詢都是一樣的:

                  var context = new ObjectContext(ConfigurationManager.ConnectionStrings["testEntities"].ConnectionString);
      context.DefaultContainerName = "testEntities";
      ObjectSet<Customer> customers = context.CreateObjectSet<Customer>();

      Console.WriteLine(customers.Count()); // 表中的行數
      Customer cust = customers.Single(c => c.ID == 1); // 獲取ID為1的Customer

      一個DataContext/ObjectContext對象有兩個作用。其一是工廠作用,我們通過它來創(chuàng)建查詢對象,另外,它會跟蹤我們對entity所做的任何修改,所以我們可以把修改結果保存到數據庫。下面的代碼就是更新customer的示例:

                  // Update Customer with L2S
      Customer cust = customers.OrderBy(c => c.Name).First();
      cust.Name = "Updated Name";
      context.SubmitChanges();

      // Update Customer with EF, Calling SaveChanges instead
      Customer cust = customers.OrderBy(c => c.Name).First();
      cust.Name = "Updated Name";
      context.SaveChanges();

       

      強類型contexts

      任何時候都去調用GetTable<>()或CreateObjectSet<>()并不是一件讓人愉快的事情,一個更好的方式是為特定的數據庫創(chuàng)建DataContext/ObjectContext的子類,在子類中為各個entity添加屬性,這就是強類型的context,如下:

          class LifePoemContext : DataContext
          {
      public LifePoemContext(string connectionString) : base(connectionString) { }

      public Table<Customer> Customers
      {
      get { return GetTable<Customer>(); }
      }

      //... 為其他table創(chuàng)建相應的屬性
      }

      // Same thing for EF
      class LifePoemContext : ObjectContext
          {
      public LifePoemContext(EntityConnection connection) : base(connection) { }

      public ObjectSet<Customer> Customers
      {
      get { return CreateObjectSet<Customer>(); }
      }

      //... 為其他table創(chuàng)建相應的屬性
      }

      之后,我們就可以通過使用屬性來寫出更加簡潔優(yōu)雅的代碼了:

                  var context = new LifePoemContext("database connection string");
      Console.WriteLine(context.Customers.Count());

      如果你是使用Visual Studio來創(chuàng)建”LINQ to SQL Classes”或”ADO.NET Entity Data Model”,它會自動為我們生成強類型的context。設計器同時還會完成其他的工作,比如對標識符使用復數形式,在我們的例子中,它是context.Customers而不是context.Customer,即使SQL表名和實體類都叫Customer。

      對象跟蹤/Object tracking

      一個DataContext/ObjectContext實例會跟蹤它創(chuàng)建的所有實體,所以當你重復請求表中相同的行時,它可以給你返回之前已經創(chuàng)建的實體。換句話說,一個context在它的生存期內不會為同一行數據生成兩個實例。你可以在L2S中通過設置DataContext對象的ObjectTrackingEnabled屬性為false來取消這種行為。在EF中,你可以基于每一種類型進行設置,如:context.Customers.MergeOption = MergeOption.NoTracking; 需要注意的是,禁用Object tracking同時也會阻止你想數據庫提交更新。

      為了說明Object tracking,假設一個Customer的名字按字母排序排在首位,同時它的ID也是最小的。那么,下面的代碼,a和b將會指向同一個對象:

                  var context = new testEntities(ConfigurationManager.ConnectionStrings["testEntities"].ConnectionString);
      Customer a = context.Customers.OrderBy(c => c.Name).First();
      Customer b = context.Customers.OrderBy(c => c.ID).First();
      Console.WriteLine(object.ReferenceEquals(a, b)); // output: True

      這會導致幾個有意思的結果。首先,讓我們考慮當L2S或EF在遇到第二個query時到底會發(fā)生什么。它從查詢數據庫開始,然后獲取ID最小的那一行數據,接著就會從該行讀取主鍵值并在context的實體緩存中查找該主鍵。如果找到,它會直接返回緩存中的實體而不更新任何值。所以,如果在這之前其他用戶更新了該Customer的Name,新的Name也會被忽略。這對于防止意外的副作用和保持一致性至關重要,畢竟,如果你更新了Customer對象但是還沒有調用SubmitChanges/SaveChanges,你是不會希望你的更新會被另外一個查詢覆蓋的吧。

      第二個結果是在你不能明確把結果轉換到一個實體類形,因為在你只選擇一行數據的部分列時會引起不必要的麻煩。例如,如果你只想獲取Customer的Name時:

                  // 下面任何一種方法都是可行的
      context.Customers.Select(c => c.Name);
      context.Customers.Select(c => new { Name = c.Name } );
      context.Customers.Select(c => new MyCustomerType { Name = c.Name } );

      // 但下面這種方法會引起麻煩
      context.Customers.Select(c => new Customer { Name = c.Name });

      原因在于Customer實體只是部分屬性被獲取,這樣下一次如果你查詢Customer的所有列時,可是context從緩存中返回的的對象只有部分屬性被賦值。

      關聯/Associations

      實體生成工具還為我們完成了一項非常有用的工作。對于我們定義在數據庫中的每個關聯(relationship),它會在關聯的兩邊添加恰當的屬性,讓我們可以使用關聯來進行查詢。比如,假設Customer和Order表存在一對多的關系:

            Create table Customer
      (
      ID int not null primary key,
      Name varchar(30) not null
      )

      Create table Orders
      (
      ID int not null primary key,
      CustomerID int references Customer (ID),
      OrderDate datetime,
      Price decimal not null
      )

      通過自動生成的實體類形,我們可以寫出如下的查詢:

                  //獲取第一個Custoemr的所有Orders
      Customer cust1 = context.Customers.OrderBy(c => c.Name).First();
      foreach (Order o in cust1.Orders)
      Console.WriteLine(o.Price);

      //獲取訂單額最小的那個Customer
      Order lowest = context.Orders.OrderBy(o => o.Price).First();
      Customer cust2 = lowest.Customer;

      并且,如果cust1和cust2正好是同一個Customer時,他們會指向同一對象:cust1 == cust2會返回true。

      讓我們來進一步查看Customer實體類中自動生成的Orders屬性的簽名:

              // With L2S
      [Association(Name="Customer_Order", Storage="_Orders", ThisKey="ID", OtherKey="CustomerID")]
      public EntitySet<Order> Orders { get {...} set {...} }

      // With EF
      [EdmRelationshipNavigationProperty("testModel", "FK__Orders__Customer__45BE5BA9", "Orders")]
      public EntityCollection<Order> Orders { get {...} set {...} }

      一個EntitySet或EntityCollection就如同一個預先定義的query,通過內置的Where來獲取相關的entities。[Association]特性給予L2S必要的信息來構建這個SQL query;[EdmRelationshipNavigationProperty]特性告知EF到EDM的何處去查找當前關聯的信息。

      和其他類型的query一樣,這里也會采用延遲執(zhí)行。對于L2S,一個EntitySet會在你對其進行枚舉時生成;而對于EF,一個EntityCollection會在你精確調用其Load方法時生成。

      下面是Orders.Customer屬性(位于關聯的另一邊):

              // With L2S
      [Association(Name="Customer_Order", Storage="_Customer", ThisKey="CustomerID", OtherKey="ID", IsForeignKey=true)]
      public Customer Customer { get {...} set {...} }

      盡管屬性類型是Customer,但它底層的字段(_Customer)卻是EntityRef類型的:private EntityRef<Customer> _Customer;  EntityRef實現了延遲裝載(deferred loading),所以直到你真正使用它時Customer才會從數據庫中獲取出來。

      EF以相同的方式工作,不同的是你必需調用EntityReference對象的Load方法來裝載Customer屬性,這意味著EF必須同時公開真正的Customer對象和它的EntityReference包裝者,如下:

              // With EF
      [EdmRelationshipNavigationProperty("testModel", "FK__Orders__Customer__45BE5BA9", "Customer")]
      public Customer Customer { get {...} set {...} }

      public EntityReference<Customer> CustomerReference

      我們也可以讓EF按照L2S的方式來工作,當我們設置如下屬性后,EFEntityCollectionsEntityReference就會自動實現延遲裝載,而不需要明確調用其Load方法。

              context.ContextOptions.LazyLoadingEnabled = true;

      在下一篇LINQ to SQL和Entity Framework(下)中,我們會討論學習這兩種LINQ-to-db技術的更多細節(jié)和值得關注的地方。

      posted @ 2011-11-10 14:03  Life a Poem  閱讀(28345)  評論(30)    收藏  舉報
      主站蜘蛛池模板: 欧美野外伦姧在线观看| 呼玛县| 秋霞电影网| 亚洲国产精品久久久久秋霞影院| 四虎国产精品永久在线看| 精品久久久久中文字幕日本| 日韩精品一区二区三区久| 双乳奶水饱满少妇呻吟免费看 | 丰满老熟妇好大bbbbb| 成人小说亚洲一区二区三区| 国产精品天干天干综合网| 在线观看国产成人av天堂| 无码福利一区二区三区| 亚洲a人片在线观看网址| 人妻内射视频麻豆| 亚洲熟妇熟女久久精品一区| 亚洲中文字幕无码中字| 国产成人精彩在线视频| 日韩高清免费一码二码三码| 亚洲欧洲日产国码高潮αv| 久久久久久久久毛片精品| 日本高清一区免费中文视频| 男人的天堂av社区在线| 92自拍视频爽啪在线观看| 日韩狼人精品在线观看| 精品一区二区三区日韩版| 日本欧美大码a在线观看| 蜜臀av人妻国产精品建身房| 久久97超碰色中文字幕| 99九九视频高清在线| www插插插无码免费视频网站| 九九热在线精品视频九九| 亚洲色成人网站www永久男男| 国产日韩精品欧美一区灰| 国产精品久久久久精品日日| 国产成人人综合亚洲欧美丁香花| 国产精品69人妻我爱绿帽子 | 国产亚洲精品久久久久蜜臀| 中文文字幕文字幕亚洲色| 国产精品香蕉在线观看不卡 | 国产成人a∨激情视频厨房|