【问题标题】:ASP.NET Core - How to update existing record and insert a new one at the same time using Entity FrameworkASP.NET Core - 如何使用实体框架更新现有记录并同时插入新记录
【发布时间】:2023-01-28 06:44:53
【问题描述】:

在 ASP.NET Core-6 Web API 实体框架中,我希望应用程序同时对数据库中的同一模型执行更新和插入。

我有这段代码:

public async Task<Response<string>> CreateIdentiticationAsync(CreateIdentiticationDto model)
{
    var response = new Response<string>();
    using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
    {
            try
            {
                var identification = _mapper.Map<Identification>(model);
                var existingIdentifications = await _dbContext.Identifications.Where(e => e.IsModified == false).ToListAsync();
                foreach (var existingIdentification in existingIdentifications)
                {
                    if (existingIdentification != null)
                    {
                        existingIdentification.IsModified = true;
                        _unitOfWork.UserIdentifications.Update(identification);
                        await _unitOfWork.Save();
                    }
                }
                Identification.IsModified = false;
                Identification.Type = model.Type;
                Identification.Name = model.Name;

                await _unitOfWork.UserIdentifications.InsertAsync(identification);
                await _unitOfWork.Save();
                response.StatusCode = (int)HttpStatusCode.Created;
                response.Successful = true;
                response.Message = "Created Successfully!";
                transaction.Complete();
                return response;
            }
            catch (Exception ex)
            {
                transaction.Dispose();
                response.Message = "An error occured";
                response.Successful = false;
                response.StatusCode = (int)HttpStatusCode.BadRequest;
                return response;
            }
    }
}

当用户想要插入一条新记录时,我希望应用程序首先检查模型,如果不存在记录,它应该只插入新记录。

但如果它存在,它应该将所有 IsModified 更新为 true,然后继续并插入一条新记录。

但是,当不存在记录时,我可以插入新记录。 但是我遇到的问题是,当它想要更新和插入新记录时,出现了这个错误:

违反 PRIMARY KEY 约束“PK_identifications”。无法在对象“dbo.identifications”中插入重复键。重复键值为 (81fe9b8d-2d9c-4d49-8f92-22afe043e327)。

注意:ID 是 Guid 并且是自动生成的

我该如何解决这个问题?

谢谢

【问题讨论】:

  • DbContext 不需要 TransactionScope 或“UnitOfWork”——它已经一个工作单元。它跟踪所有变化并持续存在全部当调用 SaveChanges 时,它们在数据库事务中。在循环中调用 Save() 要么什么都不做,要么破坏工作单元功能。我怀疑这就是您在顶部添加 TransactionScope 的原因。这只是掩盖了问题
  • 什么是_unitOfWork,它的Save方法有什么作用?除非它是空操作,否则它只会导致问题。 Identifications 是如何定义的?什么是主键,它是如何设置的?如果它是数据库生成的,则不会有重复项。如果按预期使用 EF Core,则也不会有重复项,即使您尝试两次附加相同的分离对象也是如此。仅当两个不同的对象具有相同的 PK 值时才会出现该错误
  • 这段代码有什么作用?似乎有一个部分更新了 identification 对象 N 次,每个存储的未修改对象都有一个。另一个是插入同一个 identification 已经更新了 N 次了。还有一个是修改不相关的 Identification 对象但从不存储它。如果你想存储 identification 对象,你只需要两行,_context. Update(identification); _context.SaveChanges();

标签: c# entity-framework-core


【解决方案1】:

这里要考虑很多事情,在现代 EF 中,我不会使用 unitOfWork 也不需要事务,除非非常具体的情况,您还应该验证传入模型值中的 ID 是否为空,因为 ID 应该是自动生成的(很可能这是你错误的根源)。由于我不知道您的 PK 字段的名称,因此我不会在我建议的代码中引入此类验证。

最后一个建议,避免在你的控制器上使用这段代码,它违反了单一职责原则。创建一个层,您可以在其中执行业务任务并从您的控制器调用该层。

这是一个应该有效的简化建议代码,不要忘记确保模型 ID 中的值为空。

public async Task<Response<string>> CreateIdentiticationAsync(CreateIdentiticationDto model)
{
    var response = new Response<string>();
    try
    {
        var identification = _mapper.Map<Identification>(model);
        var existingIdentifications = await _dbContext.Identifications.Where(e => e.IsModified == false).ToListAsync();
        foreach (var existingIdentification in existingIdentifications)
        {
            existingIdentification.IsModified = true;
        }

        Identification.IsModified = false;

        // If you already mapped this in line 6 why map those two fields again manually?
        // Identification.Type = model.Type;
        // Identification.Name = model.Name;

        _dbContext.UpdateRange(existingIdentifications);
        _dbContext.Add(identification);
        await _dbContext.SaveChangesAsync();

        response.StatusCode = (int)HttpStatusCode.Created;
        response.Successful = true;
        response.Message = "Created Successfully!";

        return response;
    }
    catch (Exception ex)
    {
        transaction.Dispose();
        response.Message = "An error occured";
        response.Successful = false;
        response.StatusCode = (int)HttpStatusCode.BadRequest;
        return response;
    }
}

【讨论】:

    【解决方案2】:

    公共 IActionResult 更新(int?id,UpdateEmployeeVM employeeVM) { ViewBag.Positions = new SelectList(_context.Positions, nameof(Position.Id), nameof(Position.Name)); 员工已存在=_context.Employees.Find(id); if(existed==null) { NotFound();} if (!_context.Positions.Any(p => p.Id == employeeVM.PositionId)) ModelState.AddModelError("PoitionId", "Choose Id right"); 如果(!ModelState.IsValid) { ViewBag.Positions = new SelectList(_context.Positions, nameof(Position.Id), nameof(Position.Name)); 返回视图(); } 如果 (employeeVM.Image!=null) { string result = employeeVM.Image.CheckValidate("image/", 300); 如果(结果。长度> 0) { ViewBag.Positions=new SelectList(_context.Positions,nameof(Position.Id),nameof(Position.Name)); ModelState.AddModelError("图片", 结果); } existed.ImageUrl.DeleteFile(_env.WebRootPath, "assets/img"); existed.ImageUrl = employeeVM.Image.SaveFile(Path.Combine(_env.WebRootPath, "assets", "img"));

                return View();
            }
            existed.PositionId=employeeVM.PositionId;
            existed.FullName=employeeVM.FullName;
           
            existed.ImageUrl = employeeVM.Image.SaveFile(Path.Combine(_env.WebRootPath, "assets", "img"));
            
            _context.SaveChanges();
            return RedirectToAction(nameof(Index));
        }
    

    【讨论】:

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