2

I had been getting very strange behavior form entity framework. I am coding a WebApi application so the objects I get from the browser are disconnected/detached. The data I get back is transactional such that it does not match any given table in the database. I have to do a number of lookups and data manipulation to get the actual updates to be done on the database.

The problem I seem to have is that in querying the data I am filling up the Tracked Changes cache. That wouldn't seem to be a problem to me since the true source of data should be the database. When I finally make the data changes and I call SaveChanges I get constraint errors. Here are my steps.

  1. Query data.
  2. Create rows to be inserted.
  3. compare rows to db and make db changes.

After reviewing the data in Ctx.ChangeTracker.Entries() I found that an entry to be deleted was marked as Modified when it was supposed to be deleted. The way I worked around it was by Creating a new context for step 3. And it magically started working. I thought that was it, but in my test case I do a last read from the database to verify that my transaction was writing correctly. And I was getting an extra row that should already be deleted. And in fact was, when checking the db directly. Again a new context to do that last read fixed the problem.

I just assumed the default cache setting would just be used to track changes and not to speed up queries.

If I try to use AsNoTracking in my queries I also get into trouble because if I try to delete a row queried like that I get an error. And in my code I don't know if I am going to delete or modify until later on. Is there a way to clear the cache so I don't need to create a new context?

Is there a better way to deal with these issues?

EDIT:

AsNoTracking will do the trick, to some extent. I still found myself instantiating more copies of DbContext in order to prevent errors. Many to one entities have to be deleted in order or null foreign key errors are triggered.

var details = oldInvoice.details.ToList();

Context.Entry(oldInvoice).State = EntityState.Unchanged;
Context.Entry(oldInvoice).State = EntityState.Deleted;
details.ForEach(a => Context.Entry(a).State = EntityState.Deleted);
4

1 回答 1

2

Entity Framework 提供了一个异常DbUpdateConcurrencyException,您可以在调用SaveChanges(). 您可以遍历错误,如下所示:

catch (DbUpdateConcurrencyException ex)
    {
        saveFailed = true;

        // Get the current entity values and the values in the database
        var entry = ex.Entries.Single();
        var currentValues = entry.CurrentValues;
        var databaseValues = entry.GetDatabaseValues();

        // Choose an initial set of resolved values. In this case we
        // make the default be the values currently in the database.
        var resolvedValues = databaseValues.Clone();

        // Have the user choose what the resolved values should be
        HaveUserResolveConcurrency(currentValues, databaseValues,
                                   resolvedValues);

        // Update the original values with the database values and
        // the current values with whatever the user choose.
        entry.OriginalValues.SetValues(databaseValues);
        entry.CurrentValues.SetValues(resolvedValues);
    }

} while (saveFailed);

此外,您的更新代码听起来也很可疑。通常当您通过 WebApi 或其他机制将数据传递给客户端时,返回的数据没有跟踪数据,因此您应该检查它是否存在并将其重新附加到上下文并更改其状态EntityState.Modified在调用之前如果是这样SaveChanges()

于 2013-08-18T22:00:14.547 回答