C# EF Code First一对一关系:多重性在关系中的角色*中无效

C# EF Code First一对一关系:多重性在关系中的角色*中无效,c#,database,entity-framework,ef-code-first,entity-framework-6,C#,Database,Entity Framework,Ef Code First,Entity Framework 6,我正在尝试执行以下操作: 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] publi

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

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\u Class2\u目标::多重性在角色中无效 关系“Class2\u Class1”中的“Class2\u Class1\u目标”。因为 依赖角色属性不是键属性,而是上限 依赖角色的多重性必须为“*”


这里有什么问题?

两个类中的一个必须先创建,因此需要[Required]注释。如果Class2依赖于Class1,则指定[Required,ForeignKey(“Class1”)]。您还可以在上下文类中使用fluent API来配置它。

您的模型不是1:1关联。您仍然可以有许多
Class2
对象引用同一个
Class1
对象。此外,您的模型不能保证引用
Class1
Class2
也被该
Class1
对象引用回-
Class1
可以引用任何
Class2
对象

如何配置1:1? 在SQL中保证(某种程度上)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);
    }
}
这是你们剩下的课程:

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并没有阻止您创建(和保存)一个没有
class2
class1
对象。我认为EF应该能够在保存更改之前验证此需求,但显然,它没有。同样,也有一些方法可以删除
class2
对象,而不删除其
class1
父对象。所以这对
HasRequired
-
WithRequired
并不像看上去那样严格(而且应该如此)

数据注释

在代码中实现这一点的唯一方法是通过数据注释。(当然,数据库模型仍然无法强制执行1:1)

[Key,ForeignKey(“Class1”)]
注释告诉EF
Class1
是主要实体


数据注释在许多API中扮演着一个角色,这可能是一个诅咒,因为每个API都选择自己的子集来实现,但在这里它很方便,因为现在EF不仅使用它们来设计数据模型,而且还用来验证实体。现在,如果您试图在没有
class2
的情况下保存
class1
对象,您将得到一个验证错误。

我遇到了完全相同的问题。 我想要的是DB模式有两个表,它们使用[foreign key]-->[primary key]相互交叉引用。 最后我找到了方法: 假设我们有两类:书籍和作者。 Book类应该有作者的外键,author类应该有他写的最后一本书的外键。 让EF首先使用代码理解这一点的方法是: (请注意,这是通过混合使用数据注释和fluent API完成的)


我尝试在class2的外键中添加[Required]数据注释,但仍然得到相同的错误。(我还更新了我的代码以在我的问题中显示这一点)啊,外键值应该引用每个类中的int值,而不是类对象。因此,外键应该是“ClassID”,而不是“Class1”。Class2也是如此。我也尝试过(更新了我的问题代码),但仍然得到了相同的错误。奇怪的是:当我删除ClassID和Class2Id属性时,我再也没有得到错误了-给出了什么?fluent API方法令人惊讶,与annotations方法相比,它在配置方面很有意义+1.这不是一对一的关系
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;}
}
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;}
}
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);
    }

}
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

...