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.
考虑这些简单的 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.