【问题标题】:Entity Framework (EF) modifying an instance that is already in the context实体框架 (EF) 修改已在上下文中的实例
【发布时间】:2016-04-01 22:31:35
【问题描述】:

我正在处理包含桌面(Windows 窗体)和 Windows Phone 应用程序的学校项目。错误在 Api 控制器,编辑操作中。

这是 WebApi 调用:

 HttpResponseMessage responseProizvodi;
            if (proizvod.ProizvodID == 0)
                responseProizvodi = proizvodiService.PostResponse(proizvod);
            else responseProizvodi = proizvodiService.PutResponse(proizvod.ProizvodID,proizvod);

我在 WebApiHelper 类中的方法:

 public HttpResponseMessage PostResponse(Object o)
    {
        return client.PostAsJsonAsync(route, o).Result;
    }

    public HttpResponseMessage PutResponse(int id, Object o)
    {
        return client.PutAsJsonAsync(route + "/" + id, o).Result;
    }

和使用Entity Framework生成的ApiController:

 [ResponseType(typeof(void))]
    public IHttpActionResult PutProizvodi(int id, Proizvodi proizvodi)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (id != proizvodi.ProizvodID)
        {
            return BadRequest();
        }

        db.Entry(proizvodi).State = EntityState.Modified;

        try
        {
            db.SaveChanges();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!ProizvodiExists(id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return StatusCode(HttpStatusCode.NoContent);
    }

在这一行:

 db.Entry(proizvodi).State = EntityState.Modified;

我收到了这个错误:

附加信息:附加类型为“WebApi.Models.Proizvodi”的实体失败,因为同一类型的另一个实体已经具有相同的主键值。如果图中的任何实体具有冲突的键值,则在使用“附加”方法或将实体的状态设置为“未更改”或“已修改”时,可能会发生这种情况。这可能是因为某些实体是新实体,尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“已添加”实体状态来跟踪图形,然后将非新实体的状态设置为“未更改”或“已修改”。

我阅读了类似主题的问题/答案,但没有找到答案。

其中一个说我们必须分离本地版本并设置修改我们正在修改的实体。

一种方法是在 DbSet 的本地内部导航以查看是否存在该 DbSet。如果实体存在,则分离。

 var local = yourDbContext.Set<YourModel>()
                         .Local
                         .FirstOrDefault(f => f.Id == yourModel.Id);
if (local != null)
{
  yourDbContext.Entry(local).State = EntityState.Detached;
}
yourDbContext.Entry(applicationModel).State = EntityState.Modified;

在我的例子中,local 是 null,EntityState 是 Deatached,但我仍然有同样的错误。有什么帮助吗?

编辑: 我的数据库在这里:

 public class ProizvodiController : ApiController
{
    private eProdajaEntities db = new eProdajaEntities();
    //actions
 }

【问题讨论】:

  • 不清楚您在哪里初始化或处置db。但是,对于 Web 应用程序,在多个 HTTP 调用中保持打开 DbContext 是一种不好的做法。当多个客户端访问上下文时,它将处于混乱和混乱的状态。最佳做法是在您的操作方法的主体中创建和处置您的 DbContext。
  • Db 被实例化为 Controller 类的私有成员: public class ProizvodiController : ApiController { private eProdajaEntities db = new eProdajaEntities(); //动作 //动作 //.... }
  • 接受整个域对象然后保存它是一个明显的安全漏洞。您应该细化您希望编辑的属性,然后自己更新域对象。使用这种方法,您一开始就不会遇到此错误。
  • 可能你的 Dbcontext 没有正确处理。
  • 您必须先从上下文中获取实体,然后才能附加它以进行更改。 EF 无法知道您的 proizvodi 是上下文的一部分。我看到的情况是,由于 Ef 不知道您的 proizvodi,它会创建一个新的,这会导致“相同的主键值”错误。

标签: c# asp.net asp.net-mvc entity-framework asp.net-web-api


【解决方案1】:

EF 不跟踪传递给 PutProizvodi 方法的 Proizvodi 对象(即它不知道它来自哪里,因为它没有参与实体实例的创建;该实例是创建的通过 Web.API 模型绑定器,它不知道您的数据库或 EF),这可能是它抛出错误的原因。

Proizvodi 类型是实体类型,并不意味着它会被 EF 自动跟踪;只会跟踪 EF 检索到的对象。

您需要首先通过 EF 使用传递给您的 PutProizvodi 方法的 id 查找正确的 Proizvodi 对象的实例,而不是使用 Proizvodi 实例模型绑定器正在传入。

可以使用Proizvodi 对象传递到您的PutProizvodi 方法,但更常见的是创建具有类似属性的模型,然后根据需要映射到您的实体实例。这使您可以控制将实体对象的哪些属性传入和传出应用程序。

类似这样的:-

[ResponseType(typeof(void))]
public IHttpActionResult PutProizvodi(int id, ProizvodiModel model)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    // lookup the entity using the id that was passed in    

    var proizvodi = db.Set<Proizvodi>().Where(x => x.id == id).FirstOrDefault();

    // if no entity was found with that id, return

    if (proizvodi == null) {
        return BadRequest();
    }

    // make the changes to your entity

    proizvodi.SomeProperty = model.SomeProperty;
    // etc

    // then save the changes

    db.SaveChanges();

    return StatusCode(HttpStatusCode.NoContent);
}

public class ProizvodiModel
{
    public string SomeProperty { get; set; }
}

【讨论】:

  • 什么是属性?你能给我举个例子吗?
  • @Thunfische "SomeProperty" 只是实体类上的一个示例属性名称;在上面的示例中,“Proizvodi”实体有一个名为“SomeProperty”的属性,EF 会将其映射到数据库中的列。
【解决方案2】:

我意识到我的问题出在哪里。简而言之,我做了以下事情:

1.

WebAPIHelper proizvodiService = new WebAPIHelper("http://localhost:62312/", "api/Proizvodi");

1.1

private Proizvodi proizvod { get; set; } 

2.

 HttpResponseMessage responseProizvodi=proizvodiService.GetResponse(ProizvodID);

3.

      Proizvodi p = responseProizvodi.Content.ReadAsAsync<Proizvodi().Result;
      proizvod = p; 
// do some edit on "proizvod" and put to database
      HttpResponseMessage responseProizvod = proizvodiService.PutResponse(proizvod.ProizvodID,proizvod);

重点是什么,我从数据库中获得的相同对象(对象“proizvod”,它是此类中的私有属性),我发送回数据库,然后出现错误。

对象“p”指向数据库中的对象,然后对象“proizvod”指向同一个对象,同一个地址,我进行一些编辑,将所有内容放入对象“proizvod”并发送到 PutResponse 方法。

proizvod = new Proizvodi(); 

不见了……现在一切正常。我希望我意识到问题所在。如果有人能从技术上准确地解释发生了什么,我会很高兴。我是一个初学者,想弄清楚事情是如何运作的,谢谢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-18
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    • 2017-09-17
    • 2011-07-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多