Entity?Framework導(dǎo)航屬性介紹
一、主鍵和外鍵
關(guān)系型數(shù)據(jù)庫中的一條記錄中有若干個屬性,若其中某一個屬性組是能唯一標(biāo)識一條記錄,該屬性組就可以稱為主鍵。例如:
學(xué)生版(學(xué)號、姓名、性別、班級)
其中每個學(xué)生的學(xué)號是唯一的,學(xué)號就是一個主鍵。
課程表(課程編號,課程名,學(xué)分)
其中課程編號是唯一的,課程編號就是一個主鍵。
成績表(學(xué)號、課程號、成績)
成績表中單獨的一個屬性無法唯一標(biāo)識一條記錄,學(xué)號和課程號的組合才能唯一標(biāo)識一條記錄,所以學(xué)號和課程號的屬性組是一個主鍵。
外鍵
成績表中的學(xué)號不是成績表的主鍵,但它和學(xué)生表中的學(xué)號相對應(yīng),并且學(xué)生表中的學(xué)號是學(xué)生表的主鍵,則稱成績表中的學(xué)號是學(xué)生表的外鍵。同理:成績表中的課程號是課程表的外鍵。
EntityFramework中的導(dǎo)航屬性即外鍵。下面通過例子講解如何使用EF的導(dǎo)航屬性。
二、導(dǎo)航屬性
1、新建產(chǎn)品分類表,語句如下:
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表中插入一些測試數(shù)據(jù):
--Category表插入數(shù)據(jù) INSERT INTO Category (CategoryName) select '電子產(chǎn)品' union SELECT '家用電器' UNION SELECT '圖書' --ProductDetail表插入數(shù)據(jù) INSERT INTO ProductDetail (ProductName,Price,CategoryId) SELECT '蘋果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#高級編程(第十版)',145,3 UNION SELECT '平凡的世界',231,3
2、使用DataBase First模式生成edmx文件,然后查看Category表和ProductDetail表相對應(yīng)的實體的定義
Category表定義:
//------------------------------------------------------------------------------
// <auto-generated>
// 此代碼已從模板生成。
//
// 手動更改此文件可能導(dǎo)致應(yīng)用程序出現(xiàn)意外的行為。
// 如果重新生成代碼,將覆蓋對此文件的手動更改。
// </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實體類中有一個ProductDetail類型的集合屬性,表示是導(dǎo)航屬性。
實體類型包含其他實體類型(POCO類)的屬性(也可稱為導(dǎo)航屬性),且同時滿足如下條件即可實現(xiàn)延遲加載:
- 1.該屬性的類型必須為public且不能為Sealed。
- 2.屬性標(biāo)記為Virtual。
ProductDetail實體類定義如下:
//------------------------------------------------------------------------------
// <auto-generated>
// 此代碼已從模板生成。
//
// 手動更改此文件可能導(dǎo)致應(yīng)用程序出現(xiàn)意外的行為。
// 如果重新生成代碼,將覆蓋對此文件的手動更改。
// </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類里面有一個Category類型的屬性。
導(dǎo)航屬性實現(xiàn)延遲加載的四種方式:
1、方式一
using (var dbContext = new CategoryEntities())
{
dbContext.Configuration.LazyLoadingEnabled = true; // 默認(rèn)是true,針對導(dǎo)航屬性
var categoryList = dbContext.Set<Category>().Where(p => p.CategoryId == 3);
// 只會在數(shù)據(jù)庫里面查詢Category表,不會查詢ProductDetail表
foreach(var category in categoryList)
{
Console.WriteLine("CategoryId:"+category.CategoryId+ ",CategoryName:"+category.CategoryName);
// 這時才會去數(shù)據(jù)庫查詢ProductDetail表
foreach (var product in category.ProductDetails)
{
Console.WriteLine("ProductName:"+product.ProductName);
}
}
}分別在兩處foreach循環(huán)的地方添加斷點,然后運行程序查看數(shù)據(jù)庫執(zhí)行的SQL語句情況:
執(zhí)行到斷點1時:

這時查看數(shù)據(jù)庫監(jiān)控:

繼續(xù)執(zhí)行到斷點2:

這時在查看數(shù)據(jù)庫監(jiān)控:

會發(fā)現(xiàn)遍歷ProductDetails屬性時也會查詢ProductDetail表。
2、方式二
using (var dbContext = new CategoryEntities())
{
dbContext.Configuration.LazyLoadingEnabled = false; // 不延遲加載,不會再次查詢了
var categoryList = dbContext.Set<Category>().Where(p => p.CategoryId == 3);
// 只會在數(shù)據(jù)庫里面查詢Category表,不會查詢ProductDetail表
foreach (var category in categoryList)
{
Console.WriteLine("CategoryId:" + category.CategoryId + ",CategoryName:" + category.CategoryName);
// 這時不會去數(shù)據(jù)庫查詢了,所以用戶全是空的
foreach (var product in category.ProductDetails)
{
Console.WriteLine("ProductName:" + product.ProductName);
}
}
}這時還是采用和上面一樣的方法加入斷點,只需要查看第二次循環(huán)時的數(shù)據(jù)庫監(jiān)控情況即可:

從上面的截圖中看出,如果LazyLoadingEnabled設(shè)置為false,將不會再查詢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);
// 不會再查詢
foreach (var product in category.ProductDetails)
{
Console.WriteLine("ProductName:" + product.ProductName);
}
}
}使用Include()方法會一次性加載所有的數(shù)據(jù):

4、方式四
在上面的方式2中把LazyLoadingEnabled設(shè)置為false以后就不會再查詢ProductDetail表的數(shù)據(jù)了,這時如果想要查詢ProductDetail表的數(shù)據(jù)該怎么辦呢?這時可以使用手動加載,代碼如下:
//LoadProperty 手動加載
using (var dbContext = new CategoryEntities())
{
dbContext.Configuration.LazyLoadingEnabled = false; // 不延遲加載,不會再次查詢了
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);
}
}
}添加斷點:

查看數(shù)據(jù)庫監(jiān)控:

5、插入數(shù)據(jù)
對于Category和ProductDetail表如何同時插入數(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()后面的一行代碼加斷點,查看Category信息:

可以看到這是CategoryId已經(jīng)有值了,查詢數(shù)據(jù)庫ProductDetail表:

這時Product的信息已經(jīng)插入到數(shù)據(jù)庫中了,而且CategordId也是上面生成的CategoryId。
但是這樣會導(dǎo)致一種問題存在:如果第一次SaveChanges()成功,第二次SaveChanges()之前報錯了,但是程序已經(jīng)不能回滾了,這樣就會導(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)過這樣修改以后可以保證數(shù)據(jù)的一致性了。這是情況只適合有導(dǎo)航屬性的。
示例代碼下載地址:點此下載
到此這篇關(guān)于Entity Framework導(dǎo)航屬性的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Entity?Framework根據(jù)實體的EntityState狀態(tài)實現(xiàn)增刪改查
- Entity?Framework使用DataBase?First模式實現(xiàn)數(shù)據(jù)庫的增刪改查
- Entity?Framework生成DataBase?First模式
- Entity Framework使用Code First模式管理事務(wù)
- Entity Framework管理并發(fā)
- Entity Framework使用Code First模式管理存儲過程
- Entity Framework使用Code First模式管理視圖
- Entity Framework加載控制Loading Entities
- Entity Framework使用LINQ操作實體
- Entity?Framework使用Code?First的實體繼承模式
- Entity Framework使用Code First模式管理數(shù)據(jù)庫
相關(guān)文章
ASP.NET Core3.1 Ocelot負(fù)載均衡的實現(xiàn)
這篇文章主要介紹了ASP.NET Core3.1 Ocelot負(fù)載均衡的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
巧用ASP.NET預(yù)編譯Web應(yīng)用程序規(guī)避調(diào)用延遲的方法
ASP.NET 1.x的開發(fā)人員常常聽到用戶抱怨首次調(diào)用應(yīng)用程序的時候會碰到初始化延遲。畢竟,初次請求會引發(fā)一個系列過程,包括運行庫初始化、分析、把ASPX頁面編譯成中間語言、把方法即時編譯成本地代碼等等。2011-08-08
asp.net core 2.0 webapi集成signalr(實例講解)
下面小編就為大家分享一篇asp.net core 2.0 webapi集成signalr的實例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-11-11

