我有这个插入触发器,可以处理重复的键插入尝试:
-- Create Tigger 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 dbo.Product_Category_Mapping(ProductId, CategoryId, IsFeaturedProduct, DisplayOrder)
SELECT ProductId, CategoryId, IsFeaturedProduct, DisplayOrder FROM inserted;
END
ELSE
BEGIN
PRINT 'Duplicate';
END
END
如果我直接从 DBMS 运行它,它就可以正常工作:
但是,如果我在带有 EF 的代码中使用它:
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.15" />
它似乎不起作用,抛出了重复的键异常,这正是我试图避免的:
Microsoft.EntityFrameworkCore.Relational:更新条目时发生错误。有关详细信息,请参阅内部异常。核心 Microsoft SqlClient 数据提供程序:违反 UNIQUE KEY 约束“UQ_PCM_ProductId_CategoryId”。无法在对象“dbo.Product_Category_Mapping”中插入重复的键。重复的键值为 (920, 129)。
这是通过导航属性插入的(如果有任何区别的话)。
有人可以向我指出为什么这不起作用吗?
我遵循了这两个相关问题,但我没有看到任何可以帮助的内容:
我要再次敲鼓:这个想法是一个坏的想法。给应用程序/用户留下事情“有效”的印象,而实际上却没有,这不是一个好主意。在这种情况下,你说这“不是问题”,但我可以自信地说,在某些时候,用户会问“为什么我刚才添加的产品在我要求的时候没有出现”。答案将是“您的
INSERT
被忽略了,但我们为您掩盖了该错误并且没有告诉您。”
话虽如此,如果您“必须”(不)在触发器中执行此操作,那么您的处理方式就是错误的。如果您要插入多行,这将会出现各种错误,就好像其中 1 行是重复的,您会忽略所有行。插入 100 个新产品,其中 1 个是骗局?您插入零。
相反,使用
INSERT
和 NOT EXISTS
并将其余部分“分类”。不理想,您的用户会感到困惑,但这似乎是您所追求的行为,至少:
CREATE TRIGGER dbo.BlockDuplicates_Product_Category_Mapping
ON dbo.Product_Category_Mapping
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.Product_Category_Mapping (ProductId,
CategoryId,
IsFeaturedProduct,
DisplayOrder)
SELECT i.ProductId,
i.CategoryId,
i.IsFeaturedProduct,
i.DisplayOrder
FROM inserted i
WHERE NOT EXISTS (SELECT 1
FROM dbo.Product_Category_Mapping PCM
WHERE PCM.ProductId = i.ProductId
AND PCM.CategoryId = i.CategoryId);
END;
如果您真的想善待您的用户(以避免他们在未插入内容时感到困惑),您可以进行更新插入。然后,您
UPDATE
已存在的行,到他们想要的值 INSERT
,以及 INSERT
新行。
CREATE TRIGGER dbo.BlockDuplicates_Product_Category_Mapping
ON dbo.Product_Category_Mapping
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
--No need to define a transaction, as a trigger is already inside one and automatically has XACT_ABORT enabled
UPDATE PCM
SET IsFeaturedProduct = i.IsFeaturedProduct,
DisplayOrder = i.DisplayOrder
FROM dbo.Product_Category_Mapping PCM WITH (UPDLOCK, SERIALIZABLE)
JOIN inserted i ON PCM.ProductId = i.ProductId
AND PCM.CategoryId = i.CategoryId;
INSERT INTO dbo.Product_Category_Mapping (ProductId,
CategoryId,
IsFeaturedProduct,
DisplayOrder)
SELECT i.ProductId,
i.CategoryId,
i.IsFeaturedProduct,
i.DisplayOrder
FROM inserted i
WHERE NOT EXISTS (SELECT 1
FROM dbo.Product_Category_Mapping PCM
WHERE PCM.ProductId = i.ProductId
AND PCM.CategoryId = i.CategoryId);
END;