Как сохранить объекты объектов в Entity Framework без загрязнения моей модели домена? EF (ну, реляционные DB вообще) требуют, чтобы я определил ключ - который мои объекты значения не имеют из коробки, например
С другой стороны, я не должен рассматривать проблемы сохранения в модели. Действительно ли я должен создать другой класс, который включает все поля из объекта value плюс свойство ключа, а затем сопоставить их друг с другом? Я бы предпочел не.
Ответ 2
В настоящее время я работаю над некоторыми из этих же задач. Я не являюсь поклонником добавления Id в ваш базовый класс ValueObject<T>
, так как это дает идентификатор всем объектам значений, независимо от того, нужны они или нет, а также как объект значения по определению не имеет идентификатора, это что-то, наследующее этот базовый тип больше не будет объектом значения в чистом смысле.
Прежде чем идти дальше, я хотел бы отметить, что ключевая концепция в кодировании DDD заключается в том, что вам не обязательно быть чистым DDD везде, только если вы знаете, какие уступки вы делаете и их компромиссы. Тем не менее, ваш подход, безусловно, может считаться прекрасным, однако я считаю, что он добавляет уступку, которая не может быть действительно необходимой. В первую очередь это влияет на равенство ваших объектов ценности. С добавлением Id два тега, даже с тем же именем, уже не равны.
Вот мои подходы к этой ситуации:
Сначала легкий, не совсем применимый к тому, что я думаю, ваша проблема, но это важно. Это единственный объект значения в первой части ответа Мартина.
- Сделать объект Value свойством объекта.
Пока ваш объект значения состоит только из простых свойств типа, Entity Framework отобразит это просто отлично.
Пример:
public class BlogEntry : Entity<Guid>
{
public String Text { get; private set; }
public Tag Tag { get; private set; }
// Constructors, Factories, Methods, etc
}
Entity Framework справится с этим просто отлично, в итоге вы получите единственную таблицу BlogEntry, состоящую просто из:
Теперь я полагаю, что на самом деле это не то, что вам нужно в этом случае, но для многих объектов значений это отлично работает. Я часто использую объект значения DateRange, который состоит из нескольких свойств. Тогда на моих объектах домена я просто имею свойство типа DateRange. EF сопоставляет их с таблицей для самого объекта домена.
Я рассказываю об этом, потому что возвращаясь к концессии, которую мы сделали, добавляя Id к базовому типу ValueObject<T>
, даже несмотря на то, что этот идентификатор не может быть указан в конкретной конкретной реализации объекта домена, он все еще существует и будет по-прежнему собираться что для этого, возможно, наиболее распространенный случай использования объекта ценности больше не работает так же хорошо.
ОК, наконец, на ваш конкретный случай (который я тоже запустил несколько раз). Вот как я решил обработать необходимость в том, чтобы сущность содержала список объектов Value. В основном это сводится к расширению нашего понимания домена. Предполагая, что объект значения тега предназначен для записи тега в сообщении в блоге, способ, которым я смотрю на него, заключается в том, что BlogPost содержит список PostTag со значением Tag. Да, это еще один класс, но вам не нужно добавлять его для каждого объекта значения, он нужен только тогда, когда у вас есть список объектов значений, и я думаю, что лучше выражает то, что происходит.
Итак, вот пример добавления списка объекта значения к сущности (с использованием вашего объекта значения тега выше):
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 };
}
}
Это позволит вашей BlogEntry содержать несколько тегов без компрометации объектов значений, а Entity Framework будет отображать ее просто отлично, без необходимости делать что-либо особенное.