如何使用存储在OwinContext中的DbContext的ModelBinder?

时间:2014-09-21 04:00:18

标签: c# asp.net-mvc entity-framework asp.net-mvc-5 owin

我有一个简单的模型绑定器:

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:附加类型&#39; .. Person&#39;的实体。失败,因为同一类型的另一个实体已具有相同的主键值。使用&#39;附加&#39;方法或将实体的状态设置为“未更改”#39;或者&#39;修改&#39;如果图中的任何实体具有冲突的键值。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下,请使用&#39;添加&#39;方法或“添加”#39;实体状态跟踪图形,然后将非新实体的状态设置为“未更改”。或者&#39;修改&#39;酌情。

当我在控制器操作中运行时,实体状态显示为Detached

DbContext.Entry(entity).State

为什么会这样?我该怎么办呢?是否不能使用粘合剂?

2 个答案:

答案 0 :(得分: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; 
    }

编辑:

由于主要问题似乎是你的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()共享同一个实例?

答案 1 :(得分:-1)

编辑时,请在保存之前将实体状态设置为已修改。

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

由于现在实体框架没有跟踪此实体,因此您需要在尝试保存之前明确指定这是一个已修改的实体。

另一种方法是从数据库中检索实体,以便实体框架跟踪它。然后用修改后的值更改它的值,当你这样做时,实体框架会注意到它并将其设置为自动修改。然后,您可以保存更改。