Entity Framework Core 2.1 - owned types and nested value objects

笑着哭i 提交于 2020-06-23 07:23:32

问题


I'm learning DDD and the tutorial I'm currently following is implemented using NHibernate, but since my lack of experience with it I've decided to go through the course using EF Core 2.1.

However, I'm currently a bit stuck with the following: I have three classes Customer which is an entity and two value objects (CustomerStatus and inside of it value object ExpirationDate) - like this:

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; }
}

When I try to do the following inside of my 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");
     });
});

I'm getting the following error:

The expression 'x => x.ExpirationDate.Date' is not a valid property expression. The expression should represent a simple property access: 't => t.MyProperty'.
Parameter name: propertyAccessExpression'

Beside this I've tried doing things like:

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");
      });

But it also leads to:

The expression 'x => x.Status.ExpirationDate' is not a valid property expression. The expression should represent a simple property access: 't => t.MyProperty'.

I've also tried:

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

But with no luck as well... Anyways, any help would be greatly appreciated, also if possible it would be really great if someone could explain why none of my tries are not working? Thanks in advance!

UPDATE

After doing as stated in Ivan's comment first I was getting error about CustomerStatus class constructor so I've added default protected one.

After that I started getting error:

Field 'k__BackingField' of entity type 'CustomerStatus' is readonly and so cannot be set.

Here's inner of my CustomerStatus class if it helps:

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();
    }
}

UPDATE

All it took was adding private setters on Type and ExpirationDate properties inside of CustomerStatus class and in combination with Ivan's answer it works like a charm. Thanks a lot!


回答1:


Your attempts are not working because owned types can only be configured through their owner entity, and more specifically, through their own builder returned by the OwnsOne method or provided as an argument of the Action<T> argument of the OwnsOne method of the owner entity builder.

So the configuration should be something like this (note the nested 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");
        });
    });
});


来源:https://stackoverflow.com/questions/53652135/entity-framework-core-2-1-owned-types-and-nested-value-objects

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!