【问题标题】:Map custom list of object as json in Entity Framework在实体框架中将对象的自定义列表映射为 json
【发布时间】:2018-03-11 17:45:30
【问题描述】:

我正在练习 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;
    }
}

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

然后我得到以下错误:

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

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

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

我该如何解决这个问题?

附言。我从this site得到这个想法

【问题讨论】:

  • 抱歉,几分钟后就要回家了,所以只是简短的评论:你看过[Backing Fields]docs.microsoft.com/en-us/ef/core/modeling/backing-field吗?这应该允许您让您的电话有一个私有 id 变量(在对象之外无法访问),该变量可用于外键关系的字段。它只是将一些持久性泄漏到您的域中

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


【解决方案1】:

迁移引发错误,因为 EF 正在尝试映射您的属性 User.PhoneNumbers。您必须告诉 EF 忽略该属性并映射您的字符串字段 User._phoneNumbers

为此,您可以使用注释或 fluent 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 的属性,但它没有预期的字符串类型。在这种情况下,您应该重命名您的字段或属性,以确保它们不遵循支持字段命名约定(一个不是带有“_”前缀的另一个的驼峰式版本)。

通过明确忽略该属性,您的错误应该会消失。 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 决定映射哪些实体的更多信息。

【讨论】:

    【解决方案2】:

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

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

    我所说的是基于 Vaughn Vernon 的书“实施 DDD”。以下是相关章节中的一段:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-21
      • 1970-01-01
      • 2011-08-14
      • 1970-01-01
      • 2015-10-09
      相关资源
      最近更新 更多