日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2025/3/15 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [转]EntityFramework走马观花之CRUD(中) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

學(xué)習(xí)Entity Framework技術(shù)期間查閱的優(yōu)秀文章,出于以后方便查閱的緣故,轉(zhuǎn)載至Blog,可查閱原文:http://blog.csdn.net/bitfan/article/details/13023223

?

如果是獨(dú)立的實(shí)體對(duì)象,在底層數(shù)據(jù)庫(kù)中它對(duì)應(yīng)一張獨(dú)立的表,那么,對(duì)它進(jìn)行新建、刪除和修改沒(méi)有任何難度,實(shí)在不值浪費(fèi)筆墨在它上頭。

在現(xiàn)實(shí)項(xiàng)目中,完全獨(dú)立的對(duì)象少之又少,絕大多數(shù)情況都是對(duì)象之間有著緊密的關(guān)聯(lián)。這種關(guān)聯(lián)主要分為三種類型:一對(duì)一、一對(duì)多和多對(duì)多。

如果對(duì)EF淺嘗輒止,則我?guī)缀蹩梢钥隙阋欢〞?huì)在實(shí)際開(kāi)發(fā)中被對(duì)象間的關(guān)聯(lián)弄得焦頭爛額。下面就和大家聊聊EF是如何處理不同對(duì)象關(guān)聯(lián)類型數(shù)據(jù)更新問(wèn)題的。

一對(duì)一關(guān)聯(lián)

在面向?qū)ο蟮氖澜缰?#xff0c;使用對(duì)象組合實(shí)現(xiàn)一對(duì)一關(guān)聯(lián),這種關(guān)聯(lián)具有方向性。比如A與B對(duì)象間是一對(duì)一關(guān)聯(lián),如果得到A就能順藤摸瓜得到B,就說(shuō)A可導(dǎo)航到B,這是單向一對(duì)一關(guān)聯(lián),如果從A能得到B,從B也能得到A,這就是雙向一對(duì)一關(guān)聯(lián)。

以下代碼中,Teacher與Office間是一對(duì)一雙向關(guān)聯(lián):

?

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類間的關(guān)聯(lián)在數(shù)據(jù)庫(kù)中體現(xiàn)為Teacher和Office兩個(gè)表間的一對(duì)一關(guān)聯(lián)。

這里要注意:面向?qū)ο笫澜缰袑?shí)體類的一對(duì)一雙向關(guān)聯(lián),雙方的地位是“平等”的,但在關(guān)系數(shù)據(jù)庫(kù)領(lǐng)域中,每個(gè)關(guān)聯(lián)一定有主有次,下圖為SQL Server中創(chuàng)建關(guān)聯(lián)的窗體,可以看到一對(duì)一關(guān)聯(lián)兩端分別是主鍵表和外鍵表。

?

?

在數(shù)據(jù)庫(kù)中創(chuàng)建一對(duì)一關(guān)聯(lián),有兩個(gè)很要命的東西,不注意鐵定出問(wèn)題:

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

(2)應(yīng)該把一對(duì)一關(guān)聯(lián)設(shè)置為“級(jí)聯(lián)刪除”。否則在刪除主鍵表端記錄時(shí)如果外鍵表端記錄還存在,則會(huì)報(bào)錯(cuò)。

還有另外一個(gè)小問(wèn)題:

數(shù)據(jù)庫(kù)中的一對(duì)一關(guān)聯(lián),總被EF識(shí)別為1 ~ 0..1關(guān)聯(lián),即使你使用ModelFirst方式顯式定義Teacher和Office類之間為1 ~ 1關(guān)聯(lián),生成數(shù)據(jù)庫(kù)后,再用Database First方式逆向生成數(shù)據(jù)模型,你會(huì)發(fā)現(xiàn)Teacher和Office類的關(guān)聯(lián)變成1 ~ 0..1 !

換句話說(shuō),EF實(shí)體對(duì)象的1對(duì)1關(guān)聯(lián),總是被張冠李戴為1對(duì)0..1關(guān)聯(lián)。

好了,下面具體說(shuō)說(shuō)新建記錄的問(wèn)題。

要在數(shù)據(jù)庫(kù)中創(chuàng)建一對(duì)一關(guān)聯(lián)實(shí)體對(duì)象的相關(guān)記錄,可以使用以下三種方式:

(1)new 一個(gè)Teacher對(duì)象,new一個(gè)Office對(duì)象,用代碼關(guān)聯(lián)兩者,之后SaveChanges。只要OfficeId列不是自增標(biāo)識(shí)列就能成功。

(2)new 一個(gè)Teacher對(duì)象,SaveChanges,再new一個(gè)Office對(duì)象,將其關(guān)聯(lián)上這個(gè)“老”的己存在數(shù)據(jù)庫(kù)中的Teacher對(duì)象,SaveChanges,也能成功。

(3)new一個(gè)Office對(duì)象,直接SaveChanges,這時(shí)會(huì)報(bào)錯(cuò)。因?yàn)镺ffice是從對(duì)象,它沒(méi)有關(guān)聯(lián)主對(duì)象時(shí),是無(wú)法保存到數(shù)據(jù)庫(kù)的。

?

下面看看刪除對(duì)象的情形:

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

(2)直接刪除Office對(duì)象,SaveChanges:沒(méi)有任何問(wèn)題。

?

輪到修改一對(duì)一關(guān)聯(lián)的情形了。

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

這有兩種方式實(shí)現(xiàn):

第一種方式很簡(jiǎn)單,直接找到Teacher對(duì)象所關(guān)聯(lián)的Office對(duì)象,修改它的屬性為新值,之后SaveChanges即可,這本質(zhì)上是“舊房改造”。

另一種方式是“直接分配新房”: 先new一個(gè)Office對(duì)象代表新辦公室,再把它關(guān)聯(lián)上Teacher,如下所示:

?

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

?

這里有一個(gè)細(xì)節(jié)很重要,在提取Teacher對(duì)象時(shí),一定要使用Include方法把它相關(guān)聯(lián)的“Office”對(duì)象也裝入進(jìn)來(lái)。否則,在SaveChanges時(shí),EF報(bào)告:

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

?如果確實(shí)不想Include,則可以顯式指示EF把老的Office對(duì)象狀態(tài)設(shè)置為“刪除”:

?

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

?

?

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

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

這就是說(shuō),這兩種實(shí)現(xiàn)方式?jīng)]有本質(zhì)差別

發(fā)發(fā)感觸:開(kāi)發(fā)中有很多細(xì)節(jié),光看資料不動(dòng)手是發(fā)現(xiàn)不了的。光看書不實(shí)踐能真正掌握技術(shù)?那是神話!開(kāi)發(fā)實(shí)踐是學(xué)習(xí)軟件技術(shù),提升自身能力的硬道理。

一對(duì)多關(guān)聯(lián)

有了一對(duì)一關(guān)聯(lián)的基礎(chǔ),把握一對(duì)多關(guān)聯(lián)就簡(jiǎn)單多了。

我選取“Book(書)”和“BookReview(書評(píng))”作為一對(duì)多關(guān)聯(lián)的示例,并在數(shù)據(jù)庫(kù)中為關(guān)聯(lián)啟用了“級(jí)聯(lián)刪除”特性:

?

?

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

?

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。

?

先看新建記錄的情況:

對(duì)于一對(duì)多關(guān)聯(lián),new一個(gè)Book對(duì)象,SaveChanage,之后,就可以向其BookReviews集合屬性中添加新書評(píng)對(duì)象。

這很簡(jiǎn)單,不用多說(shuō)。刪除記錄的情況就有點(diǎn)復(fù)雜。

?如果要?jiǎng)h除單個(gè)的Book對(duì)象,由于啟用了級(jí)聯(lián)刪除,干掉一個(gè)Book,它所關(guān)聯(lián)的所有BookReview也一并刪除了。

如果想刪除單個(gè)書評(píng),如果使用DB_First方式,Visual Studio生成的實(shí)體對(duì)象集合其類型為ICollection<T>,實(shí)際上是一個(gè)普通的HashSet<T>集合對(duì)象,不具備跟蹤對(duì)象狀態(tài)的功能。因此,在刪除單個(gè)對(duì)象時(shí),需要顯式設(shè)置其狀態(tài)為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();}

?

?

更簡(jiǎn)單的方式是直接從DbSet中移除,這是推薦的方式

?

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

?

?

另一個(gè)在實(shí)踐中遇到的問(wèn)題是”批量刪除”。

?

如果想刪除某本書的全部書評(píng),像下面這么干是不行的,運(yùn)行時(shí)將拋出異常:

?

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

?

?

這是因?yàn)?strong>循環(huán)迭代訪問(wèn)集合的過(guò)程中不允許修改集合。

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

?

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

?

?

這里reviewList和context.BookReviews是兩個(gè)不同的集合對(duì)象,在foreach訪問(wèn)reviewList時(shí),可以方便地移除BookReviews中的對(duì)象。

上述兩個(gè)“不同”的集合,實(shí)際引用“相同的”一堆BookReview對(duì)象,不信?

設(shè)斷點(diǎn)讓程序暫停,以下在VisualStudio“立即窗口(Immediate Windows)”測(cè)試可以證明我沒(méi)有說(shuō)謊:

?

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

?

?

關(guān)于批量刪除,默認(rèn)情況下,EF會(huì)為每個(gè)刪除的實(shí)體對(duì)象生成一條Delete命令,當(dāng)刪除大量實(shí)體時(shí),這有可能帶來(lái)性能問(wèn)題。在這種情況下,最好的解決方案是直接向數(shù)據(jù)庫(kù)發(fā)送SQL命令:

?

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

?

?

一個(gè)SQL命令搞掂,簡(jiǎn)單高效。可以收工,回家睡覺(jué)去也!

?關(guān)于一對(duì)多關(guān)聯(lián),還有一個(gè)問(wèn)題需要說(shuō)說(shuō),那就是“如何在兩個(gè)實(shí)體對(duì)象間移動(dòng)子對(duì)象”。實(shí)現(xiàn)起來(lái)也簡(jiǎn)單:

先從源實(shí)體對(duì)象中先Remove掉子對(duì)象,再Add到目標(biāo)對(duì)象中即可。

以下示例代碼把第一本書的第一條書評(píng)移到第二本書下:

?

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();

與一對(duì)一關(guān)聯(lián)的情況類似,上述代碼將只生成一個(gè)update命令。

多對(duì)多關(guān)聯(lián)

多對(duì)多關(guān)聯(lián)的最典型實(shí)例就是軟件權(quán)限管理系統(tǒng)中的“用戶(User)”與“角色(Role)”。一個(gè)用戶可以擁有多種角色,一個(gè)角色可以包容多個(gè)用戶。

?

對(duì)于實(shí)體對(duì)象間的多對(duì)多關(guān)聯(lián),EF code First自動(dòng)地在數(shù)據(jù)庫(kù)層將其拆成兩個(gè)一對(duì)多關(guān)聯(lián),變成以下這個(gè)模樣,并為這兩個(gè)關(guān)聯(lián)啟用“級(jí)聯(lián)刪除”:

?

?

多對(duì)多關(guān)聯(lián)對(duì)象的新建和修改,可以參照一對(duì)多的情形來(lái)處理。比如要?jiǎng)h除單個(gè)的User或Role對(duì)象,很簡(jiǎn)單,直接從DbSet中移除它們,再SaveChange即可。

不再多說(shuō),大家自己回去編寫實(shí)驗(yàn)代碼。

比較麻煩的是涉及到多對(duì)多關(guān)聯(lián)本身的添加與刪除問(wèn)題。典型的例子是:

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

對(duì)于上述這些情況,EF將不動(dòng)User表和Role表,只在中間表RoleUsers中動(dòng)手腳。

比如要從User與Role多對(duì)多關(guān)聯(lián)任一方的對(duì)象集合導(dǎo)航屬性中移除某個(gè)(或某些)User或Role對(duì)象,然后SaveChanges,對(duì)于這種操作,EF默認(rèn)不會(huì)修改User與Role實(shí)體對(duì)象的狀態(tài),只是在底層數(shù)據(jù)庫(kù)的中間表RoleUsers中刪除了相應(yīng)的記錄,使它們“斷開(kāi)關(guān)聯(lián)”。

理解這點(diǎn)很關(guān)鍵。

這里還有一個(gè)細(xì)節(jié):

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

請(qǐng)看以下代碼:

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之后,都能達(dá)到在底層數(shù)據(jù)庫(kù)的RoleUsers表中刪除相應(yīng)記錄的功能。但如果只寫了一句,則Role對(duì)象的Users集合中確實(shí)移除了指定的User對(duì)象,但此User對(duì)象的Roles屬性中還包容有這個(gè)Role對(duì)象,這就是數(shù)據(jù)不一致的情況。因此,最安全的寫法是兩句都寫。

總結(jié)

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

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。