Entity framework 如何在实体框架中处理价值对象?

Entity framework 如何在实体框架中处理价值对象?,entity-framework,domain-driven-design,Entity Framework,Domain Driven Design,如何在实体框架中持久化值对象而不污染域模型?EF(通常是关系数据库)要求我定义一个键——例如,我的值对象没有现成的键 public class Tag : ValueObject<Tag> { private readonly string name; public Tag(string name) { this.name = name; } public string Name { get { return this.name; }}

如何在实体框架中持久化值对象而不污染域模型?EF(通常是关系数据库)要求我定义一个键——例如,我的值对象没有现成的键

public class Tag : ValueObject<Tag>
{
   private readonly string name;

   public Tag(string name)
   {
      this.name = name;
   }

   public string Name { get { return this.name; }}
}
public类标记:ValueObject
{
私有只读字符串名称;
公共标记(字符串名称)
{
this.name=名称;
}
公共字符串名称{get{返回this.Name;}}
}
另一方面,我不应该在模型中解决持久性问题。我真的应该创建另一个类,该类包含value对象中的所有字段和一个key属性,然后将它们相互映射吗?我宁愿不要


有没有更优雅的解决方案?

沃恩·弗农(Vaughn Vernon)在他的优秀著作中写到了持久化价值对象(第248页)

ORM和单值对象

基本思想是将值的每个属性存储在父实体所在行的单独列中。换句话说,单个值对象被非规范化为其父实体的行。使用列命名约定可以清楚地标识序列化对象,并使序列化对象的命名方式标准化

ORM和由数据库实体支持的许多值

使用ORM和关系数据库持久化值实例集合的一种非常简单的方法是将值类型视为数据模型中的实体。(…)要做到这一点,我们可以雇用一名工程师


可以在这里找到C#中的示例有界上下文:

我目前正在处理一些相同的挑战。我不太喜欢在你的基本
ValueObject
类中添加一个Id,因为这会给所有的值对象一个Id,不管它们是否需要。另外,根据定义,值对象没有Id,任何继承该基本类型的东西都不再是纯意义上的值对象

在我进一步讨论之前,我要指出,编码DDD的一个关键概念是,您不必在任何地方都是纯DDD,只要您知道您做出了哪些让步以及它们的取舍。也就是说,你的方法当然可以被认为是好的,但是我相信这是一个让步,这可能不是真正必要的。这主要会影响值对象的平等性。添加Id后,两个标记(即使具有相同的名称)不再相等

以下是我对这种情况的处理方法: 首先是一个简单的问题,它并不适用于我认为你的问题,但它很重要。这是Martin答案第一部分中的单值对象

  • 使值对象成为实体的属性
只要您的值对象只包含简单的类型属性,EntityFramework就会很好地映射它

例如:

    public class BlogEntry : Entity<Guid>
    {
         public String Text { get; private set; }
         public Tag Tag { get; private set; }

         // Constructors, Factories, Methods, etc
    }
公共类BlogEntry:实体
{
公共字符串文本{get;private set;}
公共标记{get;private set;}
//施工人员、工厂、方法等
}
实体框架可以很好地处理这一点,您将得到一个单表BlogEntry,它只包含:

  • 身份证
  • 正文
  • 标签名称
现在我想这并不是你在本例中真正想要的,但对于许多价值对象来说,它非常有用。我经常使用的一个对象是DateRange值对象,它由几个属性组成。然后在我的域对象上,我有一个DateRange类型的属性。EF将这些映射到域对象本身的表

我之所以提出这个问题,是因为我们做出让步,将Id添加到
ValueObject
基本类型中,尽管它的Id可能没有列在域对象的具体实现中,但它仍然存在,并且仍然会被Entity Framework接受,可能最常见的值对象用例不再那么好地工作

好的,最后,谈谈你的具体情况(我也遇到过几次)。下面是我选择如何处理实体包含值对象列表的需要。基本上,它归结为扩展我们对该领域的理解。假设Tag value对象用于记录博客文章中的标记,我的看法是博客文章包含一个带有标记值的PostTag列表。是的,它是另外一个类,但是你不需要为每个值对象添加它,它只在你有一个值对象列表时才需要,我认为它更能表达正在发生的事情

下面是一个向实体添加值对象列表的示例(使用上面标记的值对象):

公共类BlogEntry:实体
{
公共字符串文本{get;private set;}
公共ICollection后标记{get;private set;}
//建造商:
私有BlogEntry(Guid id):基(id){}
受保护的BlogEntry():此(Guid.NewGuid()){}
//工厂:
公共静态BlogEntry创建(字符串文本,ICollection标记=null)
{
如果(tags==null){tags=new List();}
返回新的BlogEntry(){Text=Text,Tags=Tags};
}        
//方法:
public void AddTag(字符串名称)
{
添加(PostTag.Create(name));
}
}
公共类PostTag:Entity
{
//特性:
公共标记{get;private set;}
public DateTime DateAdded{get;private set;}//与标记值无关的属性。
//建造商:
私有PostTag(Guid id):基(id){}
受保护的PostTag():此(Guid.NewGuid()){}
//工厂:
公共静态PostTag创建(标记标记)
{ 
返回新的PostTag(){Tag=Tag,DateAdded=DateTime.Now};
}
公共静态PostTag创建(标记标记,DateTime DateAdd)
{ 
退回新订单
    public class BlogEntry : Entity<Guid>
    {
         public String Text { get; private set; }
         public ICollection<PostTag> PostTags { get; private set; }

         // Constructors:
         private BlogEntry(Guid id) : base(id) { }
         protected BlogEntry() : this(Guid.NewGuid()) { }

         // Factories:
         public static BlogEntry Create (String text, ICollection<PostTag> tags = null)
         {
             if(tags == null) { tags = new List<PostTag>(); }
             return new BlogEntry(){ Text = text, Tags = tags };
         }        

         // Methods:
         public void AddTag(String name)
         {
             PostTags.Add(PostTag.Create(name));
         }
    }

    public class PostTag : Entity<Guid>
    {
        // Properties:
        public Tag Tag { get; private set; }
        public DateTime DateAdded { get; private set; } // Properties that aren't relevant to the value of Tag.

        // Constructors:
        private PostTag(Guid id) : base(id) { }
        protected PostTag() : this(Guid.NewGuid()) { }

        // Factories:
        public static PostTag Create(Tag tag) 
        { 
            return new PostTag(){ Tag = tag, DateAdded = DateTime.Now };
        }

        public static PostTag Create(Tag tag, DateTime dateAdded) 
        { 
            return new PostTag(){ Tag = tag, DateAdded = dateAdded };
        }
    }