EF Code-First 一对一关系:多重性在 Role * in relationship 中无效
Posted
技术标签:
【中文标题】EF Code-First 一对一关系:多重性在 Role * in relationship 中无效【英文标题】:EF Code-First One-to-one relationship: Multiplicity is not valid in Role * in relationship 【发布时间】:2014-12-10 22:51:54 【问题描述】:我正在尝试执行以下操作:
public class class1
public int Id get;set;
[ForeignKey("Class2")]
public int Class2Id get;set;
public virtual Class2 Class2 get;set;
public class class2
public int Id get; set;
[Required]
public virtual int Class1Id get;set;
[Required]
[ForeignKey("Class1Id")]
public Class1 Class1 get;set;
但是,每次我尝试迁移数据库时,都会收到以下错误:
Class1_Class2_Target: : 多重性在角色中无效 关系“Class2_Class1”中的“Class2_Class1_Target”。因为 Dependent Role 属性不是关键属性,上限 从属角色的多重性必须是“*”。
这可能是什么问题?
【问题讨论】:
【参考方案1】:您的模型不是一对一的关联。您仍然可以有 许多 Class2
对象引用相同的 一个 Class1
对象。此外,您的模型不保证引用 Class1
的 Class2
也被此 Class1
对象引用——Class1
可以引用任何 Class2
对象。
如何配置1:1?
在 SQL 中保证(某种程度上)1:1 关联的常用方法是为 principal 实体创建一个表,为 从属 实体创建一个表,其中依赖表中的主键也是主体的外键:
(这里Class1
是校长)
现在在关系数据库中,这仍然不能保证 1:1 关联(这就是我说“有点”的原因)。这是一个 1:0..1 关联。可以有Class1
而没有Class2
。事实上,真正的 1:1 关联在 SQL 中是不可能的,因为没有一种语言结构可以在不同的表中同步插入两行。 1:0..1 是我们得到的最接近的值。
Fluent 映射
要在 EF 中为这种关联建模,您可以使用 fluent API。这是执行此操作的标准方法:
class Class1Map : EntityTypeConfiguration<Class1>
public Class1Map()
this.HasKey(c => c.Id);
this.Property(c => c.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.HasRequired(c1 => c1.Class2).WithRequiredPrincipal(c2 => c2.Class1);
在上下文中:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
modelBuilder.Configurations.Add(new Class1Map());
这是你的课程的左边:
public class Class1
public int Id get;set;
public virtual Class2 Class2 get;set;
public class Class2
public int Id get;set;
public virtual Class1 Class1 get;set;
无法在模型中配置备用外键属性,因为唯一涉及的外键必须是依赖项的主键。
这个模型的奇怪之处在于 EF 不会阻止您创建(和保存)class1
对象没有class2
。我认为 EF 应该能够在保存更改之前验证此要求,但显然它没有。同样,有一些方法可以删除 class2
对象而不删除其 class1
父对象。所以这对HasRequired
- WithRequired
并不像看起来(并且应该)那么严格。
数据注释
在代码中做到这一点的唯一方法是通过数据注释。 (当然数据库模型仍然无法执行 1:1)
public class Class1
public int Id get;set;
[Required]
public virtual Class2 Class2 get;set;
public class Class2
[Key, ForeignKey("Class1")]
public int Id get;set;
[Required]
public virtual Class1 Class1 get;set;
[Key, ForeignKey("Class1")]
注释告诉 EF Class1
是主体实体。
数据注解在许多 API 中都发挥着作用,这可能是一个诅咒,因为每个 API 都选择自己的子集来实现,但在这里它派上了用场,因为现在 EF 不仅使用它们来设计 数据模型,还用于验证实体。现在,如果您尝试在没有 class2
的情况下保存 class1
对象,您将收到验证错误。
【讨论】:
流畅的 API 方法非常棒,与注解方法相比,它对配置更有意义。 +1。【参考方案2】:我遇到了完全相同的问题。 我想要的是数据库模式有 2 个表,它们使用 [外键] --> [主键] 相互引用。 最后我找到了方法: 假设我们有 2 个类:Books 和 Authors。 Book 类应该有一个指向作者的外键,而 Author 类应该有一个指向他写的最后一本书的外键。 首先使用代码让 EF 理解这一点的方法是: (请注意,这是使用数据注释和流式 API 的混合完成的)
public class Book
...
public Guid BookId
...
public Guid AuthorId get; set;
[ForeignKey("AuthorId")]
public virtual Author author get; set;
public class Author
...
public Guid AuthorId
...
public Guid? LatestBookId get; set;
[ForeignKey("LatestBookId")]
public virtual Book book get; set;
public virtual ICollection<Book> books get; set;
// using fluent API
class BookConfiguration : EntityTypeConfiguration<Book>
public BookConfiguration()
this.HasRequired(b => b.author)
.WithMany(a => a.books);
这有效并创建了我想要的确切数据库架构。在 SQL 中,它将创建对应于以下代码的表和外键:
CREATE TABLE [dbo].[Book](
[BookId] [uniqueidentifier] NOT NULL,
[AuthorId] [uniqueidentifier] NOT NULL,
...
CONSTRAINT [PK_dbo.Book] PRIMARY KEY CLUSTERED
(
[BookId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
...
GO
ALTER TABLE [dbo].[Book] WITH CHECK ADD CONSTRAINT [FK_dbo.Book.Author_AuthorId] FOREIGN KEY([AuthorId])
REFERENCES [dbo].[Author] ([AuthorId])
GO
...
CREATE TABLE [dbo].[Author](
[AuthorId] [uniqueidentifier] NOT NULL,
[LatestBookId] [uniqueidentifier] NULL,
...
CONSTRAINT [PK_dbo.Author] PRIMARY KEY CLUSTERED
(
[AuthorId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
...
GO
ALTER TABLE [dbo].[Author] WITH CHECK ADD CONSTRAINT [FK_dbo.Author_dbo.Book_LatestBookId] FOREIGN KEY([LatestBookId])
REFERENCES [dbo].[Book] ([BookId])
GO
...
【讨论】:
这不是一对一的关系【参考方案3】:两个类中的一个必须在另一个之前创建,因此需要 [Required] 注释。如果 Class2 依赖于 Class1,则指定 [Required, ForeignKey("Class1")]。您也可以使用 fluent API 在您的上下文类中进行配置。
【讨论】:
我尝试将 [Required] 数据注释添加到 class2 中的外键,但仍然遇到相同的错误。 (我还更新了我的代码以在我的问题中显示这一点) 啊,你的外键值应该引用你在每个类中的 int 值,而不是 Class 对象。因此,外键应该是“Class1Id”,而不是“Class1”。 Class2 也是如此。 我也试过了(更新了我的问题代码),但我仍然遇到同样的错误。 奇怪的事情:当我删除 Class1Id 和 Class2Id 属性时,我不再收到错误 - 什么给出了?以上是关于EF Code-First 一对一关系:多重性在 Role * in relationship 中无效的主要内容,如果未能解决你的问题,请参考以下文章
15.翻译系列:EF 6中的级联删除EF 6 Code-First 系列
EntityFramework Code-First 简易教程-------一对多
EntityFramework Code-First 简易教程-------一对一