首页 > 解决方案 > 将两个连续行插入数据库时​​出现问题

问题描述

我有这个功能,它工作得很好

       public  DemandeConge Creat(DemandeConge DemandeConge)
    {

        try
        {
            var _db = Context;
            int numero = 0;
            //??CompanyStatique
            var session = _httpContextAccessor.HttpContext.User.Claims.ToList();
            int currentCompanyId = int.Parse(session[2].Value);

            numero = _db.DemandeConge.AsEnumerable()
                          .Where(t => t.companyID == currentCompanyId)
                          .Select(p => Convert.ToInt32(p.NumeroDemande))
                          .DefaultIfEmpty(0)
                          .Max();
            numero++;
            DemandeConge.NumeroDemande = numero.ToString();
            //_db.Entry(DemandeConge).State = EntityState.Added;
            _db.DemandeConge.Add(DemandeConge);
            _db.SaveChanges();

            return DemandeConge;
        }
        catch (Exception e)
        {
            return null;
        }
    }

但就在我尝试在插入后直接插入另一个休假需求时(无需等待或刷新页面)出现一个错误,表明这个新的 demand.id 存在

我认为我需要在保存更改后添加刷新?任何帮助和感谢

标签: sql-serverentity-frameworkasp.net-core

解决方案


像这样的代码:

        numero = _db.DemandeConge.AsEnumerable()
                      .Where(t => t.companyID == currentCompanyId)
                      .Select(p => Convert.ToInt32(p.NumeroDemande))
                      .DefaultIfEmpty(0)
                      .Max();
        numero++;

是一个很差的模式。您应该通过 Identity 列将“numero”(ID)的生成留给数据库。在您的数据库中进行设置(如果是数据库优先),并将此列的映射设置为 DatabaseGenerated.Identity。

但是,您的代码引发了很多问题。为什么它是 String 而不是 Int?这将是使用标识列的问题。

您希望避免使用这样的代码的原因是因为每个请求都希望查询数据库以获取“最大”ID,一旦您获得两个相对同时运行的请求,您将收到 2 个请求说最大 ID 为“ 100" 之前可以保留和插入 101,因此两者都尝试插入 101。通过使用标识列,数据库将获得 2x 插入并为它们提供 ID 先到先得。当您为关系设置导航属性时,EF 可以自动为您管理围绕这些新 ID 关联的 FK。(而不是尝试手动设置 FK,这是开发人员尝试获取新 ID 应用端的典型罪魁祸首)

如果您无法使用现有架构,其中 PK 是公司 ID 和此 Numero 列作为字符串的组合,那么您所能做的就是实施重试策略来解决重复问题:

const int MAXRETRIES = 5;

var session = _httpContextAccessor.HttpContext.User.Claims.ToList();
int currentCompanyId = int.Parse(session[2].Value);
int insertAttemptCount = 0;

while(insertAttempt < MAXRETRIES)
{
    try
    {
        numero = Context.DemandeConge
            .Where(t => t.companyID == currentCompanyId)
            .Select(p => Convert.ToInt32(p.NumeroDemande))
            .DefaultIfEmpty(0)
            .Max() + 1;
        DemandeConge.NumeroDemande = numero.ToString();
        Context.DemandeConge.Add(DemandeConge);
        Context.SaveChanges();
        break;
    }
    catch (UpdateException)
    {
        insertAttemptCount++;
        if (insertAttemptCount >= MAXRETRIES)
            throw; // Could not insert, throw and handle exception rather than return #null.
    }
}
return DemandeConge;

即使这也不是万无一失的,并且可能导致负载失败,而且要解决糟糕的数据库设计需要大量代码,所以我的第一个建议是修复架构,因为这样的编码容易出错且脆弱.


推荐阅读