所以,如果我理解正確,你的問題是與一個映射到零或一個關係。 您遇到的是實體框架的一個設計特性。處理一對一關係(及其可選對象)很棘手,原因很多。當你這樣做時,你不能在你的模型—中指定外鍵,而是你的實體的主鍵也是主體端的外鍵,不能指定額外的FK。
有關詳細信息,請參閱下面的內容。
映射一到零或一個
所以我們可以說,你有以下型號:
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
}
public class Car
{
public int CarId { get; set; }
public string LicensePlate { get; set; }
}
public class MyDemoContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Car> Cars { get; set; }
}
現在你想設定,讓你可以表示以下規範:一個人可以有一輛或零輛車,並且每輛車都完全屬於一個人(關係是雙向的,所以如果CarA屬於PersonA,那麼PersonA'擁有'CarA)。
,讓我們修改模型中的位:添加導航屬性和外鍵的屬性:
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public int CarId { get; set; }
public virtual Car Car { get; set; }
}
public class Car
{
public int CarId { get; set; }
public string LicensePlate { get; set; }
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
而且配置:
public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
public CarEntityTypeConfiguration()
{
this.HasRequired(c => c.Person).WithOptional(p => p.Car);
}
}
這個時候,這應該是不言自明的。該車有一個需要的人(HasRequired()),該人有一輛可選車(WithOptional())。同樣,你配置這種關係的哪一方並不重要,只要在使用Has/With和Required/Optional的正確組合時小心。從Person
方面,它應該是這樣的:
public class PersonEntityTypeConfiguration : EntityTypeConfiguration<Person>
{
public PersonEntityTypeConfiguration()
{
this.HasOptional(p => p.Car).WithOptional(c => c.Person);
}
}
現在讓我們來看看DB模式:
仔細查看:你可以看到,有沒有FK在People
指到Car
。另外,Car
中的FK不是PersonId
,而是CarId
。下面是爲FK的實際腳本:
ALTER TABLE [dbo].[Cars] WITH CHECK ADD CONSTRAINT [FK_dbo.Cars_dbo.People_CarId] FOREIGN KEY([CarId])
REFERENCES [dbo].[People] ([PersonId])
因此,這意味着我們有CarId
和PersonId
foregn關鍵屬性模型基本上忽略。它們在數據庫中,但它們不是外鍵,因爲它可能是預期的。這是因爲一對一映射不支持將FK添加到EF模型中。這是因爲一對一映射在關係數據庫中是相當成問題的。
這個想法是每個人都可以有一輛車,而那輛車只能屬於那個人。或者可能有人員記錄,沒有與他們相關的汽車。
那麼這怎麼可能用外鍵表示呢?顯然,Car
中可能有PersonId
,People
中有CarId
。爲了強制每個人只能有一輛車,PersonId
必須是唯一的Car
。但是,如果PersonId
在People
中是唯一的,那麼如何添加兩個或多個記錄,其中PersonId
是NULL
(多輛車沒有車主)?答:你不能(事實上,你可以在SQL Server 2008和更新的版本中創建一個過濾的唯一索引,但讓我們暫時忘記這個技術性問題;更別說其他RDBMS了)。更不用說您指定關係的兩端的情況...
如果People
和Car
表具有「相同」主鍵(連接記錄中的值相同),則唯一可行的方法是執行此規則, 。要做到這一點,CarId
在Car
必須是一個PK和一個FK的人民PK。這使得整個模式變得混亂。當我使用這個我寧願命名PK/FK在Car
PersonId
,並相應地對其進行配置:
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public virtual Car Car { get; set; }
}
public class Car
{
public string LicensePlate { get; set; }
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
public CarEntityTypeConfiguration()
{
this.HasRequired(c => c.Person).WithOptional(p => p.Car);
this.HasKey(c => c.PersonId);
}
}
並不理想,但也許會好一點。但是,使用此解決方案時必須保持警惕,因爲它違背了通常的命名約定,可能會導致您誤入歧途。下面是從這個模型生成的架構:
所以這種關係不是由數據庫架構強制執行,但通過實體框架本身。這就是爲什麼當你使用這個時你必須非常小心,而不是讓任何人直接用數據庫來鍛鍊。
那麼這是EF或Sql服務器的設計限制嗎?必須有辦法可以定義導航屬性的FK名稱權限? – YesMan85
我想這是兩個。在關係型數據庫中有一個真正的1-1是不可能的,所以你必須在EF中做一些技巧。另外,如果向FK添加一個唯一約束,則只有一個1-1關聯纔是可行的。我想這就是EF進入並解決這些問題的原因,因爲它引入了這個限制。 –
是的,我多玩了一會兒,我能夠在SQL服務器中以黑客的方式創建關係,但無法讓它在EF中工作。現在決定改變數據模型。 – YesMan85