11

Я использую шаблон репозитория, чтобы обеспечить доступ и сохранение моих агрегатов.Общий репозиторий для обновления всего агрегата

Проблема заключается в обновлении агрегатов, которые состоят из отношения сущностей.

Например, обратите внимание на отношения Order и OrderItem. Совокупный корень - Order, который управляет собственной коллекцией OrderItem. Таким образом, OrderRepository будет отвечать за обновление всего агрегата (не было бы OrderItemRepository).

Сохранение данных осуществляется с помощью Entity Framework 6.

метод обновления репозитория (DbContext.SaveChanges() происходит в другом месте):

public void Update(TDataEntity item) 
{ 
    var entry = context.Entry<TDataEntity>(item); 

    if (entry.State == EntityState.Detached) 
    { 
     var set = context.Set<TDataEntity>(); 

     TDataEntity attachedEntity = set.Local.SingleOrDefault(e => e.Id.Equals(item.Id)); 

     if (attachedEntity != null) 
     { 
      // If the identity is already attached, rather set the state values 
      var attachedEntry = context.Entry(attachedEntity); 
      attachedEntry.CurrentValues.SetValues(item); 
     } 
     else 
     { 
      entry.State = EntityState.Modified; 
     } 
    } 
} 

В моем примере выше, только Order объект будет обновляться, а не его объединенный OrderItem коллекция.

Должен ли я прикрепить все объекты OrderItem? Как я мог сделать это в общем?

+0

Существует предложение, чтобы добавить лучшую поддержку отслеживания изменений с графиками - https: //entityframework.codeplex.com/workitem/864 и там есть ссылка на graphdiff, которая может помочь – Colin

ответ

15

Julie Lerman дает хороший способ справиться с тем, как обновить весь агрегат в своей книге Programming Entity Framework: DbContext.

Как она пишет:

Когда отключенный объект график поступает на стороне сервера, сервер не будет знать состояние субъектов. Вам необходимо предоставить способ для обнаружения состояния, чтобы контекст мог быть сделан , который знает состояние каждого объекта.

Этот метод называется painting the state.

Есть в основном два способа сделать это:

  • Итерация по графике, используя свои знания модели и установить состояние для каждого объекта
  • Построить общий подход для отслеживания состояния

Второй вариант действительно приятный и состоит в создании интерфейса, который будет реализовывать каждый объект в вашей модели. Джули использует IObjectWithState интерфейс, который говорит текущее состояние объекта:

public interface IObjectWithState 
{ 
    State State { get; set; } 
} 
public enum State 
{ 
    Added, 
    Unchanged, 
    Modified, 
    Deleted 
} 

Первое, что вам нужно сделать, это автоматически устанавливается Unchanged каждые объекты, извлекаемые из базы данных за счет подключения событие, добавив конструктор в ваш Context класс:

public YourContext() 
{ 
((IObjectContextAdapter)this).ObjectContext 
    .ObjectMaterialized += (sender, args) => 
{ 
    var entity = args.Entity as IObjectWithState; 
    if (entity != null) 
    { 
    entity.State = State.Unchanged; 
    } 
}; 
} 

Затем измените Order и OrderItem классов для реализации интерфейса IObjectWithState и называют это ApplyChanges метода принятия корневого объекта в качестве параметра:

private static void ApplyChanges<TEntity>(TEntity root) 
where TEntity : class, IObjectWithState 
{ 
using (var context = new YourContext()) 
{ 
    context.Set<TEntity>().Add(root); 

    CheckForEntitiesWithoutStateInterface(context); 

    foreach (var entry in context.ChangeTracker 
    .Entries<IObjectWithState>()) 
    { 
    IObjectWithState stateInfo = entry.Entity; 
    entry.State = ConvertState(stateInfo.State); 
    } 
    context.SaveChanges(); 
} 
} 

private static void CheckForEntitiesWithoutStateInterface(YourContext context) 
{ 
var entitiesWithoutState = 
from e in context.ChangeTracker.Entries() 
where !(e.Entity is IObjectWithState) 
select e; 

if (entitiesWithoutState.Any()) 
{ 
    throw new NotSupportedException("All entities must implement IObjectWithState"); 
} 
} 

И последнее, но не в последнюю очередь, не забудьте установить правильное состояние вашего графа entites перед вызовом ApplyChanges ;-) (можно даже смешивать Modified и Deleted состояний в одном графике)

Джули предлагает ищите еще больше в своей книге:

Вы можете обнаружить, что хотите быть более зернистым с помощью способа Отслеживаются измененные свойства. Вместо того, чтобы маркировать весь объект с изменениями, возможно, вам понадобятся только те свойства, которые на самом деле , были помечены как измененные. В дополнение к маркировке объекта, модифицированного, клиент также является , ответственным за запись свойств, которые были изменены. Один из способов: сделать это - добавить список измененных имен свойств в интерфейс отслеживания состояния .

Но мой ответ уже слишком долго, почитайте ее book если вы хотите узнать больше ;-)

+1

Это почти то, что я сейчас делаю с проектом, и он работает очень хорошо. У меня есть метод расширения, который я привязал к DbContext, который я вызываю в SaveChanges, который проверяет все объекты, которые имеют интерфейс, и устанавливает EntityState. Работает как шарм. – VeteranCoder

+0

@MaxS У меня были проблемы с этим подходом. Если я создаю объект, а затем попытаюсь использовать его, потому что материализованное событие не срабатывает над этим объектом, в следующий раз, когда ApplyChanges будет вызван на другой объект, к которому я добавляю его, существует исключение дубликатов ключей. Возможно, мой подход ошибочен в том, что я сохраняю 1 или 2 вещи на этом пути, а не все в одной крупной сделке. Первая операция отделена от транзакции. Поэтому мне пришлось бы сделать перезагрузку и базу данных, чтобы обойти эту проблему. Не идеально. – onefootswill

+0

@onefootswill Я не уверен, что правильно понял сценарий, который вы описываете. Не могли бы вы сказать мне шаг за шагом, что вы пытаетесь сделать? Вы пытаетесь подключить недавно добавленную сущность к существующей? В любом случае, посмотрите [здесь] (http://msdn.microsoft.com/en-us/data/jj592676.aspx), чтобы узнать, как EF имеет дело с сущностями. – MaxSC

6

Мой упрямый (DDD специфический) ответ был бы:

  1. Отрезать EF объектов на уровне данных.

  2. Убедитесь, что ваш слой данных возвращает только объекты домена (а не объекты EF).

  3. Забудьте о ленивой загрузке и IQueryable() goodness (читай: кошмар) EF.

  4. Рассмотрите возможность использования базы данных документов.

  5. Не используйте общие репозитории.

Единственный способ я нашел, чтобы делать то, что вы просите в EF является первым удалить или отключить все элементы порядка в базе данных, которые ребенок в порядке, а затем добавить или активировать все элементы порядка в которые теперь являются частью вашего недавно обновленного заказа.

+0

Я фактически возвращаю объекты домена (не показаны в моем примере выше). Я просто удалил картографию и заводскую логику, чтобы упростить вопрос. Несмотря на это, у меня все еще возникает проблема обновления графика объектов данных в реализации репозитория EF. – davenewza

+0

У меня когда-то была аналогичная проблема с графом объектов примерно на 5 уровней и никогда не нашел приятного/чистого ответа. Вы думаете, что EF будет иметь некоторый способ обновления сложных графиков объектов. –

+0

5. Не используйте общие репозитории (которые уже подразумевают значения 1. и 2.). –

0

Итак, вы сделали хорошо на метод обновления для вашего совокупного корня, посмотрите на эту модель домена :

public class ProductCategory : EntityBase<Guid> 
    { 
     public virtual string Name { get; set; } 
    } 

    public class Product : EntityBase<Guid>, IAggregateRoot 
    { 
    private readonly IList<ProductCategory> _productCategories = new List<ProductCategory>(); 

    public void AddProductCategory(ProductCategory productCategory) 
     { 
      _productCategories.Add(productCategory); 
     } 
    } 

это просто продукт, который имеет категорию продукта, я только что создал ProductRepository, как мой aggregateroot является продуктом (не категория продукта), но я хочу, чтобы добавить категорию продукта при создании или обновлении продукт в сервисном слое:

public CreateProductResponse CreateProduct(CreateProductRequest request) 
     { 
      var response = new CreateProductResponse(); 
     try 
      { 
       var productModel = request.ProductViewModel.ConvertToProductModel(); 
       Product product=new Product(); 
       product.AddProductCategory(productModel.ProductCategory); 
       _productRepository.Add(productModel); 
       _unitOfWork.Commit(); 
      } 
      catch (Exception exception) 
      { 
       response.Success = false; 
      } 
      return response; 
     } 

Я просто хотел показать вам, как создавать методы домена для объектов в домене и использовать его в служебном или прикладном уровне.как вы можете увидеть ниже код добавляет категорию ProductCategory через productRepository в базе данных:

product.AddProductCategory(productModel.ProductCategory); 

теперь для обновления и ту же сущность вы можете попросить ProductRepository и принести сущность и внести изменения в него. отметить, что для извлечения сущности и значение объекта в и агрегировать отдельно вы можете написать службу запроса или readOnlyRepository:

public class BlogTagReadOnlyRepository : ReadOnlyRepository<BlogTag, string>, IBlogTagReadOnlyRepository 
    { 
     public IEnumerable<BlogTag> GetAllBlogTagsQuery(string tagName) 
     { 
      throw new NotImplementedException(); 
     } 
    } 

надеюсь, что это помогает

Смежные вопросы