【问题标题】:Entity Framework setting an entry to modified throws error实体框架设置修改的条目会引发错误
【发布时间】:2014-07-02 21:00:30
【问题描述】:

在为我的 Entity Framework 存储库编写更新方法时,我包含以下代码:

public bool UpdateProduct(int id, Models.Product product)
{
    Product ctxProduct = GetProductIncludingProductLists(id); //Pulls directly from context
    if (ctxProduct != null && product != null)
    {
        /*Update ctxProduct fields using product*/

        _ctx.Entry(ctxProduct).State = EntityState.Modified; //_ctx is my DbContext
        return true;
    }
    return false;
}

但是我将实体的状态设置为修改的代码行会引发以下错误:

EntityFramework.dll 中出现“System.InvalidOperationException”类型的第一次机会异常

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

我对这条错误消息试图告诉我的内容感到困惑。由于我已将此条目从上下文中删除,因此它的主键应该与之冲突的唯一事物就是它本身。另外,我知道这个条目已经被分配了一个 id,因为我使用它的 id 访问它。

最后,我将这个条目的状态设置为 modified 的原因是因为调用 _ctx.saveChanges() 会返回 0,这表明上下文不知道我已经更改了任何内容(当我更改时)。

谁能解释为什么会抛出这个错误以及我需要做些什么来让上下文知道我的更改?

编辑

GetProductIncludingProductLists(id):

    public Product GetProductIncludingProductLists(int id)
    {
        try
        {
            return _ctx.Products.Include("ProductLists")
                                    .ToList()
                                    .Select(p => new Product()
                                    {
                                        ProductId = p.ProductId,
                                        CUSIP = p.CUSIP,
                                        SEDOL = p.SEDOL,
                                        BUID = p.BUID,
                                        Key = p.Key,
                                        Track = p.Track,
                                        ProductLists = ((p.ProductLists.Select(l => new ProductList()
                                        {
                                            ProductListId = l.ProductListId,
                                            Name = l.Name,
                                            Products = null
                                        })
                                        .ToList() as List<ProductList>) ?? null)
                                    })
                                    .First(item => item.ProductId == id);
        }
        catch (Exception e)
        {
            System.Diagnostics.Debug.WriteLine(e.Message);
            return null;
        }
    }

之所以会出现疯狂的select语句,是因为产品和产品列表是N:N相关的,不包括它们会导致'循环序列化'出错

【问题讨论】:

  • 能否请包含 GetProductIncludingProductLists(id)?
  • 错误非常简单。 Entity Framework 具有您从数据库中检索到的实体的缓存。此实体已添加到本地缓存中,因此您无法将非附加实体添加到缓存中,因为缓存中已存在另一个版本(很可能来自数据库)。

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


【解决方案1】:

看了这段代码,恐怕对EF的工作原理有些了解。

首先,要加载一个实体,这段代码过于复杂并且会做一些可怕的事情:

1:这会将数据库中的所有产品加载到内存中

 return _ctx.Products.Include("ProductLists")
                                .ToList()

2:这会将已有对象的属性复制到新对象中

.Select(p => new Product() {  // create new object and copy properties !!

3:这会单独查询数据库中每个产品的每个相关列表(并创建一个新列表,当已有可用列表时)

p.ProductLists.Select( // query the list of each product !!

4:所有这些只是为了获得具有给定 id 的产品!

.First(item => item.ProductId == id);

你可以这样做:

return _ctx.Products
   .Include(p => p.ProductLists)  // it's much safer a lambda than a magic string
   .First(item => item.ProductId == id);

它将仅加载所需的产品及其相应的产品列表。它将附加到上下文中。

第二。如果您的产品已经附加到上下文,即您已经使用上下文加载了它,例如我刚刚展示的那样,并且假设 _ctx 尚未被释放,则该产品已经被上下文跟踪,并且你不需要关心设置它的状态。每当您对其进行任何更改时,上下文都会自动更改其状态,因此,当您调用 SaveChanges 时,更改将自动发布到数据库。

如您所见,您的代码过于复杂。尝试制作一些教程来了解 EF 的工作原理。你会节省很多时间。您可以使用EF section of MSDN。它有清晰的文档和示例。

【讨论】:

    【解决方案2】:

    您返回的产品没有被实体框架跟踪更改:

    .Select(p => 新产品()

    【讨论】:

    • 那么现在如何在不破坏序列化的情况下摆脱它?
    • 序列化是一个不同的问题,可能是您应该解决的问题。
    • 需要更多信息,你想序列化什么?
    • 我猜你正试图通过网络发送实体,可能是通过 WCF。这是我能想到的两种方法,试试这个msdn.microsoft.com/en-us/library/vstudio/… 或者在通过网络发送之前将实体转换为没有循环依赖的 DTO。
    • 这应该可以解决这个错误:johnnycode.com/2012/04/10/…
    【解决方案3】:

    第一个 ToList() 将所有产品带入上下文中(您确定要这样做吗?)。

    然后你用选择创建一个新的

    试图附加新的与加载的冲突。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-26
      • 2019-04-29
      • 2012-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多