无法使用EFCore编辑数据库条目,EntityState.Modified:“数据库操作预计会影响1行,但实际上会影响0行。”

时间:2016-09-13 00:48:37

标签: c# entity-framework asp.net-core asp.net-identity entity-framework-core

我正在使用Identity Core 1.0与ASP.NET MVC Core 1.0和Entity Framework Core 1.0创建一个以this article为起点的简单用户注册系统,我正在尝试添加用户角色。我可以添加用户角色,但我无法编辑它们。以下是Edit中的RolesController操作:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Edit(IdentityRole role)
    {
        try
        {
            _db.Roles.Attach(role);
            _db.Entry(role).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
            _db.SaveChanges();
            return RedirectToAction("Index");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            return View();
        }
    }

以下是相应视图中的表单:

@model Microsoft.AspNet.Identity.EntityFramework.IdentityRole
@{
    ViewBag.Title = "Edit";
}

<h2>Edit Role</h2>
<hr />
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)
    @Html.HiddenFor(model => model.Id)
    <div>Role name</div>
    <p>@Html.TextBoxFor(model => model.Name)</p>
    <input type="submit" value="Save" />
}

新角色名称未保存到数据库,我收到以下异常:Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded.

我能够使用这个确切的代码(使用Microsoft.AspNet.Identity.EntityFramework依赖项而不是EntityFrameworkCore)来编辑使用EF 7,Identity 3等的数据库条目。

有关为什么此代码不允许修改数据库条目的任何想法?

13 个答案:

答案 0 :(得分:3)

除非隐藏的异常隐藏在此作为一个愚蠢的随机异常,否则原因在异常中明确说明。

在您Id操作中收到role对象上的Edit,然后尝试在数据库中查找该ID。您看到的异常消息指出,它期望找到一个与您附加的对象具有匹配ID的行,但它不是,因此它无法执行更新,因为它无法找到匹配的行来更新它

编辑:

您要将实体附加两次,移除对.Attach(role)的调用并保留其下方的行,这足以将对象添加到处于修改状态的跟踪上下文中。

//_db.Roles.Attach(role); //REMOVE THIS LINE !.
_db.Entry(role).State = Microsoft.EntityFrameworkCore.EntityState.Modified;

请注意,将条目的状态设置为modified将在调用.SaveChanges()时更新所有属性值,因此,如果您只想更新某些属性,请参阅this answer

如果这不能解决您的问题,请检查您可能错过的任何内部异常。有时,异常消息没有意义,并掩盖了您可能在内部异常中找到的真正问题。

答案 1 :(得分:1)

您可以尝试如下所示。

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(IdentityRole role)
{
    try
    {
         _db.Entry(role).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
        _db.SaveChanges();
        return RedirectToAction("Index");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
        return View();
    }
}

注意:

_db.Entry(role).State = EntityState.Modified;

  • 您不仅要将实体附加到_db,还要标记。{ 整个实体为dirty
  • 执行_db.SaveChanges()时,EF会生成update 陈述will update all the fields of the entity

_db.Roles.Attach(role)

  • 将实体附加到上下文 而不将其标记为脏
  • 就像_db.Entry(role).State = EntityState.Unchanged;
  • 除非您继续update a property on the entity,否则下次致电context.SaveChanges()时,EF 将无法生成 database update entity _db.Roles.Attach(role); // State = Unchanged role.RoleName = "Admin"; // State = Modified, and only the RoleName property is dirty context.SaveChanges(); 1}}。

如果您需要生成数据库更新,则必须执行以下操作:

body{overflow:hidden;}

答案 2 :(得分:1)

您的答案对我不起作用。我这样解决了我的错误。 更改的模型类别比例

[Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public string Id { get; set; }

在我更改了我的映射类之后

builder.Property(c => c.Id).HasColumnName("ID").IsRequired();

最后一次更改是

 CustomerEntity thisrole = (from x in db.Customers
                          where x.Id == Id
                          select x).First();
            thisrole.Id = accountNum;
            thisrole.Name = name;
            thisrole.Phone = phone;
            thisrole.Email = email;
            thisrole.Address = address;
            db.SaveChanges();              
            return true;

我希望此解决方案适用于某人。

答案 3 :(得分:1)

如果您尝试更新具有新的相关实体的实体,也会发生这种情况。

someEntity.AnotherEntity = new AnotherEntity();
dbContext.Update(someEntity);
dbContext.SaveChanges(); // Exception

相反,请执行以下操作:

someEntity.AnotherEntity = new AnotherEntity();
dbContext.AnotherEntitySet.Add(someEntity.AnotherEntity);
dbContext.Update(someEntity);
dbContext.SaveChanges(); // No Exception

答案 4 :(得分:0)

我通过结合两种方法解决了这个问题

var thisRole = _db.Roles.Where(r => r.Id.Equals(role.Id, 
StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
_db.Roles.Attach(thisRole);
thisRole.Name = role.Name;
_db.SaveChanges();

答案 5 :(得分:0)

如果表上有触发器INSTEAD OF INSERT,则数据库取消操作,并且EntityFramework会引发此错误。

答案 6 :(得分:0)

这里有同样的错误,最终导致的问题是我在重用更新代码时正在插入...

  Role role = new Role();
  role.Value = input;
  context.Add(role);                    
  await context.SaveChangesAsync();

插入时没有状态可以更改...

答案 7 :(得分:0)

执行更新或删除后,EF Core读取受影响的行数。

SELECT [ExampleEntityId]
FROM [ExampleEntities]
WHERE @@ROWCOUNT = 1 AND
[ExampleEntityId] = scope_identity();

如果您的实体没有作为主键的IdentityColumn,则EF Core会引发DbUpdateConcurrencyException。

在我的情况下,我向数据库中的相关表添加了主键(并将它们设置为标识)。

答案 8 :(得分:0)

在mysql中,它需要一个AUTO_INCREMENT ID

像这样:

更改表wfdbcore201testwftransitioninstance 修改列ID int(11)NOT NULL AUTO_INCREMENT FIRST;

答案 9 :(得分:0)

使用Razor Pages时,没有一个可接受的答案对我有用。 我最终使用InputModel Input对其进行了修复。 (我在角色上有自己的课程)。

    public class AppRole:IdentityRole<int> { }
 [BindProperty]
 public InputModel Input { get; set; }
 public class InputModel
 {
     [Required]
     public string Name { get; set; }

     public string NormalizedName=>Name.ToUpper();

     public int Id { get; set; }

     // Used to check for concurrency in the post method
     public string OriginalConcurrencyStamp { get; set; }

 }

通过OnGet方法播种输入。

 public IActionResult OnGet(int? id)
 {
      if (id == null)
      {
          return NotFound();
      }

      var appRole = _context.Roles.Find(id);

      if(appRole == null) { return NotFound(); }

      Input.Id = id.Value;
      Input.Name = appRole.Name;
      Input.OriginalConcurrencyStamp = appRole.ConcurrencyStamp;

      return Page();
  }

然后在发布方法中,我“找到”角色并在那里进行更新。

 public IActionResult OnPostAsync()
 {
     if (!ModelState.IsValid)
     {
         return Page();
     }

     var appRole = _context.Roles.Find(Input.Id);

     // The role has been deleted
     if(appRole == null)
     {
         ViewData["Error"] = "This Role has been deleted by someone else.\nReturn to the list.";
         return Page();
     }

     // the role has been changed by another user.
     if(appRole.ConcurrencyStamp != Input.OriginalConcurrencyStamp)
     {
         ViewData["Error"] = "This Role has been changed by someone else.\nReturn to the list.";
         return Page();
     }

     // no need to SaveChanges if there are no changes.
     var hasChanges = false;
     if(appRole.Name != Input.Name)
     {
         appRole.Name = Input.Name;
         hasChanges = true;
     }

     if(appRole.SortOrder != Input.SortOrder)
     {
         appRole.SortOrder = Input.SortOrder;
         hasChanges = true;
     }

     if(appRole.NormalizedName != Input.NormalizedName)
     {
         appRole.NormalizedName = Input.NormalizedName;
         hasChanges = true;
     }

     if(!hasChanges)
     {
         ViewData["Error"] = "No Changes Detected.";
         return Page();
     }

     appRole.ConcurrencyStamp = Guid.NewGuid().ToString();

     _context.Entry(appRole).State = EntityState.Modified;

     try
     {
         _context.SaveChanges();
     }
     catch (DbUpdateConcurrencyException ex)
     {
         if (!AppRoleExists(Input.Id))
         {
             return NotFound();
         }
         else { throw; }
     }

     return RedirectToPage("./Index");
 }

我的Edit.cshtml文件如下所示。

@page
@model MyApp.Areas.Identity.Pages.Account.Manage.Roles.EditModel

@{
    ViewData["Title"] = "Edit";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>Edit</h1>

<h4>Role</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Input.Id" />
            <input type="hidden" asp-for="Input.OriginalConcurrencyStamp" />
            <label class="text-danger">@ViewData["Error"]</label>
            <div class="form-group">
                <label asp-for="Input.Name" class="control-label"></label>
                <input asp-for="Input.Name" class="form-control" />
                <span asp-validation-for="Input.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />

            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

答案 10 :(得分:0)

在我的情况下,问题是我试图从表中删除一个值而不检查该值是否实际存在:


public DbSet<AdminSettings> AdminSettingsTable { get; set; }

public AdminSettings AdminSettings
  {
    get
    {
      return AdminSettingsTable.FirstOrDefault();
    }
    set
    {
      // This if-statement prevents the crash
      if (AdminSettingsTable.Contains(value))
      {
        AdminSettingsTable.Remove(value);
      }
      SaveChanges();
      AdminSettingsTable.Add(value);
   }
}

答案 11 :(得分:0)

我在 delete 操作中遇到此错误。

就我而言,我试图在其他表中删除一行以及与其相关的行(外键关系)。事实证明,如果删除父项,则在数据库中添加了一个触发器以删除相关行。如果要删除,请确保您尝试删除的行存在,或者您是否添加了任何触发器来删除相关的行。

答案 12 :(得分:-1)

对于 SQL Server,EF Core 在 Where 子句中包含时间戳属性,我仍然不明白为什么。所以我必须跟踪主键和时间戳以重新附加实体。

TL;DR:如果您的 Update 语句的 Where 子句中缺少某些内容,请检查您的(EF Core)生成的 SQL。

示例:

实体:

CREATE TABLE [dbo].[Blog]
    [BlogID] [int] IDENTITY(0,1) NOT NULL,
    [sysTS] [timestamp] NOT NULL,
    [IsPublished] [bit] NOT NULL

public class Blog {
  public int BlogId { get; set; }   
  public byte[] SysTS { get; set; }
  public bool IsPublished { get; set; }
}

代码:

var blog = new Blog { IsPublished = false };
using(var context = CreateNewContext()) {
  context.Blog.Add(blog);
  await context.SaveChangesAsync();
}
var blogId = blog.BlogID
// var blogSysTs = blog.SysTs; // this I had to track too

// ... do other stuff

// here also SysTS = blogSysTs was needed
var blog2 = new Blog { BlogID = blogId, IsPublished = true }; 
using(var context2 = CreateNewContext()) {
  var entry = context2.Blog.Attach(blog2);
  entry.Property(b => b.IsPublished).IsModified = true;
  await context2.SaveChangesAsync();
}

结果如下:

UPDATE Blog
SET IsPublished = @p1
WHERE BlogID = @p2 and SysTS IS NULL

只有在 SysTS 被跟踪的情况下才有合法的更新查询:

UPDATE Blog
SET IsPublished = @p1
WHERE BlogID = @p2 and SysTS = @p3

我知道这肯定与 SQL Server 设置无关,我只是不知道为什么。 但它的要点是:检查您的 EF Core 生成的 SQL 查询以了解该行未更新的原因。