.net 为什么我需要在实体框架核心关系中传递模型类而不是id

jjjwad0x  于 2023-10-21  发布在  .NET
关注(0)|答案(2)|浏览(96)

假设我在.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; }

如果不需要,我会收到以下警告:
退出构造函数时,不可为空的变量必须包含非空值。考虑将其声明为可空。
为什么我需要指定完整对象?在某些情况下,这可能意味着我需要执行一个大型查询,以便保存完整的对象。

txu3uszq

txu3uszq1#

法比奥走在正确的道路上,但有一个更简单的答案。用于标识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都是不可空的。现在,如果您想单独创建一个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();

如果关系Map正确,EF会自动计算插入顺序和FK关联,这是使用ORM与ADO风格的CRUD操作。
我建议不要在EF DbContext之上使用似乎是存储库模式的东西。DbContext已经充当了工作容器的单元,DbSets充当了存储库,因此尝试抽象这些是不必要的,并且容易引入EF可以提供的功能和性能的问题和限制。可以说,在EF之上引入UoW和Repository模式是有正当理由的,例如促进单元测试或实现核心过滤/授权检查,但在开始时,我建议直接使用DbContext和DbSets以避免引入复杂性。
编辑:不可空引用类型错误和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的,它禁用了该类的警告/异常,在我们的例子中,是关于非空的ClassA引用的。我通常有一个公共构造函数来初始化所有必需字段的实体,所以你可以将这个默认构造函数标记为protected,否则如果你只是在实体上使用默认构造函数(或者没有构造函数),就把这个标记为public
83qze16e

83qze16e2#

public int A_ID { get; set; }**不会自动标识外键。正确的代码是:

public int AId { get; set; }

如果A类对象需要一个还不存在的B类对象,那么如何首先创建A类对象(以获取其引用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();

相关问题