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 对象。此外,您的模型不保证引用 Class1Class2 也被此 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 简易教程-------一对一

Database-First,Model-First,Code-first

EF Code-First 学习之旅

20.1翻译系列:EF 6中自动数据迁移技术EF 6 Code-First系列