我有两个具有一对多关系的主从表。我想让触发器在删除发生后触发,无论是作为级联操作(通过其 PK 删除主文件)还是通过特定操作(通过其 PK 删除详细信息)。
这是我的触发器:
CREATE TRIGGER [dbo].[tr_update_stocks]
ON [dbo].[WarehouseComponentLoadRows]
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON
IF ((SELECT TRIGGER_NESTLEVEL()) > 1)
RETURN;
DECLARE @Id BIGINT
DECLARE @WarehouseId INT
DECLARE @ComponentId INT
DECLARE @ColorId INT
DECLARE @Quantity DECIMAL (18,6)
DECLARE deleted_cursor CURSOR FAST_FORWARD FOR
SELECT
Id, WarehouseId,
ComponentId, ColorId,
Quantity
FROM
DELETED
OPEN deleted_cursor
FETCH NEXT FROM deleted_cursor into @Id, @WarehouseId, @ComponentId, @ColorId, @Quantity
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE [dbo].[WarehouseComponents]
SET [dbo].[WarehouseComponents].[StockQuantity] = [dbo].[WarehouseComponents].[StockQuantity] - @Quantity,
[dbo].[WarehouseComponents].[AvailableQuantity] = [dbo].[WarehouseComponents].[AvailableQuantity] - @Quantity
FROM
[dbo].[WarehouseComponents]
WHERE
[dbo].[WarehouseComponents].[WarehouseId] = @WarehouseId
AND [dbo].[WarehouseComponents].[ComponentId] = @ComponentId
AND [dbo].[WarehouseComponents].[ColorId] = @ColorId
FETCH NEXT FROM deleted_cursor INTO @Id, @WarehouseId, @ComponentId, @ColorId, @Quantity
END
CLOSE deleted_cursor
DEALLOCATE deleted_cursor
DECLARE inserted_cursor CURSOR FAST_FORWARD FOR
SELECT Id
, WarehouseId
, ComponentId
, ColorId
, Quantity
FROM INSERTED
OPEN inserted_cursor
FETCH NEXT FROM inserted_cursor into @Id, @WarehouseId, @ComponentId, @ColorId, @Quantity
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE [dbo].[WarehouseComponents]
SET
[dbo].[WarehouseComponents].[StockQuantity] = [dbo].[WarehouseComponents].[StockQuantity] + @Quantity,
[dbo].[WarehouseComponents].[AvailableQuantity] = [dbo].[WarehouseComponents].[AvailableQuantity] + @Quantity
FROM
[dbo].[WarehouseComponents]
WHERE [dbo].[WarehouseComponents].[WarehouseId] = @WarehouseId AND [dbo].[WarehouseComponents].[ComponentId] = @ComponentId AND [dbo].[WarehouseComponents].[ColorId] = @ColorId
FETCH NEXT FROM inserted_cursor into @Id, @WarehouseId, @ComponentId, @ColorId, @Quantity
END
CLOSE inserted_cursor
DEALLOCATE inserted_cursor
END
GO
ALTER TABLE [dbo].[WarehouseComponentLoadRows]
ENABLE TRIGGER [tr_update_stocks]
GO
在这里你可以找到我的主表:
CREATE TABLE [dbo].[WarehouseComponentLoads]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[SupplierId] [int] NOT NULL,
[Date] [datetime2](7) NOT NULL,
[CreatedAt] [datetime2](7) NULL,
[CreatedBy] [nvarchar](255) NULL,
[UpdatedAt] [datetime2](7) NULL,
[UpdatedBy] [nvarchar](255) NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[WarehouseComponentLoads]
ADD CONSTRAINT [PK_WarehouseComponentLoads]
PRIMARY KEY CLUSTERED ([Id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF,
ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_WarehouseComponentLoads_SupplierId]
ON [dbo].[WarehouseComponentLoads] ([SupplierId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE [dbo].[WarehouseComponentLoads] WITH CHECK
ADD CONSTRAINT [FK_WarehouseComponentLoads_Subjects_SupplierId]
FOREIGN KEY([SupplierId])
REFERENCES [dbo].[Subjects] ([Id])
GO
ALTER TABLE [dbo].[WarehouseComponentLoads] CHECK CONSTRAINT [FK_WarehouseComponentLoads_Subjects_SupplierId]
GO
这是我的详细信息表:
CREATE TABLE [dbo].[WarehouseComponentLoadRows]
(
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[WarehouseComponentLoadId] [int] NOT NULL,
[WarehouseId] [int] NOT NULL,
[ComponentId] [int] NOT NULL,
[ColorId] [int] NOT NULL,
[Quantity] [decimal](18, 6) NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[WarehouseComponentLoadRows]
ADD CONSTRAINT [PK_WarehouseComponentLoadRows]
PRIMARY KEY CLUSTERED ([Id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF,
ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_WarehouseComponentLoadRows_ColorId]
ON [dbo].[WarehouseComponentLoadRows] ([ColorId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_WarehouseComponentLoadRows_ComponentId]
ON [dbo].[WarehouseComponentLoadRows] ([ComponentId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_WarehouseComponentLoadRows_WarehouseComponentLoadId]
ON [dbo].[WarehouseComponentLoadRows] ([WarehouseComponentLoadId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_WarehouseComponentLoadRows_WarehouseId]
ON [dbo].[WarehouseComponentLoadRows] ([WarehouseId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE [dbo].[WarehouseComponentLoadRows] WITH CHECK
ADD CONSTRAINT [FK_WarehouseComponentLoadRows_Colors_ColorId]
FOREIGN KEY([ColorId])
REFERENCES [dbo].[Colors] ([Id])
GO
ALTER TABLE [dbo].[WarehouseComponentLoadRows] CHECK CONSTRAINT [FK_WarehouseComponentLoadRows_Colors_ColorId]
GO
ALTER TABLE [dbo].[WarehouseComponentLoadRows] WITH CHECK
ADD CONSTRAINT [FK_WarehouseComponentLoadRows_Components_ComponentId]
FOREIGN KEY([ComponentId])
REFERENCES [dbo].[Components] ([Id])
GO
ALTER TABLE [dbo].[WarehouseComponentLoadRows] CHECK CONSTRAINT [FK_WarehouseComponentLoadRows_Components_ComponentId]
GO
ALTER TABLE [dbo].[WarehouseComponentLoadRows] WITH CHECK
ADD CONSTRAINT [FK_WarehouseComponentLoadRows_WarehouseComponentLoads_WarehouseComponentLoadId]
FOREIGN KEY([WarehouseComponentLoadId])
REFERENCES [dbo].[WarehouseComponentLoads] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[WarehouseComponentLoadRows] CHECK CONSTRAINT [FK_WarehouseComponentLoadRows_WarehouseComponentLoads_WarehouseComponentLoadId]
GO
ALTER TABLE [dbo].[WarehouseComponentLoadRows] WITH CHECK
ADD CONSTRAINT [FK_WarehouseComponentLoadRows_Warehouses_WarehouseId]
FOREIGN KEY([WarehouseId])
REFERENCES [dbo].[Warehouses] ([Id])
GO
ALTER TABLE [dbo].[WarehouseComponentLoadRows] CHECK CONSTRAINT [FK_WarehouseComponentLoadRows_Warehouses_WarehouseId]
GO
在这里您可以找到要更新的丝袜表:
CREATE TABLE [dbo].[WarehouseComponents]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[WarehouseId] [int] NOT NULL,
[ComponentId] [int] NOT NULL,
[ColorId] [int] NOT NULL,
[StockQuantity] [decimal](18, 6) NOT NULL,
[AvailableQuantity] [decimal](18, 6) NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[WarehouseComponents]
ADD CONSTRAINT [PK_WarehouseComponents]
PRIMARY KEY CLUSTERED ([Id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF,
ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_WarehouseComponents_ColorId]
ON [dbo].[WarehouseComponents] ([ColorId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_WarehouseComponents_ComponentId]
ON [dbo].[WarehouseComponents] ([ComponentId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_WarehouseComponents_WarehouseId]
ON [dbo].[WarehouseComponents] ([WarehouseId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE [dbo].[WarehouseComponents] WITH CHECK
ADD CONSTRAINT [FK_WarehouseComponents_Colors_ColorId]
FOREIGN KEY([ColorId])
REFERENCES [dbo].[Colors] ([Id])
GO
ALTER TABLE [dbo].[WarehouseComponents] CHECK CONSTRAINT [FK_WarehouseComponents_Colors_ColorId]
GO
ALTER TABLE [dbo].[WarehouseComponents] WITH CHECK
ADD CONSTRAINT [FK_WarehouseComponents_Components_ComponentId]
FOREIGN KEY([ComponentId])
REFERENCES [dbo].[Components] ([Id])
GO
ALTER TABLE [dbo].[WarehouseComponents] CHECK CONSTRAINT [FK_WarehouseComponents_Components_ComponentId]
GO
ALTER TABLE [dbo].[WarehouseComponents] WITH CHECK
ADD CONSTRAINT [FK_WarehouseComponents_Warehouses_WarehouseId]
FOREIGN KEY([WarehouseId])
REFERENCES [dbo].[Warehouses] ([Id])
GO
ALTER TABLE [dbo].[WarehouseComponents] CHECK CONSTRAINT [FK_WarehouseComponents_Warehouses_WarehouseId]
GO
基本上我遇到了这个问题:每当我使用 EF 7 插入或更新整个主从实体时,一切都正常。如果我在详细信息表中插入、更新或删除一行,一切都会顺利。但是,如果我从上下文中通过主实体的 PK 删除主实体并保存更改,则就像触发器不会触发,虽然主从记录已正确删除,但长袜不会更新。这仅发生在 EF 上下文中。如果我尝试通过一个简单的查询从 SQL Server 中按 PK 删除主行,级联行为将被正确应用,并且触发器将按预期触发,从而调整袜子。
请考虑我的上下文的流畅 API 包含特定配置,以使 EF 知道我正在使用触发器,如下所示:
modelBuilder.Entity<WarehouseComponentLoadRow>()
.ToTable(tb => tb.HasTrigger("tr_update_stocks"));
同样有趣的是,如果我尝试“手动”删除子行,然后删除同一事务范围内的父行(即,在调用
SaveChangesAsync
之前),详细信息行将与主行一起删除,甚至袜子也会被删除。已按预期更新。
问题仅出现在从上下文中级联删除时。我有什么遗漏的吗?
EF Core 无法绕过您的触发器。触发逻辑肯定有bug。
你的触发器可能正确,也可能不正确,但它使用了大量的坏东西:
TRIGGER_NESTLEVEL
。(WarehouseId, ComponentId, ColorId)
是两个表中的主键或唯一键,否则您最终可能会更新多行。你的触发器应该是这样的:
CREATE OR ALTER TRIGGER dbo.tr_update_stocks
ON dbo.WarehouseComponentLoadRows
AFTER INSERT, UPDATE, DELETE
AS
IF @@ROWCOUNT = 0 OR TRIGGER_NESTLEVEL(OBJECT_ID('dbo.tr_update_stocks')) > 1
RETURN;
SET NOCOUNT ON;
UPDATE wc
SET
StockQuantity += i.DiffQuantity,
AvailableQuantity += i.DiffQuantity
FROM
dbo.WarehouseComponents wc
JOIN (
SELECT
i.WarehouseId,
i.ComponentId,
i.ColorId,
SUM(i.Quantity) AS DiffQuantity
FROM (
SELECT
i.WarehouseId,
i.ComponentId,
i.ColorId,
i.Quantity
FROM inserted i
UNION ALL
SELECT
d.WarehouseId,
d.ComponentId,
d.ColorId,
-d.Quantity
FROM deleted d
) i
GROUP BY
i.WarehouseId,
i.ComponentId,
i.ColorId
) i ON d.WarehouseId = wc.WarehouseId
AND d.ComponentId = wc.ComponentId
AND d.ColorId = wc.ColorId;