一对一映射问题

本文关键字:问题 映射 一对一 | 更新日期: 2023-09-27 18:07:30

我对SQL的理解是相当基本的,我来自一个NHibernate的世界,所以我对这个问题相当困惑…

我有两个类:

public class UserProfile
{
    public UserProfile() { }
    [Key]
    public int UserId { get; set; }
    public string UserName { get; set; }
    public SerialNumber Serial { get; set; }
    // Other properties
}

public class SerialNumber
{
    public SerialNumber() 
    {
        // My code that creates a unique Code
    }
    [Key]
    public string Code { get; set; }
    public UserProfile User { get; set; }
    // Other Properties
}

我有这个

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Other modelBuilder stuff
    modelBuilder.Entity<UserProfile>()
        .HasOptional(s => s.Serial)
        .WithRequired(u => u.User);
}

在我运行Add-Migration之后,我得到这个:

public override void Up()
{
    DropForeignKey("dbo.UserProfile", "Serial_Code", "dbo.SerialNumbers");
    DropIndex("dbo.UserProfile", new[] { "Serial_Code" });
    RenameColumn(table: "dbo.SerialNumbers", name: "Serial_Code", newName: "User_UserId");
    AddForeignKey("dbo.SerialNumbers", "User_UserId", "dbo.UserProfile", "UserId");
    CreateIndex("dbo.SerialNumbers", "User_UserId");
}
public override void Down()
{
    // Inverse of above
}

在我运行Update-Database后,我得到一个错误:"Either the parameter @objname is ambiguous or the claimed @objtype (COLUMN) is wrong."

我可以告诉由Add-Migration生成的代码是错误的,因为我的db表甚至没有一个列的名称为"Serial_Code"

我需要Code是我的Key作为EntityFramework没有一个选项强制列是Unique,除了使其成为一个键。

如何使SerialSerialNumber中成为UserProfileUser的可选属性,当它添加到UserProfile时自动设置?

一对一映射问题

默认情况下,在一对一关系中,实体框架要求依赖项上的外键也是主键。这可能是由于实体框架只在主键上设置唯一约束(或者更确切地说,唯一约束是由于它是主键而存在的)。在数据库级别上,一对一关系没有映射到关系的两边,即没有在两个表上创建外键。当您考虑参照完整性如何工作时,这是有道理的:如果删除关系的一方,它将导致另一方的数据库不完整,从而导致一种无限循环。

因此,外键被添加到依赖项中,在像这里这样的一对零对一的情况下,它是所需导航属性的结尾:您的SerialNumber模型。这意味着要真正创建一对一而不是一对多,您在SerialNumber上的键需要是User_UserId(自动生成的外键列,因为您没有手动指定外键属性)。以这种方式设置它,你的模型看起来像:

public class UserProfile
{
    [Key]
    public int UserId { get; set; }
    public SerialNumber Serial { get; set; }
    ...
}
public class SerialNumber
{
    [Key]
    [ForeignKey("User")]
    public int UserId { get; set; }
    public UserProfile User { get; set; }
    ...
}

或使用Fluent API:

modelBuilder.Entity<SerialNumber>()
    .HasRequired(m => m.User)
    .WithOptional(m => m.Serial);

(您需要去掉[Key]数据注释,以便EF可以制作外键,它们是表的主键。)

但是,

这使您在Code属性上没有唯一的约束。当然,您可以在表上手动设置此约束,或者如果不使用自动迁移,则可以更改迁移以创建约束:

CreateIndex("dbo.SerialNumbers", "Code", unique: true);

这里的问题是实体框架不会为你处理唯一的验证。所以,你需要在你的代码中手动维护完整性(即,尝试用你要保存的Code来查找SerialNumber,只有当你没有从查询中得到任何反馈时才保存它)。

我知道这有点痛苦,但目前,这是你能做的。希望EF最终能在未来的版本中解决唯一性问题。