【问题标题】:C# - Entity Framework Core - Database operation expected to affect 1 row(s) but actually affected 0 row(s)C# - Entity Framework Core - 数据库操作预计会影响 1 行,但实际上影响了 0 行
【发布时间】:2021-02-16 19:21:04
【问题描述】:

我正在使用 Entity Framework 与 SQL Server 数据库交互的 ASP.NET Core 3.1 中构建一个 n 层 Web 应用程序。一切正常,除了两次更新实体而不在两个请求之间刷新。

所以我第一次从数据库中读取实体并更新它时,它工作得很好。当我尝试在之后立即更新实体时,出现以下异常:

Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException:数据库操作预计会影响 1 行,但实际上影响了 0 行。
自加载实体后,数据可能已被修改或删除...

我将通用存储库模式与异步代码结合使用。

我的代码概览:

GenericRepository.cs:

public class Repository<T> : IRepository<T> where T : class
{        
    protected readonly DataContext _context;
    protected DbSet<T> _entities;
    
    public Repository(DataContext context)
    {
        this._context = context;
        _entities = context.Set<T>();
    }
}

MemberRepository.cs:

public class MemberRepository : Repository<Member>, IMemberRepository
{    
    public MemberRepository(DataContext context) : base(context)
    {
        //
    }

    public async override Task<Member> SelectByIdAsync(int id)
    {
        return await this._entities
                    .Where(m => m.Id == id)
                    .Include(m => m.Addresses)
                    .Include(m => m.MemberHobbies)
                    .Include(m => m.Car)
                    .Include(m => m.EmailAddresses)
                    .FirstOrDefaultAsync();
        
        //return await this._context.Members.Where(m => m.Id == id).Include(m => m.Addresses).Include(m => m.MemberHobbies).Include(m => m.Car).Include(m => m.EmailAddresses).FirstOrDefaultAsync();
    }

    public async override Task UpdateAsync(Member member)
    {
        this._context.Members.Update(member);
        //this._context.Set<Member>().Update(member);
        await this._context.SaveChangesAsync();
    }
}

startup.cs:

services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
services.AddScoped<IMemberRepository, MemberRepository>();

我尝试改变阅读或更新实体的方式(请参阅注释掉的行),但结果相同。另外:我还有其他可以完美运行的实体,唯一的区别是 Member 类有多个外键(参见 .Include(...))

可能与异步函数使用不当有关吗?可能是因为我没有返回更新后的成员,因此下次执行更新时,我发送的是一个现已过期的成员?

另外:我找到了this answer,但这对我来说毫无意义

谢谢!

【问题讨论】:

    标签: c# entity-framework asynchronous .net-core entity-framework-core


    【解决方案1】:

    我使用这样的通用代码进行更新:

    public virtual async Task<T> UpdateAsync(T t)
            {
                if (t == null) return null;
    
                T exist;
    
                try
                {
                    exist = await Context.Set<T>().FindAsync(t.Id);
                    if (exist == null)
                    {
                        t.ErrorMessage = "Can't find item to update";
                        return t;
                    }
                    Context.Entry(exist).CurrentValues.SetValues(t);
                    var result = await Context.SaveChangesAsync();
                    if (result == 0) t.ErrorMessage = "Can't saved item";
                }
                catch (Exception ex)
                {
    
                    t.ErrorMessage = ex.Message;
                    return t;
                }
    
    
                return exist;
            }
    
    

    或者你可以试试这个会员类的代码:

    public async Task UpdateAsync(Member member)
    {
     try
     {
        var exist = await _context.Members.Where(i.Id=member.Id).FirstOrDefaultAsync();
       if (exist == null) ....errorMessage = "Can't find item to update";
     
       Context.Entry(exist).CurrentValues.SetValues(member);
       var result = await Context.SaveChangesAsync();
    
       if (result == 0) ....errorMessage = "Can't save item";
    
     }
     catch (Exception ex)
     {
        ...errorMessage = ex.Message;
     }
    }
    

    【讨论】:

      【解决方案2】:

      EF 会跟踪您的数据。这是你要做的:

      1. 获取同一条目的两个副本;
      2. 更改之前的副本;
      3. 更新之前副本的更改;
      4. 更改后一个副本,该副本会跟踪第 3 步之前的条目状态;
      5. 更新后一个副本所做的更改,但由于此更改反映到条目的前一个状态 - 您会收到该错误;

      你应该这样做:

      1. 为您的业务对象添加一个额外的层,用于携带状态;
      2. 在收到更新请求后获取数据库条目的当前状态;
      3. 将更改应用到您的数据库条目;
      4. 保存;

      例子:

      // DB entry
      public class Member
      {
          public int Id { get; set; }
          public string Name { get; set; }
      }
      
      // DTO for carrying around
      public class MemberForNameChangeDto
      {
          public int Id { get; set; }
          public string Name { get; set; }
      }
      
      public class Repo
      {
          public Task<bool> UpdateMemberName(MemberForNameChangeDto memberForNameChange)
          {
              // _context.Members is DbSet<Member>
              var memberEntry = _context.Members.Find(memberForNameChange.Id);
              // null checks, verifications, etc
              memberEntry.Name = memberForNameChange.Name;
              var result = await _context.SaveChangesAsync();
      
              return result > 0;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2018-09-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-31
        相关资源
        最近更新 更多