我正在练习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
然后我收到以下错误:
错误:实体类型'PhoneNumber'需要定义主键。
重点是我不想向PhoneNumber
添加主键,因为它是ValueObject
。
我也不明白为什么会在迁移中抛出错误。
我该如何解决这个问题?
PS。我从this site
得到了这个想法答案 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类型持久数据被建模为数据库实体。