2014-09-21 4 views
3

У меня есть простая модель связующий:Как использовать ModelBinders с DbContext, хранящимся в OwinContext?

public class PersonBinder : IModelBinder 
{ 
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     // ... 
     // id = routevalues[ id ] 

     var db = controllerContext.HttpContext.GetOwinContext().Get<ApplicationDbContext>(); 

     return db.Set<Person>().FirstOrDefault(o => o.Id == id); 
    } 
} 

И это прекрасно работает. Вот к примеру:

public ActionResult Edit(Person entity, int? id) 
{ 
    if (entity == null && id.HasValue) 
     throw new HttpException(404, "Person not found."); 

    return View(person); 
} 

Вопрос в том, когда я пытаюсь сохранить его в базе данных:

// DbContext = HttpContext.GetOwinContext().Get<ApplicationDbContext>() 

[HttpPost] 
public async Task<ActionResult> Edit(Person entity, int? id) 
{ 
    if (entity == null && id.HasValue) 
     throw new HttpException(404, "Person not found."); 

    if (ModelState.IsValid) 
    { 

     // WORKS when inserting a new person to the database 
     if (!id.HasValue) 
      DbContext.People.Add(entity); 
     else 
     { 
      // if I try to attach I get an error: see bellow 
      // if I don't attach, it does nothing 
     } 

     await DbContext.SaveChangesAsync(); 
    } 

    return View(entity); 
} 

Ошибка на прикрепиться:

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

статус предприятие показывает, как Detached, когда я запускаю это в действии контроллера:

DbContext.Entry(entity).State 

Почему это происходит? Как я могу это исправить? Невозможно ли использовать для этого связующие?

+0

Проблема в том, что ваши DbContexts - это два разных экземпляра. Как вы получаете объект DbContext в OWIN и как его получить в контроллере? – trailmax

ответ

5

Прежде чем приступать к подключению, что, если вы отсоедините существующее устройство? Что-то вроде: (не проверено - жаль, что это основано на какой-то код в старом проекте, где мы должны были сделать что-то подобное по разным причинам, но, надеюсь, дает представление)

if (!id.HasValue) 
     DbContext.People.Add(entity); 
    else 
    { 
     var attachedEntity = DbContext.People.Find(id); 
     if (attachedEntity != null && DbContext.Entry(attachedEntity).State != EntityState.Detached) 
     { 
      DbContext.Entry(attachedEntity).State = EntityState.Detached; 
      // You may need to recursively detach child entities here if any 
     } 
     DbContext.People.Attach(entity); 
     DbContext.Entry(entity).State = EntityState.Modified; 
    } 

EDIT:

Поскольку основная проблема заключается в том, что экземпляр вашего DbContext отличается от того, что в документации для IOwinContext.Get<T> говорится: «Получает значение из среды OWIN или возвращает значение по умолчанию (T), если оно отсутствует».

http://msdn.microsoft.com/en-us/library/dn270607(v=vs.113).aspx

, ты можешь где-то (я не использовал Owin так, не знаю, где лучшее место было бы - но я предполагаю, что в начале вашего трубопровода) позвонить IOwinContext.Set<ApplicationDbContext> и передать ему новый DbContext, чтобы ваши 2 обращения к HttpContext.GetOwinContext(). Get() совместно используют один и тот же экземпляр?

+0

Это действительно работает. Но тогда у меня есть еще один обратный путь к базе данных, это то, что я хотел бы избежать, если это возможно. (1. Связывание идет в базу данных, 2. Моя служба снова отправляется для отсоединения, затем присоединяется) – BrunoLM

+0

Я предполагаю, что он не имеет одного и того же экземпляра, так что это невозможно сделать за один выстрел – BrunoLM

+0

Ах, я вижу вопрос сейчас. Хм, не можете ли вы ввести тот же экземпляр DbContext в свои контроллеры через все, что вы используете для IoC? – mutex

-1

Когда вы редактируете, перед сохранением задайте состояние объекта как измененное.

DbContext.Entry(entity).State = EntityState.Modified; 

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

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

+0

Если я делаю это, я получаю: «Исключение типа« System.InvalidOperationException »произошло в EntityFramework.dll, но не было обработано в коде пользователя Дополнительная информация: Прикрепление объекта типа« xxx »не удалось, поскольку другой объект того же тип уже имеет такое же значение первичного ключа. Это может произойти при использовании метода «Прикрепить» или установки состояния объекта в «Без изменений» или «Модифицировано», если любые объекты на графике имеют противоречивые значения ключей ... ' – BrunoLM

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