Entity?Framework導(dǎo)航屬性介紹
一、主鍵和外鍵
關(guān)系型數(shù)據(jù)庫(kù)中的一條記錄中有若干個(gè)屬性,若其中某一個(gè)屬性組是能唯一標(biāo)識(shí)一條記錄,該屬性組就可以稱為主鍵。例如:
學(xué)生版(學(xué)號(hào)、姓名、性別、班級(jí))
其中每個(gè)學(xué)生的學(xué)號(hào)是唯一的,學(xué)號(hào)就是一個(gè)主鍵。
課程表(課程編號(hào),課程名,學(xué)分)
其中課程編號(hào)是唯一的,課程編號(hào)就是一個(gè)主鍵。
成績(jī)表(學(xué)號(hào)、課程號(hào)、成績(jī))
成績(jī)表中單獨(dú)的一個(gè)屬性無(wú)法唯一標(biāo)識(shí)一條記錄,學(xué)號(hào)和課程號(hào)的組合才能唯一標(biāo)識(shí)一條記錄,所以學(xué)號(hào)和課程號(hào)的屬性組是一個(gè)主鍵。
外鍵
成績(jī)表中的學(xué)號(hào)不是成績(jī)表的主鍵,但它和學(xué)生表中的學(xué)號(hào)相對(duì)應(yīng),并且學(xué)生表中的學(xué)號(hào)是學(xué)生表的主鍵,則稱成績(jī)表中的學(xué)號(hào)是學(xué)生表的外鍵。同理:成績(jī)表中的課程號(hào)是課程表的外鍵。
EntityFramework中的導(dǎo)航屬性即外鍵。下面通過(guò)例子講解如何使用EF的導(dǎo)航屬性。
二、導(dǎo)航屬性
1、新建產(chǎn)品分類表,語(yǔ)句如下:
CREATE table Category ( CategoryId int primary key not null identity, CategoryName varchar(64) )
新建產(chǎn)品明細(xì)表,其中CategoryId是外鍵
CREATE TABLE [dbo].[ProductDetail]( [ProductId] [int] IDENTITY(1,1) NOT NULL, [ProductName] [varchar](32) NULL, [Price] [decimal](9, 2) NULL, [CategoryId] [int] NULL, PRIMARY KEY CLUSTERED ( [ProductId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO SET ANSI_PADDING OFF GO ALTER TABLE [dbo].[ProductDetail] WITH CHECK ADD CONSTRAINT [FK_Category] FOREIGN KEY([CategoryId]) REFERENCES [dbo].[Category] ([CategoryId]) GO ALTER TABLE [dbo].[ProductDetail] CHECK CONSTRAINT [FK_Category] GO
分別往Category表和ProductDetail表中插入一些測(cè)試數(shù)據(jù):
--Category表插入數(shù)據(jù) INSERT INTO Category (CategoryName) select '電子產(chǎn)品' union SELECT '家用電器' UNION SELECT '圖書(shū)' --ProductDetail表插入數(shù)據(jù) INSERT INTO ProductDetail (ProductName,Price,CategoryId) SELECT '蘋(píng)果6s手機(jī)',5633,1 UNION SELECT 'Dell電腦',6998,1 UNION SELECT '佳能相機(jī)',5633,1 UNION SELECT '海爾洗衣機(jī)',1234,2 UNION SELECT '格力空調(diào)',2344,2 UNION SELECT '美的冰箱',3218,2 UNION SELECT '白鹿原',342,3 UNION SELECT 'C#高級(jí)編程(第十版)',145,3 UNION SELECT '平凡的世界',231,3
2、使用DataBase First模式生成edmx文件,然后查看Category表和ProductDetail表相對(duì)應(yīng)的實(shí)體的定義
Category表定義:
//------------------------------------------------------------------------------ // <auto-generated> // 此代碼已從模板生成。 // // 手動(dòng)更改此文件可能導(dǎo)致應(yīng)用程序出現(xiàn)意外的行為。 // 如果重新生成代碼,將覆蓋對(duì)此文件的手動(dòng)更改。 // </auto-generated> //------------------------------------------------------------------------------ namespace EFNavigateDemo { using System; using System.Collections.Generic; public partial class Category { [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] public Category() { this.ProductDetails = new HashSet<ProductDetail>(); } public int CategoryId { get; set; } public string CategoryName { get; set; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public virtual ICollection<ProductDetail> ProductDetails { get; set; } } }
Category實(shí)體類中有一個(gè)ProductDetail類型的集合屬性,表示是導(dǎo)航屬性。
實(shí)體類型包含其他實(shí)體類型(POCO類)的屬性(也可稱為導(dǎo)航屬性),且同時(shí)滿足如下條件即可實(shí)現(xiàn)延遲加載:
- 1.該屬性的類型必須為public且不能為Sealed。
- 2.屬性標(biāo)記為Virtual。
ProductDetail實(shí)體類定義如下:
//------------------------------------------------------------------------------ // <auto-generated> // 此代碼已從模板生成。 // // 手動(dòng)更改此文件可能導(dǎo)致應(yīng)用程序出現(xiàn)意外的行為。 // 如果重新生成代碼,將覆蓋對(duì)此文件的手動(dòng)更改。 // </auto-generated> //------------------------------------------------------------------------------ namespace EFNavigateDemo { using System; using System.Collections.Generic; public partial class ProductDetail { public int ProductId { get; set; } public string ProductName { get; set; } public Nullable<decimal> Price { get; set; } public Nullable<int> CategoryId { get; set; } public virtual Category Category { get; set; } } }
ProductDetail類里面有一個(gè)Category類型的屬性。
導(dǎo)航屬性實(shí)現(xiàn)延遲加載的四種方式:
1、方式一
using (var dbContext = new CategoryEntities()) { dbContext.Configuration.LazyLoadingEnabled = true; // 默認(rèn)是true,針對(duì)導(dǎo)航屬性 var categoryList = dbContext.Set<Category>().Where(p => p.CategoryId == 3); // 只會(huì)在數(shù)據(jù)庫(kù)里面查詢Category表,不會(huì)查詢ProductDetail表 foreach(var category in categoryList) { Console.WriteLine("CategoryId:"+category.CategoryId+ ",CategoryName:"+category.CategoryName); // 這時(shí)才會(huì)去數(shù)據(jù)庫(kù)查詢ProductDetail表 foreach (var product in category.ProductDetails) { Console.WriteLine("ProductName:"+product.ProductName); } } }
分別在兩處foreach循環(huán)的地方添加斷點(diǎn),然后運(yùn)行程序查看數(shù)據(jù)庫(kù)執(zhí)行的SQL語(yǔ)句情況:
執(zhí)行到斷點(diǎn)1時(shí):
這時(shí)查看數(shù)據(jù)庫(kù)監(jiān)控:
繼續(xù)執(zhí)行到斷點(diǎn)2:
這時(shí)在查看數(shù)據(jù)庫(kù)監(jiān)控:
會(huì)發(fā)現(xiàn)遍歷ProductDetails屬性時(shí)也會(huì)查詢ProductDetail表。
2、方式二
using (var dbContext = new CategoryEntities()) { dbContext.Configuration.LazyLoadingEnabled = false; // 不延遲加載,不會(huì)再次查詢了 var categoryList = dbContext.Set<Category>().Where(p => p.CategoryId == 3); // 只會(huì)在數(shù)據(jù)庫(kù)里面查詢Category表,不會(huì)查詢ProductDetail表 foreach (var category in categoryList) { Console.WriteLine("CategoryId:" + category.CategoryId + ",CategoryName:" + category.CategoryName); // 這時(shí)不會(huì)去數(shù)據(jù)庫(kù)查詢了,所以用戶全是空的 foreach (var product in category.ProductDetails) { Console.WriteLine("ProductName:" + product.ProductName); } } }
這時(shí)還是采用和上面一樣的方法加入斷點(diǎn),只需要查看第二次循環(huán)時(shí)的數(shù)據(jù)庫(kù)監(jiān)控情況即可:
從上面的截圖中看出,如果LazyLoadingEnabled設(shè)置為false,將不會(huì)再查詢ProductDetail表的數(shù)據(jù)了。
3、方式三
// 顯示加載 using (var dbContext = new CategoryEntities()) { // 不延遲加載,指定Include,一次性加載主表和從表的所有數(shù)據(jù) var categoryList = dbContext.Set<Category>().Include("ProductDetails").Where(p => p.CategoryId == 3); foreach (var category in categoryList) { Console.WriteLine("CategoryId:" + category.CategoryId + ",CategoryName:" + category.CategoryName); // 不會(huì)再查詢 foreach (var product in category.ProductDetails) { Console.WriteLine("ProductName:" + product.ProductName); } } }
使用Include()方法會(huì)一次性加載所有的數(shù)據(jù):
4、方式四
在上面的方式2中把LazyLoadingEnabled設(shè)置為false以后就不會(huì)再查詢ProductDetail表的數(shù)據(jù)了,這時(shí)如果想要查詢ProductDetail表的數(shù)據(jù)該怎么辦呢?這時(shí)可以使用手動(dòng)加載,代碼如下:
//LoadProperty 手動(dòng)加載 using (var dbContext = new CategoryEntities()) { dbContext.Configuration.LazyLoadingEnabled = false; // 不延遲加載,不會(huì)再次查詢了 var categoryList = dbContext.Set<Category>().Where(p => p.CategoryId == 3); foreach (var category in categoryList) { Console.WriteLine("CategoryId:" + category.CategoryId + ",CategoryName:" + category.CategoryName); dbContext.Entry<Category>(category).Collection(p => p.ProductDetails).Load();// 集合顯示加載 foreach (var product in category.ProductDetails) { Console.WriteLine("ProductName:" + product.ProductName); } } }
添加斷點(diǎn):
查看數(shù)據(jù)庫(kù)監(jiān)控:
5、插入數(shù)據(jù)
對(duì)于Category和ProductDetail表如何同時(shí)插入數(shù)據(jù)?先看下面的一段代碼:
using (var dbContext = new CategoryEntities()) { using (TransactionScope trans = new TransactionScope()) { Category category = new Category() { CategoryName = "自行車" }; dbContext.Categories.Add(category); dbContext.SaveChanges();//category.CategoryId賦值了 ProductDetail product = new ProductDetail() { ProductName = "美利達(dá)", Price = 2312, CategoryId = category.CategoryId }; dbContext.ProductDetails.Add(product); dbContext.SaveChanges(); trans.Complete();//提交事務(wù) } }
在第一次SaveChanges()后面的一行代碼加斷點(diǎn),查看Category信息:
可以看到這是CategoryId已經(jīng)有值了,查詢數(shù)據(jù)庫(kù)ProductDetail表:
這時(shí)Product的信息已經(jīng)插入到數(shù)據(jù)庫(kù)中了,而且CategordId也是上面生成的CategoryId。
但是這樣會(huì)導(dǎo)致一種問(wèn)題存在:如果第一次SaveChanges()成功,第二次SaveChanges()之前報(bào)錯(cuò)了,但是程序已經(jīng)不能回滾了,這樣就會(huì)導(dǎo)致數(shù)據(jù)不一致了。使用下面的代碼進(jìn)行優(yōu)化:
using (var dbContext = new CategoryEntities()) { using (TransactionScope trans = new TransactionScope()) { Category category = new Category() { CategoryName = "汽車" }; ProductDetail product = new ProductDetail() { ProductName = "上海大眾", Price = 190090, CategoryId = category.CategoryId }; category.ProductDetails = new List<ProductDetail>() { product}; dbContext.Categories.Add(category); dbContext.SaveChanges(); trans.Complete();//提交事務(wù) } }
經(jīng)過(guò)這樣修改以后可以保證數(shù)據(jù)的一致性了。這是情況只適合有導(dǎo)航屬性的。
示例代碼下載地址:點(diǎn)此下載
到此這篇關(guān)于Entity Framework導(dǎo)航屬性的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Entity?Framework根據(jù)實(shí)體的EntityState狀態(tài)實(shí)現(xiàn)增刪改查
- Entity?Framework使用DataBase?First模式實(shí)現(xiàn)數(shù)據(jù)庫(kù)的增刪改查
- Entity?Framework生成DataBase?First模式
- Entity Framework使用Code First模式管理事務(wù)
- Entity Framework管理并發(fā)
- Entity Framework使用Code First模式管理存儲(chǔ)過(guò)程
- Entity Framework使用Code First模式管理視圖
- Entity Framework加載控制Loading Entities
- Entity Framework使用LINQ操作實(shí)體
- Entity?Framework使用Code?First的實(shí)體繼承模式
- Entity Framework使用Code First模式管理數(shù)據(jù)庫(kù)
相關(guān)文章
.net Core 使用IHttpClientFactory請(qǐng)求實(shí)現(xiàn)
這篇文章主要介紹了.net Core 使用IHttpClientFactory請(qǐng)求實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01asp.net中強(qiáng)制取消TFS2008中其它成員的簽出文件的方法
有個(gè)項(xiàng)目,以前的成員離職了,剛好又簽出了一個(gè)文件在TFS中并且上了鎖,導(dǎo)致后面的維護(hù)無(wú)法簽入和生成。在網(wǎng)上查了一下,找到了如下解決辦法2012-08-08ASP.NET Core 集成 React SPA應(yīng)用的步驟
這篇文章主要介紹了ASP.NET Core 集成 React SPA應(yīng)用的步驟,幫助大家更好的理解和學(xué)習(xí)使用.net技術(shù),感興趣的朋友可以了解下2021-04-04asp.net關(guān)于Cookie跨域(域名)的問(wèn)題
Cookie是一個(gè)偉大的發(fā)明,它允許Web開(kāi)發(fā)者保留他們的用戶的登錄狀態(tài)。但是當(dāng)你的站點(diǎn)有一個(gè)以上的域名時(shí)就會(huì)出現(xiàn)問(wèn)題了。在Cookie規(guī)范上說(shuō),一個(gè)cookie只能用于一個(gè)域名,不能夠發(fā)給其它的域名。因此,如果在瀏覽器中對(duì)一個(gè)域名設(shè)置了一個(gè)cookie,這個(gè)cookie對(duì)于其它的域名將無(wú)效。如果你想讓你的用戶從你的站點(diǎn)中的其中一個(gè)進(jìn)行登錄,同時(shí)也可以在其它域名上進(jìn)行登錄,這可真是一個(gè)大難題。2012-12-12asp.net窗體的打開(kāi)和關(guān)閉(輸出js)
asp.net窗體的打開(kāi)和關(guān)閉(輸出js),需要的朋友可以參考下。2011-06-06.Net?Core?進(jìn)程守護(hù)之Supervisor使用詳解
這篇文章主要介紹了.Net?Core?進(jìn)程守護(hù)之Supervisor使用,Supervisor它可以很方便的監(jiān)聽(tīng)、啟動(dòng)、停止、重啟一個(gè)或多個(gè)進(jìn)程,對(duì).Net?Core?進(jìn)程守護(hù)之Supervisor使用相關(guān)知識(shí)感興趣的朋友一起看看吧2022-04-04集合類List與Dictonary實(shí)例練習(xí)
本文將詳細(xì)介紹下List<>泛型集合/Dictonary<>字典/泛型集合練習(xí) /中日期轉(zhuǎn)換提取為方法以及泛型集合練習(xí)之翻譯軟件,感興趣的你可不要錯(cuò)過(guò)了哈2013-02-02