在Entity Framework中将对象的自定义列表映射为json

时间:2018-03-11 17:45:31

标签: entity-framework domain-driven-design entity-framework-core value-objects

我正在练习DDD,我有一个叫做ValueObject的课程。在Entity内,我有ValueObject的自定义列表。我试图将该集合保持为JSON。

我有以下课程:

public class User : Entity
{
    public string Username {get;set;}

    private string _phoneNumbers = string.Empty;
    public virtual PhoneList PhoneNumbers
    {
        get => (PhoneList)_phoneNumbers;
        set => _phoneNumbers = value;
    }
}

// A class to map the IList of PhoneNumber to string and vice versa
public class PhoneList : IEnumerable<PhoneNumber>
{
    private IList<PhoneNumber> PhoneNumbers{ get; }

    public PhoneList(IList<PhoneNumber> phoneNumbers)
    {
        PhoneNumbers = phoneNumbers;
    }

    public static explicit operator PhoneList(string phoneList)
    {
        var phoneNumbers = JsonConvert.DeserializeObject<IList<PhoneNumber>>(phoneList);
        return new PhoneList(phoneNumbers);
    }

    public static implicit operator string(PhoneList phoneList)
    {
        return JsonConvert.SerializeObject(phoneList);
    }

    public IEnumerator<PhoneNumber> GetEnumerator()
    {
        return PhoneNumbers.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class PhoneNumber : ValueObject
{
    public string Number {get;set;}
    protected override IEnumerable<object> GetAtomicValues()
    {
        yield return Number;
    }
}

当我使用命令Add-Migration InitialCreate

运行EF Core迁移工具时

然后我收到以下错误:

  

错误:实体类型'PhoneNumber'需要定义主键。

重点是我不想向PhoneNumber添加主键,因为它是ValueObject

我也不明白为什么会在迁移中抛出错误。

我该如何解决这个问题?

PS。我从this site

得到了这个想法

2 个答案:

答案 0 :(得分:2)

迁移导致错误,因为EF正在尝试映射您的媒体资源User.PhoneNumbers。您必须告诉EF忽略该属性并改为映射您的字符串字段User._phoneNumbers

为此,您可以使用注释或流畅的API。检查this page以获取两者的示例。

使用流畅的API:

class MyDbContext : DbContext
{
    public DbSet<User> Users { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>()
            .Ignore(u => u.PhoneNumbers)   //Do not map property
            .Property("_phoneNumbers");    //Map field
        ...
    }
}

如果PhoneNumbers_phoneNumbers的数据类型相同,则您不需要执行任何操作,因为EF会隐式处理私有字段作为基于公共属性的支持字段关于命名约定,但这不是你的情况,因为数据类型是不同的。有关EF here中支持字段的详细信息。

由于这一点,您在尝试映射_phoneNumbers字段时可能会收到错误,因为已存在名为PhoneNumbers的属性,但它没有预期的字符串类型。在这种情况下,您应该重命名您的字段或您的属性,以确保它们不遵循支持字段命名约定(一个不是另一个带有&#39; _&#39;前缀的camelcase版本)。

通过明确忽略该属性,您的错误应该消失。 EF仅映射DbSet中具有DbContext的实体,这些实体在其他映射实体的导航属性中使用,或者包含在OnModelCreating方法中。无论如何,如果您仍然使用ValueObject实体发现迁移错误,则可以在OnModelCreating中明确忽略实体:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    //Do not map these entities
    modelBuilder.Ignore<PhoneNumber>();
    modelBuilder.Ignore<PhoneList>();
    ...
}

有关EF如何按惯例here决定要映射哪些实体的详细信息。

答案 1 :(得分:0)

  

重点是我不想将主键添加到PhoneNumber,因为它是一个ValueObject。

PhoneNumber确实是Value object,但仅限于域层的上下文中。从持久性的角度来看,PhoneNumber 可能Entity,这不是针对DDD的。如果您将其视为Value object,那么您将遵循DDD方法。持久性是另一种与其他武器作战的野兽。

我所说的是基于Vaughn Vernon的书,&#34;实施DDD&#34;。以下是相关章节中的段落:

  

然而,有时候,模型中的值对象会出现   必要性存储为关系的实体   持久性存储。换句话说,当持久化时,一个实例   特定的值对象类型将在关系中占据自己的行   专门为其类型存在的数据库表,它将具有   它自己的数据库主键列。例如,这发生在   使用ORM支持Value Object实例的集合。在这样的   在这种情况下,Value类型持久数据被建模为数据库实体。