提问人:guirms 提问时间:10/10/2023 最后编辑:marc_sguirms 更新时间:10/11/2023 访问量:77
为什么我需要在 Entity Framework Core 关系中传递模型类而不是 id
Why do I need to pass the model class instead of id in Entity Framework Core relationship
问:
假设我在 .NET 中有一个名为“B”的模型类,它具有模型类“A”所需的 fk(一对一关系)。
模型的代码应如下所示:
public class A
{
public int Id { get; set; }
public virtual required B ClassB { get; set; }
}
public class B
{
public int Id { get; set; }
public int A_ID { get; set; }
public virtual required A ClassB { get; set; }
}
现在,假设我在表 “A” 中有一条 id 为“1” 的记录,我想在类 “B” 中添加一条记录,该记录的 fk 为 1。
在我看来,这段代码应该足够了:
var classBToAdd = new B
{
A_ID = 1
}
_myRepo.Add(classBToAdd);
但我不能这样做,因为关系是必需的:
public virtual required A ClassB { get; set; }
如果不是必需的,我会收到以下警告:
退出构造函数时,不可为 null 的变量必须包含非 null 值。请考虑将其声明为可为 null。
为什么需要指定完整的对象?在某些情况下,这可能意味着我需要执行一个大查询才能保存完整的对象。
答:
代码中的 public int A_ID { get; set; } 行不会自动标识外键。正确的代码是:
public int AId { get; set; }
如果 A 类对象需要尚不存在的 B 类对象,如何先创建该类对象(以获取其引用 ID)?
此代码有效:
public class A
{
public int Id { get; set; }
public virtual B ClassB { get; set; }
}
public class B
{
public int Id { get; set; }
public int AId { get; set; }
public virtual A ClassA { get; set; }
}
...并在您的 Main() 中:
A newClassA = new A();
db.Add(newClassA);
db.SaveChanges();
B classBToAdd = new B
{
AId = newClassA.Id
};
db.Add(classBToAdd);
db.SaveChanges();
评论
SaveChanges
SaveChanges()
法比奥走在正确的轨道上,但有一个更简单的答案。用于标识 FK 的 EF 约定基于类型,而不是属性名称。这意味着,如果要声明不遵循隐含约定的 FK,则需要告诉 EF 要使用什么:
public class B
{
public int Id { get; set; }
public int A_ID { get; set; }
[ForeignKey(nameof(A_ID))]
public virtual A ClassA { get; set; }
}
您不需要必需的,因为 A 和 A_ID 都不能为 null。现在,如果要单独创建 B,只需将其与 A 记录关联,而不使用 ClassA 导航属性,则它应该按预期工作。
如果您要同时创建 A 和 B,则采用最简单的形式:
var newA = new A() { /* populate A details */ };
newA.Bs.Add(new B() { /* populate B details */ });
_context.As.Add(newA);
_context.SaveChanges();
如果关系映射正确,EF 会自动计算出插入顺序和 FK 关联,这就是使用 ORM 与 ADO 样式 CRUD 操作的意义所在。
我建议不要在 EF DbContext 之上使用似乎是存储库的模式。DbContext 已充当工作单元容器,而 DbSet 充当存储库,因此尝试抽象这些既没有必要,也容易给 EF 可以提供的功能和性能带来问题和限制。可以说,在 EF 上引入 UoW 和 Repository 模式是有正当理由的,例如促进单元测试或实现核心过滤/授权检查,但在开始时,我建议直接使用 DbContext 和 DbSets,以避免引入复杂性。
编辑:不可为 Null 的引用类型错误和 EF。编译器将发出警告,或者如果您的项目选择了严格强制执行,并且您有未填充的必需 ClassA,则会出现异常。
解决此问题的最正确方法是:
[Required]
public virtual A? ClassA { get; set; }
其他选项包括:
public virtual A ClassA { get; set; } = null!;
或者对构造函数使用宽恕标记:
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public B()
{ }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
- 请注意,此构造函数用于类 B,并禁用此类的警告/异常,因此在我们的示例中,关于非 null ClassA 引用。我通常有公共构造函数来初始化具有所有必填字段的实体,因此您可以将此默认构造函数标记为 ,否则,如果您只是在实体上使用默认构造函数(或没有构造函数),请使此 .
protected
public
评论
[Required]
= null!;
评论