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

Map custom list of object as json in Entity Framework

我正在练习 DDD,我有一个 class,也就是所谓的 ValueObject。在 Entity 中,我有那个 ValueObject 的自定义列表。我试图将该集合保存为 JSON.

我有以下 classes:

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

当我 运行 使用命令的 EF Core 迁移工具时:Add-Migration InitialCreate

然后我得到以下错误:

Error: The entity type 'PhoneNumber' requires a primary key to be defined.

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

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

我该如何解决这个问题?

PS。我从 this site

得到这个想法

迁移引发错误,因为 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 会隐式处理私有字段作为 [=42] 的支持字段=] 属性 基于命名约定,但这不是你的情况,因为数据类型不同。有关 EF here.

中支持字段的更多信息

正因为如此,您在尝试映射 _phoneNumbers 字段时也可能会出错,因为已经有一个名为 PhoneNumbers 的 属性 但它没有预期的字符串类型。在这种情况下,您应该重命名您的字段或 属性 以确保它们不遵循支持字段命名约定(一个不是另一个带有“_”前缀的驼峰版本)。

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

中明确忽略这些实体
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    //Do not map these entities
    modelBuilder.Ignore<PhoneNumber>();
    modelBuilder.Ignore<PhoneList>();
    ...
}

有关 EF 如何根据约定决定要映射哪些实体的更多信息 here

The whole point is that I don't want to add a primary key to PhoneNumber, because it's a ValueObject.

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

我所说的是基于 Vaughn Vernon "Implementing DDD" 的书。这是相关章节的一段话:

There are times, however, when a Value Object in the model will of necessity be stored as an Entity with respect to a relational persistence store. In other words, when persisted, an instance of a specific Value Object type will occupy its own row in a relational database table that exists specifically for its type, and it will have its own database primary key column. This happens, for example, when supporting a collection of Value Object instances with ORM. In such cases the Value type persistent data is modeled as a database entity.