实体框架核心2.1-拥有的类型和嵌套值对象

时间:2018-12-06 13:05:13

标签: c# domain-driven-design value-objects entity-framework-core-2.1 owned-types

我正在学习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();
    }
}

更新

只需要在Type类内的ExpirationDateCustomerStatus属性上添加私有设置器,并结合Ivan的回答,就可以像魅力一样。非常感谢!

1 个答案:

答案 0 :(得分:6)

您的尝试无效,因为只能通过其 owner 实体(更具体地说,通过OwnsOne返回的其自己的构建器来配置拥有的类型方法或作为所有者实体构建器的Action<T>方法的OwnsOne参数的参数提供。

因此配置应如下所示(请注意嵌套的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");
        });
    });
});