报错内容:System.InvalidOperationException:"The entity type 'xxx' cannot be configured as owned because it has already been configured as a non-owned. If you want to override previous configuration first remove the entity type from the model by calling 'Ignore'.
原因:我在实体中添加了一个值对象。值对象如下:
有一个枚举类型的属性Weekday,由于显式得未配置 [Owned] 特性 导致报错
cs
public class WeeklyLoopTimeStamp:ValueObject
{
// 日期
public DayOfWeek Weekday
{
get; set;
}
// 时间
public DateTime TimePoint
{
get; set;
}
protected override IEnumerable<object> GetAtomicValues()
{
yield return Weekday;
yield return TimePoint;
}
}
在EF Core中,当你想要将属性标记为值对象,并且希望EF Core将其配置为复合类型(Complex Type)时,你需要在值对象的属性上添加[Owned]特性。
添加[Owned]特性的好处是,EF Core会自动处理该属性与其所属实体之间的映射和关系。它简化了代码,并减少了手动配置的工作量。
然而,并非所有情况下都需要添加[Owned]特性。以下是一些情况说明:
-
当属性的数据类型本身是一个简单的值类型(如string、int、DateTime等)时,EF Core会自动将其视为复合类型的一部分,无需添加[Owned]特性。
-
当属性的数据类型是自定义的类或结构体,并且你希望将其视为独立的实体类型(拥有自己的唯一标识符),而不是复合类型时,你不需要添加[Owned]特性。
-
当属性是一个集合类型(如List、ICollection等)时,EF Core会自动将其配置为导航属性,不需要添加[Owned]特性。
所以,只有当你确实希望将属性配置为值对象并作为复合类型存储在同一个数据库表中时,才需要在值对象的属性上添加[Owned]特性。
如果没有添加[Owned]特性而不报错,很可能是由于EF Core的默认行为能够正确地推断出属性应该被视为复合类型。然而,为了明确你的意图并提高代码的清晰度,建议在值对象的属性上显式添加[Owned]特性。
拓展问题:
1. 值对象在数据表中以何种结构被存储?
在数据库中,如果一个实体拥有一个值对象属性,通常会将该值对象的属性作为实体表中的列进行存储。
简单理解为:值对象的属性会被提取到实体属性中,从而成为单独的一列
对于你提到的WxTemplateForPlanOrderEntity实体,如果WeeklyLoopTimeStamp是一个值对象属性,那么通常会根据数据库的设计,创建一个包含WeeklyLoopTimeStamp属性的列。
具体地,可以根据你所使用的数据库类型和ORM框架的要求,将WeeklyLoopTimeStamp属性的值序列化为适当的数据类型(例如字符串、JSON等),并将其存储在数据库表的相应列中。
例如,在关系型数据库中,你可以将WeeklyLoopTimeStamp属性的值以JSON格式序列化,并将其存储为varchar或json类型的列。在NoSQL数据库中,可能会选择直接将WeeklyLoopTimeStamp属性的值存储为文档或字段的形式。
需要注意的是,数据库中的列与实体的属性之间需要进行映射关系的配置。这通常由ORM框架负责处理,通过特定的注解、配置文件或代码来定义实体属性与数据库列之间的映射关系。
综上所述,数据库的存储方式取决于数据库类型、ORM框架和相关配置,但一般来说,值对象属性会被作为实体表中的单独列进行存储。
2. 如果实体下的值对象是一对多关系?数据表中会如何存储?
例子:
cs
/// <summary>
/// 发送数据时机 按周循环
/// </summary>
public List<WeeklyLoopTimeStamp> WeeklyLoopTimeStamp
{
get;
set;
}
那么需要根据ORM来配置,一般会创建一张另外的关联表。外键与实体相关联。
cs
builder.OwnsMany( );
具体传入的配置方法需要根据项目规范制定。