2015-07-08 3 views
2

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

ПРИМЕНЕНИЕ ФОН

У меня есть веб-приложение, которое сначала использует код Entity Framework 6.1. Я использую шаблон репозитория в моем DAL, что означает, что у меня есть Repo, который запрашивает мой контекст и возвращает результаты в Services, который затем использует automapper для сопоставления моего объекта с моделью просмотра, а затем возвращает VM. Сервисы вызываются с помощью методов Web API.

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

Проблема, с которой я сталкиваюсь, связана с обновлением существующих объектов (PUT). Поток выглядит следующим образом:

Угловая проблема HTTP PUT для метода WebAPI, передающего VM как параметр. Затем WebAPI вызывает метод Service, передавая параметр VM как параметр. Метод услуг преобразует виртуальную машину в объект EF с использованием automapper Затем метод службы вызывает соответствующий репозиторий, передавая в качестве параметра новый экземпляр отдельного объекта EF.

Пример метода обслуживания:

public async Task<List<DependantViewModel>> SaveDependants(int customerId, List<DependantViewModel> dependantViewModels) 
    { 
     var dependantEntities = Mapper.Map<List<DependantViewModel>, List<Dependant>>(dependantViewModels); 

     bool result = false; 
     foreach (var dependantEntity in dependantEntities) 
     { 
      result = await _dependantRepository.InsertOrUpdate(dependantEntity); 
      if (result != true) 
      { 
       // log errror 
      } 
     } 

     return Mapper.Map<List<Dependant>, List<DependantViewModel>>(dependantEntities); 
    } 

BaseRepo:

public virtual async Task<bool> InsertOrUpdate(TE entity) 
    { 
     if (entity.Id == 0 || entity.Id == ModelState.New) 
     { 
      // insert new item 
      _context.Entry<TE>(entity).State = EntityState.Added; 
     } 
     else 
     { 
      // update existing item 
      _context.Entry<TE>(entity).State = EntityState.Modified; 
      _context.Entry<TE>(entity).Property(o => o.CreatedUserId).IsModified = false; 
      _context.Entry<TE>(entity).Property(o => o.CreatedDate).IsModified = false; 
     } 
     return await _context.SaveChangesAsync() > 0; 
    } 

Исключение происходит при попытке установить:

_context.Entry (сущность) .State = EntityState.Modified;

Прикрепление объект типа «Business.Data.Entities.Customer.Dependant» не удалось, потому что другой сущность одного и того же типа уже есть один и тот же значение первичного ключа. Этот может произойти при использовании метода «Прикрепить» или установки состояния объекта в «Без изменений» или «Модифицировано», если любые объекты на графике имеют конфликтующие значения ключей. Это может быть связано с тем, что некоторые объекты являются новыми, а еще не получили значения ключей базы данных. В этом случае используйте метод «Добавить» или состояние «Добавлено» для отслеживания графика и , затем установите состояние не новых объектов в «Без изменений» или «Модифицировано», как .

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

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

Цените любую обратную связь

+0

ли вы использовать IoC контейнер? Если да, то какой Scope/Lifestyle вы используете для Context (реализация платформы Entity DbContext)? – Maris

+0

Я использую IOC (autofac), и я использую InstancePerLifetimeScope – FaNIX

+1

Вы должны использовать область '.InstancePerRequest()', потому что вы используете WebAPI. Это может быть причиной того, что вы получаете это исключение! – Maris

ответ

4

Это работает для меня:.

public virtual async Task<bool> InsertOrUpdate(TE entity) 
{ 
    if (entity.Id == 0 || entity.Id == ModelState.New) 
    { 
     // insert new item 
     _context.Entry<TE>(entity).State = EntityState.Added; 
    } 
    else 
    {   
     var attachedEntity = _context.ChangeTracker.Entries<TE>().FirstOrDefault(e => e.Entity.Id == entity.Id); 
     if (attachedEntity != null) 
     { 
      // the entity you want to update is already attached, we need to detach it and attach the updated entity instead 
      _context.Entry<TE>(attachedEntity.Entity).State = EntityState.Detached; 
     } 

     _context.Entry<TE>(entity).State = EntityState.Modified; // Attach entity, and set State to Modified. 
     _context.Entry<TE>(entity).Property(o => o.CreatedUserId).IsModified = false; 
     _context.Entry<TE>(entity).Property(o => o.CreatedDate).IsModified = false; 
    } 
    return await _context.SaveChangesAsync() > 0; 
} 
+0

Вы все еще делаете SQL-запрос для каждого объекта в 'List'. Это не правильный путь! Вы должны использовать 'SaveChanges()' только тогда, когда все сущности «добавлены» или «обновлены» в EntityFrameworks UoW! – Maris

+0

Я изменил базовое репо, чтобы добавить все дочерние сущности, а затем вызвать SaveChanges, ваш пример вызывает SaveChanges в службе каждый раз, я изменил его, чтобы сделать это один раз только в репо. Я просто не опубликовал это, так как он не подходит для заданного вопроса. – FaNIX

+0

Да, вы правы. Была ошибка в моем коде. Я переместил SaveChanges() из цикла foreach. – Maris

1

В начале я должен сохранить, что у вас есть использование SaveChanges() только тогда, когда все entites добавляется или Обновлено EF контекст (это не ответ на ваш вопрос, но я должен упомянуть об этом). Пример:

public async Task<List<DependantViewModel>> SaveDependants(int customerId, List<DependantViewModel> dependantViewModels) 
{ 
    var dependantEntities = Mapper.Map<List<DependantViewModel>, List<Dependant>>(dependantViewModels); 

    bool result = false; 
    foreach (var dependantEntity in dependantEntities) 
    { 
     // Also InsertOrUpdate don't need to be async in that case 
     _dependantRepository.InsertOrUpdate(dependantEntity); 
     if (result != true) 
     { 
      // log errror 
     } 
    } 
    // Let the SaveChanges be async 
    result = await _dependantRepository.SaveChanges(); 

    return Mapper.Map<List<Dependant>, List<DependantViewModel>>(dependantEntities); 
} 

BaseRepo:

public async Task<bool> SaveChanges(){ 
    return await _context.SaveChangesAsync() != 0; 
} 

Это сделает только один запрос к БД вместо нескольких.

А насчет вашего вопроса, то, что должно помочь:

public virtual void InsertOrUpdate(TE entity) 
{ 
    var entitySet = _context.Set<TE>(); 
    if(entity.Id.Equals(default(int))){ 
     entitySet.Add(entity); 
     return; 
    } 
    entitySet.Update(entity); 
} 

это так. Надеюсь, что это поможет (я не проверял код, так что, вероятно, может быть какая-то ошибка)

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