我正在尝试使用EF6更新记录。首先找到记录,如果它存在,则更新。 这是我的代码:

var book = new Model.Book
{
    BookNumber =  _book.BookNumber,
    BookName = _book.BookName,
    BookTitle = _book.BookTitle,
};
using (var db = new MyContextDB())
{
    var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
    if (result != null)
    {
        try
        {
            db.Books.Attach(book);
            db.Entry(book).State = EntityState.Modified;
            db.SaveChanges();
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

每次我尝试使用上面的代码更新记录时,我都会得到这个错误:

{System.Data.Entity.Infrastructure。DbUpdateConcurrencyException:存储 更新、插入或删除语句受影响的数量超出预期 自实体以来,实体可能已被修改或删除 被加载。刷新ObjectStateManager条目


当前回答

如Renat所说,删除:db.Books.Attach(book);

另外,将结果查询改为使用“AsNoTracking”,因为该查询会抛出实体框架的模型状态。它认为“结果”是现在要追踪的书,而你不想要那个。

var result = db.Books.AsNoTracking().SingleOrDefault(b => b.BookNumber == bookNumber);

其他回答

你应该删除db.Books.Attach(book);

如果您希望更新对象中的所有字段,则应该使用Entry()方法。 还要记住,您不能更改字段id(键),因此首先将id设置为与您编辑时相同。

using(var context = new ...())
{
    var EditedObj = context
        .Obj
        .Where(x => x. ....)
        .First();

    NewObj.Id = EditedObj.Id; //This is important when we first create an object (NewObj), in which the default Id = 0. We can not change an existing key.

    context.Entry(EditedObj).CurrentValues.SetValues(NewObj);

    context.SaveChanges();
}

我找到了一个很好的方法。

 var Update = context.UpdateTables.Find(id);
        Update.Title = title;

        // Mark as Changed
        context.Entry(Update).State = System.Data.Entity.EntityState.Modified;
        context.SaveChanges();

当尝试使用Attach()和SaveChanges()组合更新记录时,我也有同样的问题,但我使用的是SQLite DB及其EF提供者(相同的代码在SQLServer DB中工作没有问题)。

I found out, when your DB column has GUID (or UniqueIdentity) in SQLite and your model is nvarchar, SQLIte EF treats it as Binary(i.e., byte[]) by default. So when SQLite EF provider tries to convert GUID into the model (string in my case) it will fail as it will convert to byte[]. The fix is to tell the SQLite EF to treat GUID as TEXT (and therefore conversion is into strings, not byte[]) by defining "BinaryGUID=false;" in the connectionstring (or metadata, if you're using database first) like so:

  <connectionStrings>
    <add name="Entities" connectionString="metadata=res://savetyping...=System.Data.SQLite.EF6;provider connection string=&quot;data source=C:\...\db.sqlite3;Version=3;BinaryGUID=false;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>

链接到对我有效的解决方案: SQLite实体框架6提供者如何处理指南?

所以你有一个更新的实体,你想用最少的代码在数据库中更新它……

并发性总是很棘手,但我假设您只是希望您的更新能够胜出。下面是我对相同情况的处理方法,并修改名称以模仿您的类。换句话说,只需将attach更改为add,这对我来说很管用:

public static void SaveBook(Model.Book myBook)
{
    using (var ctx = new BookDBContext())
    {
        ctx.Books.Add(myBook);
        ctx.Entry(myBook).State = System.Data.Entity.EntityState.Modified;
        ctx.SaveChanges();
    }
}