日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

[转]EntityFramework走马观花之CRUD(中)

發布時間:2025/3/15 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [转]EntityFramework走马观花之CRUD(中) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

學習Entity Framework技術期間查閱的優秀文章,出于以后方便查閱的緣故,轉載至Blog,可查閱原文:http://blog.csdn.net/bitfan/article/details/13023223

?

如果是獨立的實體對象,在底層數據庫中它對應一張獨立的表,那么,對它進行新建、刪除和修改沒有任何難度,實在不值浪費筆墨在它上頭。

在現實項目中,完全獨立的對象少之又少,絕大多數情況都是對象之間有著緊密的關聯。這種關聯主要分為三種類型:一對一、一對多和多對多。

如果對EF淺嘗輒止,則我幾乎可以肯定你一定會在實際開發中被對象間的關聯弄得焦頭爛額。下面就和大家聊聊EF是如何處理不同對象關聯類型數據更新問題的。

一對一關聯

在面向對象的世界中,使用對象組合實現一對一關聯,這種關聯具有方向性。比如A與B對象間是一對一關聯,如果得到A就能順藤摸瓜得到B,就說A可導航到B,這是單向一對一關聯,如果從A能得到B,從B也能得到A,這就是雙向一對一關聯。

以下代碼中,Teacher與Office間是一對一雙向關聯:

?

public partialclass Teacher{public int TeacherId { get; set; }public string Name { get; set; }public virtual Office Office { get; set; }}public partialclass Office{public int OfficeId { get; set; }public string Address { get; set; }public virtual Teacher Teacher { get; set; }}

?

?

User與Office類間的關聯在數據庫中體現為Teacher和Office兩個表間的一對一關聯。

這里要注意:面向對象世界中實體類的一對一雙向關聯,雙方的地位是“平等”的,但在關系數據庫領域中,每個關聯一定有主有次,下圖為SQL Server中創建關聯的窗體,可以看到一對一關聯兩端分別是主鍵表和外鍵表。

?

?

在數據庫中創建一對一關聯,有兩個很要命的東西,不注意鐵定出問題:

(1)在外鍵表一端,其Key不能是自增標識字段。比如Office作為從表,其主鍵OfficeId就不能是自增的,否則EF會在向Office添加記錄時報告:Adependent property in a ReferentialConstraint is mapped to a store-generatedcolumn.

(2)應該把一對一關聯設置為“級聯刪除”。否則在刪除主鍵表端記錄時如果外鍵表端記錄還存在,則會報錯。

還有另外一個小問題:

數據庫中的一對一關聯,總被EF識別為1 ~ 0..1關聯,即使你使用ModelFirst方式顯式定義Teacher和Office類之間為1 ~ 1關聯,生成數據庫后,再用Database First方式逆向生成數據模型,你會發現Teacher和Office類的關聯變成1 ~ 0..1 !

換句話說,EF實體對象的1對1關聯,總是被張冠李戴為1對0..1關聯。

好了,下面具體說說新建記錄的問題。

要在數據庫中創建一對一關聯實體對象的相關記錄,可以使用以下三種方式:

(1)new 一個Teacher對象,new一個Office對象,用代碼關聯兩者,之后SaveChanges。只要OfficeId列不是自增標識列就能成功。

(2)new 一個Teacher對象,SaveChanges,再new一個Office對象,將其關聯上這個“老”的己存在數據庫中的Teacher對象,SaveChanges,也能成功。

(3)new一個Office對象,直接SaveChanges,這時會報錯。因為Office是從對象,它沒有關聯主對象時,是無法保存到數據庫的。

?

下面看看刪除對象的情形:

(1)刪除Teacher對象,SaveChanges,則它關聯的Office也會被同時刪除,注意這要求啟用“級聯刪除”特性,否則,必須先從DbSet<Office>中移除Office對象,再刪除Teacher,操作才能成功。

(2)直接刪除Office對象,SaveChanges:沒有任何問題。

?

輪到修改一對一關聯的情形了。

假設我們需要給一個Teacher對象“換”另一個“Office”對象(即換辦公室,估計這位老師升官了)。

這有兩種方式實現:

第一種方式很簡單,直接找到Teacher對象所關聯的Office對象,修改它的屬性為新值,之后SaveChanges即可,這本質上是“舊房改造”。

另一種方式是“直接分配新房”: 先new一個Office對象代表新辦公室,再把它關聯上Teacher,如下所示:

?

using (varcontext = new EFModelFirstDbEntities()){//查找要換辦公室的Teacher對象 Teacher teacher = context.Teachers.Include("Office").First();teacher.Office = new Office{Address = "Bit" +ran.Next(1, 100)}; context.SaveChanges();}

?

這里有一個細節很重要,在提取Teacher對象時,一定要使用Include方法把它相關聯的“Office”對象也裝入進來。否則,在SaveChanges時,EF報告:

違反了 PRIMARY KEY 約束“PK_Office”。不能在對象“dbo.Office”中插入重復鍵。

?如果確實不想Include,則可以顯式指示EF把老的Office對象狀態設置為“刪除”:

?

Teacher teacher =context.Teachers.First();context.Entry(teacher.Office).State = EntityState.Deleted;teacher.Office = new Office{Address = "Bit" +ran.Next(1, 100)};context.SaveChanges();

?

?

有的朋友可能會推測EF會生成兩條SQL命令,一條是Delete,另一條是Insert,但我最終測試的結果可能會讓你吃驚:

上述兩種方式,最后生成的都是一條Update命令。

這就是說,這兩種實現方式沒有本質差別。

發發感觸:開發中有很多細節,光看資料不動手是發現不了的。光看書不實踐能真正掌握技術?那是神話!開發實踐是學習軟件技術,提升自身能力的硬道理。

一對多關聯

有了一對一關聯的基礎,把握一對多關聯就簡單多了。

我選取“Book(書)”和“BookReview(書評)”作為一對多關聯的示例,并在數據庫中為關聯啟用了“級聯刪除”特性:

?

?

如果使用Db First方式,可以得到以下實體類代碼:

?

public partial class Book{public Book(){this.BookReviews = new HashSet<BookReview>();}public int BookId { get; set; }public string BookName { get; set; }public virtual ICollection<BookReview> BookReviews { get; set; }}public partial class BookReview{public int BookReviewId { get; set; }public string Reader { get; set; }public string Content { get; set; }public int BookId { get; set; }public virtual Book Book { get; set; }}

?

上述代碼中,最值得注意的就是Book類的BookReviews屬性是HashSet。

?

先看新建記錄的情況:

對于一對多關聯,new一個Book對象,SaveChanage,之后,就可以向其BookReviews集合屬性中添加新書評對象。

這很簡單,不用多說。刪除記錄的情況就有點復雜。

?如果要刪除單個的Book對象,由于啟用了級聯刪除,干掉一個Book,它所關聯的所有BookReview也一并刪除了。

如果想刪除單個書評,如果使用DB_First方式,Visual Studio生成的實體對象集合其類型為ICollection<T>,實際上是一個普通的HashSet<T>集合對象,不具備跟蹤對象狀態的功能。因此,在刪除單個對象時,需要顯式設置其狀態為EntityState.Deleted,否則,刪除將失敗:

?

using(var context=newEFModelFirstDbEntities()){Book book =context.Books.First();BookReview reviewToBeDelete = book.BookReviews.FirstOrDefault();context.Entry(reviewToBeDelete).State =EntityState.Deleted;book.BookReviews.Remove(reviewToBeDelete);context.SaveChanges();}

?

?

更簡單的方式是直接從DbSet中移除,這是推薦的方式

?

Book book = context.Books.First();BookReview reviewToBeDelete =book.BookReviews.FirstOrDefault();context.BookReviews.Remove(reviewToBeDelete);context.SaveChanges();

?

?

另一個在實踐中遇到的問題是”批量刪除”。

?

如果想刪除某本書的全部書評,像下面這么干是不行的,運行時將拋出異常:

?

foreach (var review in book.Reviews){context.BookReviews.Remove(review);}

?

?

這是因為循環迭代訪問集合的過程中不允許修改集合。

然而,可以使用ToList()方法將DbSet()中的實體集合提取為內存集合,然后foreach訪問時,從“原始對象集合”中移除:

?

var reviewList = book.Reviews.ToList();foreach (var review in reviewList){context.BookReviews.Remove(review);}

?

?

這里reviewList和context.BookReviews是兩個不同的集合對象,在foreach訪問reviewList時,可以方便地移除BookReviews中的對象。

上述兩個“不同”的集合,實際引用“相同的”一堆BookReview對象,不信?

設斷點讓程序暫停,以下在VisualStudio“立即窗口(Immediate Windows)”測試可以證明我沒有說謊:

?

?reviewList==book.Reviewsfalse?reviewList[0]==book.Reviews[0]True

?

?

關于批量刪除,默認情況下,EF會為每個刪除的實體對象生成一條Delete命令,當刪除大量實體時,這有可能帶來性能問題。在這種情況下,最好的解決方案是直接向數據庫發送SQL命令:

?

Book book = context.Books.First();context.Database.ExecuteSqlCommand( "Delete from BookReview where BookId={0}" , book.BookId);

?

?

一個SQL命令搞掂,簡單高效。可以收工,回家睡覺去也!

?關于一對多關聯,還有一個問題需要說說,那就是“如何在兩個實體對象間移動子對象”。實現起來也簡單:

先從源實體對象中先Remove掉子對象,再Add到目標對象中即可。

以下示例代碼把第一本書的第一條書評移到第二本書下:

?

Book book1 = context.Books.First();Book book2 = context.Books.OrderBy(b =>b.BookId).Skip(1).First();BookReview review = book1.BookReviews.First();book1.BookReviews.Remove(review);book2.BookReviews.Add(review);context.SaveChanges();

與一對一關聯的情況類似,上述代碼將只生成一個update命令。

多對多關聯

多對多關聯的最典型實例就是軟件權限管理系統中的“用戶(User)”與“角色(Role)”。一個用戶可以擁有多種角色,一個角色可以包容多個用戶。

?

對于實體對象間的多對多關聯,EF code First自動地在數據庫層將其拆成兩個一對多關聯,變成以下這個模樣,并為這兩個關聯啟用“級聯刪除”:

?

?

多對多關聯對象的新建和修改,可以參照一對多的情形來處理。比如要刪除單個的User或Role對象,很簡單,直接從DbSet中移除它們,再SaveChange即可。

不再多說,大家自己回去編寫實驗代碼。

比較麻煩的是涉及到多對多關聯本身的添加與刪除問題。典型的例子是:

把某個User加入到特定的Role的User集合中,或者從Role的User集合中移除某個User。

對于上述這些情況,EF將不動User表和Role表,只在中間表RoleUsers中動手腳。

比如要從User與Role多對多關聯任一方的對象集合導航屬性中移除某個(或某些)User或Role對象,然后SaveChanges,對于這種操作,EF默認不會修改User與Role實體對象的狀態,只是在底層數據庫的中間表RoleUsers中刪除了相應的記錄,使它們“斷開關聯”。

理解這點很關鍵。

這里還有一個細節:

如果在SaveChange之后,涉及到的相關實體對象(User和Role)都還需要繼續使用,則一定要注意:你僅從一方集合導航屬性中移除了某對象,另一方的集合導航屬性中對此對象的引用是還在的,這有可能帶來不一致的問題。只有等到下一次從數據庫中裝入數據時,才能重回一致。

請看以下代碼:

using (varcontext = new EFCodeFirstDbContext()){User u =context.Users.Find(userId);Role r =context.Roles.Find(roleId);if (u != null && r !=null){//以下兩句,任一種方式均可行 r.Users.Remove(u);//u.Roles.Remove(r);int result =context.SaveChanges();Console.WriteLine("將用戶從角色中移除操作完畢。返回值:" + result);}}

?

上述加粗的兩句,不管寫哪一句,SaveChanges之后,都能達到在底層數據庫的RoleUsers表中刪除相應記錄的功能。但如果只寫了一句,則Role對象的Users集合中確實移除了指定的User對象,但此User對象的Roles屬性中還包容有這個Role對象,這就是數據不一致的情況。因此,最安全的寫法是兩句都寫。

總結

以上是生活随笔為你收集整理的[转]EntityFramework走马观花之CRUD(中)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。