You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用EF Core时SQL Server插入触发器未触发,仍抛出重复键异常

EF Core 3.1.15中INSTEAD OF INSERT触发器未生效的原因分析

问题描述

我编写了一个用于处理重复键插入尝试的INSTEAD OF INSERT触发器:

-- Create trigger to not throw in case of duplicate mapping attempt
CREATE TRIGGER dbo.BlockDuplicates_Product_Category_Mapping
ON dbo.Product_Category_Mapping
INSTEAD OF INSERT
AS
BEGIN
  SET NOCOUNT ON;

  IF NOT EXISTS (SELECT 1 
                 FROM inserted AS i 
                 INNER JOIN dbo.Product_Category_Mapping AS p
                            ON i.ProductId = p.ProductId
                            AND i.CategoryId = p.CategoryId)
  BEGIN
      INSERT INTO dbo.Product_Category_Mapping (ProductId, CategoryId, IsFeaturedProduct, DisplayOrder)
          SELECT ProductId, CategoryId, IsFeaturedProduct, DisplayOrder 
          FROM inserted;
  END
  ELSE
  BEGIN
      PRINT 'Duplicate';
  END
END

直接在数据库管理系统中运行插入语句时,触发器可以正常工作,但使用EF Core(版本3.1.15)通过导航属性插入数据时,触发器似乎未生效,仍抛出重复键异常:

Microsoft.EntityFrameworkCore.Relational: An error occurred while updating the entries. See the inner exception for details. Core Microsoft SqlClient Data Provider: Violation of UNIQUE KEY constraint 'UQ_PCM_ProductId_CategoryId'. Cannot insert duplicate key in object 'dbo.Product_Category_Mapping'. The duplicate key value is (920, 129).

问题原因

1. 触发器未处理插入批次内的重复记录

当前触发器仅检查插入记录与表中已有数据的重复情况,但如果EF Core一次插入多条重复的关联记录(比如通过导航属性重复添加同一个关联),inserted表中会存在多条相同ProductId+CategoryId的记录。此时触发器的IF NOT EXISTS判断会认为没有与表中现有数据重复,从而执行INSERT操作,而插入多条重复记录会直接触发数据库的唯一键约束,导致报错。

2. 触发器的批量处理逻辑缺陷

当前触发器的逻辑是「只要插入批次中有任意一条记录与表中现有数据重复,就完全跳过所有插入」,而非仅跳过重复记录、插入不重复的记录。这种逻辑在EF Core的批量或多次插入场景下,会导致要么全插(可能包含批次内重复)要么全不插,无法正确处理部分重复的情况。

3. EF Core导航属性的插入行为

EF Core在处理导航属性关联时,可能会将关联关系的插入拆分为多条独立INSERT语句,或者在一次插入请求中包含重复的关联记录。当插入重复记录时,触发器的现有逻辑无法拦截批次内的重复,最终触发数据库的唯一键约束。

修正后的触发器示例

-- Create trigger to handle duplicate mapping attempts (including batch duplicates)
CREATE TRIGGER dbo.BlockDuplicates_Product_Category_Mapping
ON dbo.Product_Category_Mapping
INSTEAD OF INSERT
AS
BEGIN
  SET NOCOUNT ON;

  -- 插入不重复的记录:先对inserted去重,再排除表中已存在的
  INSERT INTO dbo.Product_Category_Mapping (ProductId, CategoryId, IsFeaturedProduct, DisplayOrder)
  SELECT DISTINCT i.ProductId, i.CategoryId, i.IsFeaturedProduct, i.DisplayOrder
  FROM inserted AS i
  WHERE NOT EXISTS (
      SELECT 1 
      FROM dbo.Product_Category_Mapping AS p
      WHERE i.ProductId = p.ProductId
        AND i.CategoryId = p.CategoryId
  );

  -- 可选:输出重复记录的提示
  IF EXISTS (
      SELECT 1
      FROM inserted AS i
      WHERE EXISTS (
          SELECT 1 
          FROM dbo.Product_Category_Mapping AS p
          WHERE i.ProductId = p.ProductId
            AND i.CategoryId = p.CategoryId
      )
      OR EXISTS (
          SELECT 1
          FROM inserted AS i2
          WHERE i2.ProductId = i.ProductId
            AND i2.CategoryId = i.CategoryId
          GROUP BY i2.ProductId, i2.CategoryId
          HAVING COUNT(*) > 1
      )
  )
  BEGIN
      PRINT 'Duplicate records detected and skipped';
  END
END

内容的提问来源于stack exchange,提问作者anastaciu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 07:50:34