EF Core 和 DDD:将 ValueObjects 存储在与实体相同的 table 中,还使用参数化构造函数在实体上设置 属性 值

EF Core and DDD: Store ValueObjects in the same table as Entities, also use parametrized constructors to set property values on Entities

考虑这些简单的 classes。它们属于具有领域驱动设计 (DDD) 原则的简单应用程序,因此每个 Entity 和 ValueObject 通过构造函数接收其 属性 值,同时隐藏默认的无参数构造函数。属性也将是只读的。

    public class MyClass
    {
       public Guid Id {get;}
       public ValueObject ValueObject1 {get;}
       public ValueObject ValueObject2 {get;}
       public MyClass(ValueObject valueObject1, ValueObject valueObject2) 
       {
          ValueObject1 = valueObject1;
          ValueObject2 = valueObject2;
       }
       private MyClass(){}
    }

    public class ValueObject
    {
       public string Value {get;}
       public ValueObject(string value) 
       {
          Value = value;
       }
       private ValueObject(){}
    }

我希望能够使用 EntityFramework Core 2.2.6.

基于此模型创建数据库

显然,EF Core 2.2.6 可以通过它们的参数化构造函数自动为这些 class 提供 属性 值,只要构造函数参数和 class 属性具有相同的名称(不区分大小写)。太好了。

现在我希望将 ValueObject 存储在与 MyClass 相同的 table 中。有人告诉我,要做到这一点,我应该在 DBContext 的 OnModelCreating 中使用 modelBuilder.OwnsOne<>,而不是 modelBuilder.Property<>

OnModelCreating 中的 DBContext 配置如下所示:

modelBuilder.Entity<MyClass>(b => b.HasKey(mc => mc.Id));
modelBuilder.Entity<MyClass>(b => b.OwnsOne(mc => mc.ValueObject1,rb =>
        {
            rb.Property(vo => vo.Value);
        }));
modelBuilder.Entity<MyClass>(b => b.OwnsOne(mc => mc.ValueObject2, rb =>
        {
            rb.Property(vo => vo.Value);
        }));

现在看来 modelBuilder.OwnsOne<>modelBuilder.Property<> 是互斥的,这意味着你不能同时使用它们,因为每次我尝试对它们都使用 Add-Migration 时,我得到:

'ValueObject' cannot be used as a property on entity type 'MyClass' because it is configured as a navigation.

但是如果我不使用 modelBuilder.Property<> 而只使用 modelBuilder.OwnsOne<>,我会得到:

No suitable constructor found for entity type 'MyClass'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'valueObject1', 'valueObject2' in 'MyClass(ValueObject valueObject1, ValueObject valueObject2)'.

这意味着 属性 绑定模式的构造函数仅在我使用 modelBuilder.Property<> 配置 MyClass.

上的属性时才有效

所以我的问题是:我应该如何配置 DBContext 以允许 EF Core 通过参数化构造函数设置 属性 值, 将 ValueObjects 存储在同一个table作为实体?

事情是这样的。 正如@Gert Arnold 指出的那样:

1.您需要在域模型的所有属性上都有私有设置器。从版本 2.2.6 开始,EF Core 无法使用只读属性。

但这不是我的问题。结果我忘了在我自己的项目中包含一个相当于 MyClass 的私有构造函数。我只是希望在我花费数小时的工作并弄明白之前看到@Ivan Stoev 的评论。 EF Core给我的报错信息太隐晦,没有指出问题所在:

No suitable constructor found for entity type 'MyClass'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'valueObject1', 'valueObject2' in 'MyClass(ValueObject valueObject1, ValueObject valueObject2)'.

实际上,那个特定的构造函数没有问题。

2。 如果您希望 EF Core 正确使用构造函数绑定并通过构造函数参数将值提供给您的属性,您只需拥有一个私有的、无参数的构造函数。 不是这种情况。 EF Core 根本无法使用构造函数绑定将实体注入其他实体。 它基本上是在告诉我们不能使用特定的构造函数,并且因为它根本找不到要使用的 suitable 构造函数,所以通过提供无参数构造函数,你给了它一种创建方法没有构造函数绑定的对象

3。您应该在 DbContext.OnModelCreating 中使用 modelBuilder.OwnsOne<> 而不是 modelBuilder.Property<> 来为实体(在 DDD 中)配置值对象,以将其存储在与实体相同的数据库 table 中。

我认为 EF Core 需要给你一个更清晰的信息,说明当你没有私有的、无参数的构造函数时,它是如何混淆它应该使用哪个构造函数的。 I'll bring it up with the EF Core team.