提问



我正在使用Entity Framework来填充网格控件。有时当我进行更新时,我收到以下错误:



  存储更新,插入或删除语句会影响意外的行数(0)。自实体加载后,实体可能已被修改或删除。刷新ObjectStateManager条目。



我无法弄清楚如何重现这一点。但它可能与我进行更新的距离有多大关联。有没有人看过这个或有没有人知道错误信息是指什么?


编辑:不幸的是,我不再可以自由地重现我在这里遇到的问题,因为我离开了这个项目并且不记得我是否最终找到了解决方案,如果其他开发人员修复它,或者我是否解决了这个问题。因此我不能接受任何答案。

最佳参考


这是一种称为乐观并发的功能的副作用。


不是100%确定如何在实体框架中打开/关闭它,但基本上它告诉你的是,当你从数据库中抓取数据和保存更改时,其他人已经更改了数据(这意味着何时你去保存了0行实际上已更新了。在SQL术语中,他们的update查询s where子句包含行中每个字段的原始值,如果0行受到影响,它就知道了出了点问题。


它背后的想法是你不会覆盖你的应用程序并不知道发生的变化 - 这基本上是.NET在你所有更新中引入的一点安全措施。


如果它是一致的,可能在你自己的逻辑中发生(EG:你实际上是在选择和更新之间的另一种方法中自己更新数据),但它可能只是两者之间的竞争条件应用。

其它参考1


我碰到了这个,它是由实体的ID(密钥)字段没有设置引起的。因此当上下文保存数据时,它找不到ID=0.确保在你的地方放置一个断点更新语句并验证是否已设置实体的ID。


来自Paul Bellora的评论



  由于忘记包含隐藏的ID,我遇到了这个问题
  在.cshtml编辑页面中输入


其它参考2


哇,很多答案,但是当我做了一些稍微不同的事情时我得到了这个错误。


简而言之,如果您创建一个新对象并告诉EF使用EntityState.Modified修改它,那么它将抛出此错误,因为它在数据库中尚未存在。这是我的代码:


MyObject foo = new MyObject()
{
    someAttribute = someValue
};

context.Entry(foo).State = EntityState.Modified;
context.SaveChanges();


是的,这看起来很愚蠢,但它之所以产生,是因为之前已经创建了foo传递给它的方法,现在只有someValue传递给它并创建foo本身。


轻松修复,只需将EntityState.Modified更改为EntityState.Added或将整行更改为:


context.MyObject.Add(foo);

其它参考3


我正面临同样的惊吓错误...... :)然后我意识到我忘了设置一个


@Html.HiddenFor(model => model.UserProfile.UserId)


对于正在更新的对象的主键!我倾向于忘记这个简单但非常重要的东西!


顺便说一句:HiddenFor适用于ASP.NET MVC。

其它参考4


检查您是否忘记了GridView中的DataKeyNames属性。
在GridView中修改数据时必须使用它


http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.datakeynames.aspx [36]

其它参考5


问题是由以下两种情况之一引起的: -



  1. 您尝试更新具有一个或多个属性的行Concurrency Mode: Fixed ..并且Optimistic Concurrency阻止了数据的保存。 IE浏览器。有些人在收到服务器数据和保存服务器数据之间更改了行数据。

  2. 您尝试更新或删除行但行不存在。有人在检索之间更改数据(在这种情况下,删除)的另一个示例然后保存或者您平息我们尝试更新字段这不是一个身份(即StoreGeneratedPattern = Computed),并且该行不存在。


其它参考6


我遇到了同样的问题,并且@webtrifusion的答案帮助找到了解决方案。


我的模型在实体的ID上使用Bind(Exclude)属性,这导致实体ID的值在HttpPost上为零。


namespace OrderUp.Models
{
[Bind(Exclude = "OrderID")]
public class Order
{
    [ScaffoldColumn(false)]
    public int OrderID { get; set; }

    [ScaffoldColumn(false)]
    public System.DateTime OrderDate { get; set; }

    [Required(ErrorMessage = "Name is required")]
    public string Username { get; set; }
    }
}   

其它参考7


我有同样的问题,我发现这是由RowVersion引起的,它是null。
检查您的 ID RowVersion 不为空


有关更多信息,请参阅本教程


http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application [[[38]

其它参考8


我得到了同样的错误,因为PK的一部分是一个日期时间列,并且插入的记录使用DateTime.Now作为该列的值。实体框架将以毫秒精度插入值,然后以毫秒精度查找刚刚插入的值。但是,SqlServer将该值四舍五入为第二个精度,因此实体框架无法找到毫秒精度值。


解决方案是在插入之前截断DateTime.Now的毫秒数。

其它参考9


编辑时将实体的id或主键包含在视图中作为隐藏字段





      @Html.HiddenFor(m => m.Id)


解决了这个问题。


此外,如果您的模型包括未使用的项目也包括它并将其发布到控制器

其它参考10


您需要明确包含主键的BoundField。如果您不希望用户看到主键,则必须通过css隐藏它:


    <asp:BoundField DataField="Id_primary_key" ItemStyle-CssClass="hidden" 
HeaderStyle-CssClass="hidden" />


其中hidden是css中的一个类,它的显示设置为none。

其它参考11


我也遇到了这个错误。它的问题是由我试图保存到的表上的触发器引起的。触发器使用INSTEAD OF INSERT,这意味着有0行插入到该表中,因此出现错误。幸运的是,触发器功能可能不正确,但我想这可能是一个有效的操作,应该以某种方式在代码中处理。希望这有一天可以帮助某人。

其它参考12


从模型优先改为代码优先后,我开始收到此错误。我有多个线程更新数据库,其中一些可能更新同一行。我不知道为什么我使用模型优先没有问题,假设它使用不同的并发默认值。


为了在一个地方处理它,知道它可能发生的条件,我在我的DbContext类中添加了以下重载:


using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;

public class MyDbContext: DbContext {
...
        public int SaveChanges(bool refreshOnConcurrencyException, RefreshMode refreshMode = RefreshMode.ClientWins) {
            try {
                return SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex) {
                foreach (DbEntityEntry entry in ex.Entries) {
                    if (refreshMode == RefreshMode.ClientWins)
                        entry.OriginalValues.SetValues(entry.GetDatabaseValues());
                    else
                        entry.Reload();
                }
                return SaveChanges();
            }
        }
}


然后在适用的地方调用SaveChanges(true)

其它参考13


  @Html.HiddenFor(model => model.RowVersion)


我的rowversion为null,因此必须将其添加到视图中
这解决了我的问题

其它参考14


[DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.None)]这一行在我的案例中起了作用:


using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;


[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int? SomeNumber { get; set; }

其它参考15


只需确保表和表单都有主键和edmx更新。


我发现更新期间的任何错误通常是因为:
  - 表中没有主键
  - 编辑视图/表单中没有主键(例如@Html.HiddenFor(m=>m.Id)

其它参考16


使用async方法时偶尔会出现此错误。自从我切换到同步方法以来没有发生过。


偶尔出错:


[Authorize(Roles = "Admin")]
[HttpDelete]
[Route("file/{id}/{customerId}/")]
public async Task<IHttpActionResult> Delete(int id, int customerId)
{
    var file = new Models.File() { Id = id, CustomerId = customerId };
    db.Files.Attach(file);
    db.Files.Remove(file);

    await db.SaveChangesAsync();

    return Ok();
}


一直工作:


[Authorize(Roles = "Admin")]
[HttpDelete]
[Route("file/{id}/{customerId}/")]
public IHttpActionResult Delete(int id, int customerId)
{
    var file = new Models.File() { Id = id, CustomerId = customerId };
    db.Files.Attach(file);
    db.Files.Remove(file);

    db.SaveChanges();

    return Ok();
}

其它参考17


当我删除DB中的某些行(在循环中)并在同一个表中添加新行时,我收到了该错误。


对我来说,解决方案是动态地在每个循环迭代中创建一个新的上下文

其它参考18


    public void Save(object entity)
    {
        using (var transaction = Connection.BeginTransaction())
        {
        try
                {
                    SaveChanges();
                    transaction.Commit();
                }
                catch (OptimisticConcurrencyException)
                {
                    if (ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Deleted || ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Modified)
                        this.Refresh(RefreshMode.StoreWins, entity);
                    else if (ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Added)
                        Detach(entity);
                    AcceptAllChanges(); 
                    transaction.Commit();
                }
        }
    }

其它参考19


我有同样的问题。
在我的情况下,我试图更新主键,这是不允许的。

其它参考20


如果您尝试在edmx文件中创建映射到函数Imports,则可能导致此错误。只需清除edmx中给定实体的映射详细信息中的插入,更新和删除字段,它就可以正常工作。
我希望我说清楚。

其它参考21


我也有这个错误。在某些情况下,实体可能不知道您正在使用的实际数据库上下文或模型可能不同。为此,set:EntityState.Modified;到EntityState.Added;


去做这个:


if (ModelState.IsValid)
{
context.Entry(yourModelReference).State = EntityState.Added;
context.SaveChanges();
}


这将确保实体知道您正在使用或添加您正在使用的州。此时需要设置所有正确的模型值。小心不要丢失可能在后台进行的任何更改。


希望这可以帮助。

其它参考22


如果您试图插入一个独特的约束情况,也就是说如果您每个雇主只能有一种类型的地址,并且您尝试在同一雇主中插入同一类型的第二种类型,那么您也会遇到同样的问题。





如果分配给所有对象属性,也可能会发生这种情况,它们被分配了与之前相同的值。


        using(var db = new MyContext())
        {
            var address = db.Addresses.FirstOrDefault(x => x.Id == Id);

            address.StreetAddress = StreetAddress; // if you are assigning   
            address.City = City;                   // all of the same values
            address.State = State;                 // as they are
            address.ZipCode = ZipCode;             // in the database    

            db.SaveChanges();           // Then this will throw that exception
        }

其它参考23


我有同样的问题。但这是由于我自己的错误。实际上我正在保存一个对象而不是添加它。所以这就是冲突。

其它参考24


在Sql Server环境中调试此问题的一种方法是使用SqlServer副本中包含的Sql Profiler,或者如果使用Express版本,请通过以下链接从CodePlex免费获取Express Profiler的副本:


Express Profiler [39]


通过使用Sql Profiler,您可以访问EF发送给数据库的任何内容。就我而言,这相当于:


exec sp_executesql N'UPDATE [dbo].[Category]
SET [ParentID] = @0, [1048] = NULL, [1033] = @1, [MemberID] = @2, [AddedOn] = @3
WHERE ([CategoryID] = @4)
',N'@0 uniqueidentifier,@1 nvarchar(50),@2 uniqueidentifier,@3 datetime2(7),@4 uniqueidentifier',
@0='E060F2CA-433A-46A7-86BD-80CD165F5023',@1=N'I-Like-Noodles-Do-You',@2='EEDF2C83-2123-4B1C-BF8D-BE2D2FA26D09',
@3='2014-01-29 15:30:27.0435565',@4='3410FD1E-1C76-4D71-B08E-73849838F778'
go


我将其粘贴到Sql Server的查询窗口中并执行它。果然,虽然它运行了,但是这个查询影响了0条记录,因此EF返回了错误。


在我的情况下,问题是由CategoryID引起的。


发送到数据库的ID EF没有识别出CategoryID,因此0个记录受到影响。


这不是EF的错,而是一个错误的空值合并??语句在View Controller中向无意义的数据层发送。

其它参考25


上述答案都没有完全涵盖我的情况和解决方案。


在MVC5控制器中抛出错误的代码:


        if (ModelState.IsValid)
        {
            db.Entry(object).State = EntityState.Modified; 
            db.SaveChanges(); // line that threw exception
            return RedirectToAction("Index");
        }


我在编辑视图中保存对象时收到此异常。它抛出它的原因是因为当我回去保存它时,我修改了在对象上形成主键的属性。因此,将其状态设置为Modified对EF没有任何意义 - 它是一个新条目,而不是以前保存的条目。


您可以通过以下方法解决此问题:A)修改保存调用以添加对象,或者B)在编辑时不要更改主键。我做了B)。

其它参考26


我在一个缺少主键并且有DATETIME(2,3)列的表上遇到了这个问题(因此实体s主键是所有列的组合)...执行插入时时间戳有一个更精确的时间(2018-03-20 08:29:51.8319154)被截断为(2018-03-20 08:29:51.832),因此关键字段的查找失败。

其它参考27


我使用Telerik的RadGrid遇到了这个问题。我将主键作为一个网格列设置为只读。如果列是display =false,它会正常工作但readonly =true会导致问题。我通过使gridbound列display=false并添加单独的模板列进行显示来解决它


<telerik:GridBoundColumn HeaderText="Shouldnt see" Display="false" 
     UniqueName="Id" DataField="Id">
</telerik:GridBoundColumn>
<telerik:GridTemplateColumn HeaderText="Id" UniqueName="IdDisplay">
    <ItemTemplate>
        <asp:Label ID="IDLabel" runat="server" 
            Text='<%# Eval("Id") %>'></asp:Label>                               
    </ItemTemplate>
</telerik:GridTemplateColumn> 

其它参考28


当附加一个在数据库中不存在的对象时,我遇到了这个异常。我假设对象是从一个单独的上下文加载的,但是如果是用户第一次访问该站点,则该对象是从头开始创建的。我们有自动递增的主键,所以我可以替换


context.Users.Attach(orderer);





if (orderer.Id > 0) {
    context.Users.Attach(orderer);
}

其它参考29


如果尝试使用数据库中不存在的Id更新记录,则可能会发生这种情况。