【问题标题】:After checking if a record exist, and it doesn't, SaveChanges gives an DbUpdateException检查记录是否存在后,SaveChanges 给出 DbUpdateException
【发布时间】:2017-04-05 13:27:28
【问题描述】:

我有一个表,它在两列 F1Kant[1] 上有一个唯一索引。没有外键,只有主键(一个整数 Id)和这个索引。

在添加新记录之前,我会检查数据库中是否存在具有此索引的记录。[2] 我知道它没有! ExistsLaserData 返回 false。

然后我将记录添加到 DbSet 并执行 SaveChanges 但现在我得到一个异常

DbUpdateException: SqlException: 违反 UNIQUE KEY 约束 'IX_laserdata_F1_Kant'。无法在对象中插入重复键 'dbo.LaserData'。重复键值为 (123456, NULL)。

这是我添加的记录!

上下文被初始化为

LaserDataContext context = new LaserDataContext();
OrderBy(x => x.F1).ThenBy(x => x.Kant).ToList();

我正在使用的两种方法

public bool ExistsLaserData(string article, string kant)
{
    bool result = false;
    var found = _context.LaserData.Where(x => x.F1 == article && x.Kant == kant).FirstOrDefault();
    if (found != null)
    {
        result = true;
    }

    return result;
}

public void AddLaserData(ref LaserData d)
{
    try
    {
        _context.LaserData.Add(d);
        _context.SaveChanges();
    }
    catch (Exception ex)
    {
        throw;
    }

}

为什么在我检查时记录不存在,或者为什么在我执行 SaveChanges 时它存在?

[1] 这个数据库的开头是一个 Excel 文档,所以名字很奇怪...

[2] 我不想跳过 ExistLaserData 检查,所以请不要建议我只使用异常。

【问题讨论】:

  • 我通常尝试添加返回输入的行数。如果我得到零,则使用更新来更改值。当你有一个主键时,每个唯一 id 在数据表中只允许一行。
  • 如果这个系统有可能被多个用户使用并且这些键值是用户提供的,那么 no 量的“预检查”可以让您免于种族,所以你必须处理异常无论如何
  • 是的,但是我可以像处理异常一样处理它,我会这样做。但这是在添加新记录的对话框中,我想给用户一个“提示”,表明他正在尝试添加已经存在的记录...

标签: c# sql-server entity-framework-6


【解决方案1】:

在这里,您正在检查同一行中的两个值,并且仅当单个行的两个键相同时才返回一行。

你可以试试这个吗?代替 && ,使用 ||

public bool ExistsLaserData(string article, string kant)
{
    bool result = false;
    var found = _context.LaserData.Where(x => x.F1 == article || x.Kant == kant).FirstOrDefault();
    if (found != null)
    {
        result = true;
    }

    return result;
}

【讨论】:

  • 我觉得我表达自己有点笨拙。它是一个同时包含 F1 和 Kant 的索引。这两列的组合必须是唯一的,但 F1 可能有许多相同的值(康德的值不同),康德的许多值也可能是保存的(F1 的值不同)。那么这不会让我不得不使用 && 吗?
  • 是的,你应该按照你说的这种方式使用 &&。我认为如果您使用 LaserData 发现它会更好,也许这就是它返回 false 的原因。
  • 好的.. 是的,你是对的。在这种情况下,您应该使用“&&”。您也可以添加调用方法吗?你也可以使用 Any 而不是 Where。 bool found = _context.LaserData.Any(x => x.F1 == article && x.Kant == kant);
  • 他也可以用这个:return _context.LaserData.Any(x => x.F1 == article && x.Kant == kant);更短的代码
【解决方案2】:

我认为您应该将文章转换为整数,然后检查它是否存在。

正如你所说的 F1 是 int 而 article 是 string 所以你不能比较它们。

然后您可以使用它来添加或更新:

LaserData found = _context.LaserData.FirstOrDefault( p => p.F1==(int) article);
if(found == null)
{
    _context.LaserData.Add(d);
    _context.SaveChanges();
}

我希望这对你有帮助。如果这不是你想要的,也许你需要解释更多。

如果文章类型像 F1,那么你可以在不强制转换为 int 的情况下编写:

LaserData found = _context.LaserData.FirstOrDefault( p => p.F1== article);

如果它不是唯一的,您不必检查 kant。但是你可以检查你是否喜欢。

编辑: 就用这个:

public bool ExistsLaserData(string article, string kant)
{
bool result = false;
LaserData found = _context.LaserData.FirstOrDefault(x => x.F1 == article && x.Kant == kant);
if (found != null)
{
    result = true;
}

return result;

}

【讨论】:

  • 没有 F1 是 NVARCHAR(50) 而康德是 CHAR(1)。还有一个 Id,它是一个 IDENTITY INT,它是主键。我在 F1 和 Kant 两列上有一个索引,这是唯一的。
  • 试过了,我得到了同样的异常。
  • 我认为这对你来说是最好的方法:LaserData found = _context.LaserData.FirstOrDefault( p => p.F1== article && p.kant==kant); if(found == null) { _context.LaserData.Add(d); _context.SaveChanges(); }
【解决方案3】:

您的kant 属性很可能没有正确映射。这将解释异常和代码的行为。

  • 异常表明您尝试为 kant 插入 null 值,这是有道理的,因为由于映射不正确,EF 从未发送过值。
  • 只要您不将null 传递给kant 参数的方法,ExistsLaserData 总是返回false。这将导致您的代码始终尝试 AddLaserData 方法。

因此解决方法是确保正确映射此属性并纠正列。


如果不是这样,那么您将尝试插入多个具有相同 F1 值但具有 null kant 值的记录,这会破坏唯一索引。解决这个问题的唯一方法是使用 where 过滤器更改索引,以便它只检查 kant 是否有值。

CREATE UNIQUE NONCLUSTERED INDEX [IX_laserdata_F1_Kant] ON dbo.LaserData (F1 ASC, Kant ASC) WHERE (Kant IS NOT NULL)

【讨论】:

    【解决方案4】:

    好的,我找到了错误发生的位置,它与 ExistsLaserData 无关。 ExistsLaserData 完美运行!

    因为这是一个全新的问题,我将提出一个新问题

    感谢您的所有建议,我将使用其中的一些!

    PS!作为预告片。在 SaveChanges 事件的覆盖中,我 Trim() 所有字符串字段。这会导致 base.SaveChanges 抛出异常...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-20
      • 2015-01-21
      • 2012-08-13
      • 2015-01-02
      • 1970-01-01
      相关资源
      最近更新 更多