Entity Framework Core 2.1 - 拥有类型和嵌套值对象

Posted

技术标签:

【中文标题】Entity Framework Core 2.1 - 拥有类型和嵌套值对象【英文标题】:Entity Framework Core 2.1 - owned types and nested value objects 【发布时间】:2021-10-19 17:53:48 【问题描述】:

我正在学习 DDD,我目前正在学习的教程是使用 NHibernate 实现的,但由于我缺乏这方面的经验,我决定使用 EF Core 2.1 完成课程。

但是,我目前有点坚持以下几点:我有三个类Customer,这是一个实体和两个值对象(CustomerStatus 和它内部的值对象ExpirationDate) - 像这样:

public class Customer : Entity

    //... constructor, other properties and behavior omitted for the sake of simplicity
    public CustomerStatus Status  get; set; 


public class CustomerStatus : ValueObject<CustomerStatus>

    // customer status is enum containing two values (Regular,Advanced)
    public CustomerStatusType Type  get; 
    public ExpirationDate ExpirationDate  get; 


public class ExpirationDate : ValueObject<ExpirationDate>

    //... constructor, other properties and behavior omitted for the sake of simplicity
    public DateTime? Date  get; private set; 

当我尝试在我的DbContext 中执行以下操作时:

modelBuilder.Entity<Customer>(table =>
      
   table.OwnsOne(x => x.Status,
     name =>
     
        name.Property(x => x.ExpirationDate.Date).HasColumnName("StatusExpirationDate");
        name.Property(x => x.Type).HasColumnName("Status");
     );
);

我收到以下错误:

表达式“x => x.ExpirationDate.Date”不是有效的属性表达式。该表达式应该表示一个简单的属性访问:'t => t.MyProperty'。 参数名称:propertyAccessExpression'

除此之外,我还尝试过以下操作:

table.OwnsOne(x => x.Status.ExpirationDate,
      name =>
      
         name.Property(x => x.Date).HasColumnName("StatusExpirationDate");
      );
 table.OwnsOne(x => x.Status,
      name =>
      
          name.Property(x => x.Type).HasColumnName("Status");
      );

但这也导致:

表达式“x => x.Status.ExpirationDate”不是有效的属性表达式。该表达式应该表示一个简单的属性访问:'t => t.MyProperty'。

我也试过了:

modelBuilder.Entity<Customer>()
                    .OwnsOne(p => p.Status, 
              cb => cb.OwnsOne(c => c.ExpirationDate));

但也没有运气......无论如何,任何帮助将不胜感激,如果有人能解释为什么我的尝试都不起作用,如果可能的话,那将是非常棒的?提前致谢!

更新

首先按照 Ivan 的评论中所述进行操作后,我收到了关于 CustomerStatus 类构造函数的错误,因此我添加了默认受保护的构造函数。

之后我开始收到错误:

实体类型“CustomerStatus”的字段“k__BackingField”是只读的,因此无法设置。

如果有帮助,这是我的 CustomerStatus 课程的内部:

public class CustomerStatus : ValueObject<CustomerStatus>

    public CustomerStatusType Type  get; 
    public ExpirationDate ExpirationDate  get; 

    public static readonly CustomerStatus Regular =
        new CustomerStatus(CustomerStatusType.Regular, ExpirationDate.Infinite);
    public bool IsAdvanced => Type == CustomerStatusType.Advanced && !ExpirationDate.IsExpired;

    private CustomerStatus(CustomerStatusType type, ExpirationDate expirationDate)
    
        Type = type;
        ExpirationDate = expirationDate;
    

    protected CustomerStatus()
    

    
    public static CustomerStatus Create(CustomerStatusType type, ExpirationDate expirationDate)
    
        return new CustomerStatus(type, expirationDate);
    

    public CustomerStatus Promote()
    
        return new CustomerStatus(CustomerStatusType.Advanced, ExpirationDate.Create(DateTime.UtcNow.AddYears(1)).Value);
    

    protected override bool EqualsCore(CustomerStatus other)
    
        return Type == other.Type && ExpirationDate == other.ExpirationDate;

    

    protected override int GetHashCodeCore()
    
        return Type.GetHashCode() ^ ExpirationDate.GetHashCode();
    

更新

只需要在 CustomerStatus 类内的 TypeExpirationDate 属性上添加私有设置器,并结合 Ivan 的答案,它就像一个魅力。非常感谢!

【问题讨论】:

【参考方案1】:

您的尝试无效,因为拥有的类型只能通过它们的 owner 实体进行配置,更具体地说,通过 OwnsOne 方法返回的它们自己的构建器或作为所有者实体构建器的OwnsOne 方法的Action&lt;T&gt; 参数的参数提供。

所以配置应该是这样的(注意嵌套的OwnsOne):

modelBuilder.Entity<Customer>(customer =>
      
    customer.OwnsOne(e => e.Status, status =>
    
        status.Property(e => e.Type).HasColumnName("Status");
        status.OwnsOne(e => e.ExpirationDate, expirationDate =>
        
            expirationDate.Property(e => e.Date).HasColumnName("StatusExpirationDate");
        );
    );
);

【讨论】:

感谢您的快速回复和解释,但是当我按照说明进行操作时,我收到了新错误,其中指出:实体类型“CustomerStatus”的“字段”k__BackingField 是只读的所以不能设置。我会用新问题更新我的问题并添加 CustomerStatus 类的属性... 该死,只需要在两个属性上添加私有设置器...对不起我的愚蠢-.-我将您的答案标记为正确答案,再次感谢一吨!干杯 我要添加评论说 EF (Core) 不能很好地与 DDD 和值对象配合使用,因为 EF Core 对“实体”类型、它们的构造函数、属性、字段等有自己的要求。通常与 DDD 不同。但我决定不这样做,因为这不是问题的一部分。

以上是关于Entity Framework Core 2.1 - 拥有类型和嵌套值对象的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework core 2.1 多对多选择查询

使用 Entity Framework Core (2.1) 调用标量函数的最佳实践

Entity Framework Core 2.1 无法更新具有关系的实体

Entity Framework Core 2.1 - 拥有类型和嵌套值对象

Entity Framework Core快速开始

Entity Framework Core 快速开始