一对一映射问题
本文关键字:问题 映射 一对一 | 更新日期: 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
,除了使其成为一个键。
如何使Serial
在SerialNumber
中成为UserProfile
和User
的可选属性,当它添加到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
,只有当你没有从查询中得到任何反馈时才保存它)。