Entity Framework - 理清關系 - 基於外鍵關聯的單向一對一關系


注:本文針對的是 Entity Framework Code First 場景。

之前寫過三篇文章試圖理清Entity Framework中的一對一關系(單相思(單向一對一), 兩情相悅(雙向一對一), 兩情相悅-續),但當時理得不夠清,新的一年重新理一理。

當時“一對一”的實體關系,對應的數據庫關系是外鍵關聯(實際上是一種“一對多”關系,所以映射時用了WithMany)。而數據庫中的“一對一”關系是共享主鍵(這是我個人的理解,不妥之處,歡迎指出),下篇文章將要理的就是這個關系。

由於雙向“一對一”關系很少用到,而且不推薦使用,為了更清楚地理解,我們這里只談單向一對一關系,也就是“基於外鍵關聯的單向一對一關系(One-to-one Unidirectional relationships)”,對應的之前的文章是單相思(單向一對一)

1. 類圖

2. 類的定義

public class BlogSite
{
public int BlogID { get; set; }
public string BlogApp { get; set; }
public bool IsActive { get; set; }
public Guid UserID { get; set; }
public virtual BlogUser BlogUser { get; set; }
}
public class BlogUser
{
public Guid UserID { get; set; }
public string Author { get; set; }
public int BlogID { get; set; }
}

3. 數據庫結構

4. Enitity Framework映射關系定義

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogSite>()
.HasRequired(b => b.BlogUser)
.WithMany();
}

怎么理解這里的HasRequired與WithMany呢?

我的理解是:HasRequired是針對BlogSite與BlogUser的關系,WithMany是針對BlogUser與BlogSite的關系。.HasRequired(b => b.BlogUser).WithMany()表示BlogSite與BlogUser存在Required關聯關系(One-To-One, 每一個BlogSite都有一個對應的BlogUser),而這個關聯對BlogUser來說是One-To-Many(一個BlogUser可以有多個BlogSite)。

你也許會疑惑?明明是單向一對一的實體關系,這里怎么弄出個一對多的關系?

如果有這樣的疑惑,屬正常現象,我從去年7月份寫那篇文章開始疑惑,一直疑惑到現在,寫篇文章時才有點搞明白。

注意,“單向一對一”是什么?是實體關系;Entity Framework是什么?是O/RM,是用來映射實體關系與數據庫關系;還少了什么?數據庫關系。

從上面的圖中的數據庫結構可以看出,BlogSite與BlogUser之間是外鍵關聯關系,下面的圖可以更清楚地看出這一點。

這個外鍵關聯表示的就是BlogUser與BlogSite之間是一對多的數據庫關系。

總結一下:

實體關系 —— BlogSite與BlogUser之間的單向一對一

數據庫關系 —— BlogUser與BlogSite之間的一對多

是不是這樣呢?Entity Framework是不是也是這樣認為的呢?我們來驗證一下。

 

怎么知道Entity Framework的想法呢?

通過EDM。

可這里是Code First?

不管什么First,都有EDM,因為這是Entity Framework的地圖,沒有它,Entity Framework就會暈頭轉向。Code First的EDM是在EF運行時生成的,不是沒有地圖,只是在Entity Framework的心中,我們看不到而已。

怎么讓Entity Framework說出心里話呢?

從Morteza Manavi大師那學到一招,代碼如下:

using (var context = new Context())
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;

using (XmlWriter writer = XmlWriter.Create(@"Model.edmx", settings))
{
EdmxWriter.WriteEdmx(context, writer);
}
}

通過上面的代碼,你就可以拿到EF心中的地圖 —— edmx文件。請看地圖:

果然,EF心中的地圖就是BlogUser與BlogSite的一對多關系。地圖的作用是什么?是讓EF通過地圖在數據庫找到對應的數據。地圖是如何產生的?是我們通過FluentAPI告訴Entity Framework:.HasRequired(b => b.BlogUser).WithMany()。

我們再來剖析一下.HasRequired(b => b.BlogUser).WithMany()。

之前,我一直被困擾,是因為總是把這里的定義當作實體的關系的定義。錯!這里雖然用的是實體進行定義,但定義的是實體與數據庫中的數據之間的映射關系(這本來就是常識,竟然被忽略了),更多的是告訴EF這些實體在數據庫中的數據關系。EF最終是根據這個定義生成相應的SQL。

那我們從生成查詢SQL的角度來理解一下:

.HasRequired(b => b.BlogUser)告訴EF,這是一個INNER JOIN查詢(BlogSite INNER JOIN BlogUser);但INNER JOIN還需要條件,WithMany()告訴EF這是一個外鍵關聯,EF據此進行推斷,從BlogUser中找到主鍵UserID,並檢查BlogSite中是否存在名為UserID的屬性,如果存在,就以此為外鍵進行查詢。而我們的BlogSite中有UserID,於是生成下面的SQL:

SELECT 
[Extent1].[BlogID] AS [BlogID],
[Extent1].[BlogApp] AS [BlogApp],
[Extent1].[IsActive] AS [IsActive],
[Extent1].[UserID] AS [UserID],
[Extent2].[UserID] AS [UserID1],
[Extent2].[Author] AS [Author]
FROM [dbo].[BlogSite] AS [Extent1]
INNER JOIN [dbo].[BlogUser] AS [Extent2] ON [Extent1].[UserID] = [Extent2].[UserID]
WHERE 1 = [Extent1].[IsActive]

 

小結

理清“基於外鍵關聯的單向一對一關系”,關鍵在於對modelBuilder.Entity<A>().HasRequired(A => A.B).WithMany()的理解。

我再來理解一次:

.HasRequired(A => A.B) 表示:1)實體A與實體B是一對一關系,實體A有一個導航屬性A.B;2)在數據庫中表A與表B存在一對一關聯(INNER JOIN)。

.WithMany() 表示:1) 實體B與實體A可以沒有關系,也可以是一對多關系;2)在數據庫中表A與表B存在外鍵關聯。

上面全是我的個人理解,真正理清Entity Framework中的關系需要大家的力量,我只是拋個磚。

 

除了“基於外鍵關聯的單向一對一關系”,還有“基於共享主鍵的單向一對一關系”,這也是我們開發中經常碰到的一種關系,比如博客文章(BlogPost)與文章內容(PostBody),新聞(NewsItem)與新聞內容(NewsBody)。下一篇文章將會理理這個關系。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM