EF Code-First 一对一关系:多重性在关系中的 Role * 中无效

问题描述 投票:0回答:3

我正在尝试执行以下操作:

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 属性不是关键属性,上限 依赖角色的重数必须是“*”。

这里可能出现什么问题?

c# database entity-framework ef-code-first entity-framework-6
3个回答
71
投票

您的模型不是 1:1 关联。您仍然可以有 many

Class2
对象引用同一个 one
Class1
对象。此外,您的模型不保证引用
Class2
Class1
也被此
Class1
对象引用 —
Class1
可以引用任何
Class2
对象。

如何配置1:1?

在 SQL 中保证(某种程度上)1:1 关联的常见方法是为principal实体创建一个表,并为dependent实体创建一个表,其中从属表中的主键也是校长:

1:1

(这里

Class1
是主体)

现在在关系数据库中,这仍然不能保证 1:1 关联(这就是我说“某种程度上”的原因)。这是一个 1:0..1 关联。可以有

Class1
而没有
Class2
。事实上,真正的 1:1 关联在 SQL 中是不可能的,因为没有语言结构可以在不同的表中同步插入两行。 1:0..1 是我们得到的最接近的值。

流畅的映射

要在 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;}
}

无法在模型中配置备用外键属性,因为涉及的唯一 FK 必须是依赖者的主键。

此模型的奇怪之处在于,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 不仅使用它们来

设计数据模型,还验证实体。现在,如果您尝试保存没有 class1

class2
 对象,您将收到验证错误。


2
投票
我遇到了类似的问题(不是 1:1 的解决方案,但可能对其他人有价值)。 我想要的是数据库模式有 2 个表,它们通过 [外键] --> [主键] 相互交叉引用。 最后我找到了方法: 假设我们有 2 个类:书籍和作者。 Book 类应该有一个作者的外键,而 Author 类应该有一个他写的最后一本书的外键。 首先使用代码让 EF 理解这一点的方法是: (请注意,这是使用数据注释和 Fluent 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 ...
    

-1
投票
两个类中的一个必须在另一个之前创建,因此需要 [Required] 注释。如果 Class2 依赖于 Class1,则指定 [Required,foreignKey("Class1")]。您还可以使用 Fluent API 在您的上下文类中进行配置。

© www.soinside.com 2019 - 2024. All rights reserved.