【问题标题】:Using DbContext.SaveChanges() with "After insert trigger"将 DbContext.SaveChanges() 与“插入触发器后”一起使用
【发布时间】:2020-01-12 13:32:36
【问题描述】:

插入触发器后在调用实体框架的dbContext.SaveChanges()时引发异常!!

错误信息:

元数据集合中不存在身份为 '' 的成员

这是我的触发代码:

ALTER trigger [dbo].[AddAccountForCustomer] on [dbo].[Customer] after insert
as
begin

IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND  TABLE_NAME = 'Acc_Accounts'))
BEGIN
    declare @customerName nvarchar(256) = (select NameAr from inserted) 
    declare @customerID bigint = (select customerID from inserted)

    declare @serial  NCHAR(10) = (SELECT '11' + RIGHT('00000000'+ CAST(MAX(CAST(REPLACE(AccountID, '11', '') AS bigint)) + 1 AS VARCHAR(10)), 8)

    FROM [Acc_Accounts] WHERE '11' = LEFT(AccountID,2));
    select @serial

    insert into Acc_Accounts (AccountID, [Name], AccountTypeID, ParentAccountID) values ((case when @serial is null then ('1100000001') else @serial end), 
                                                                                                    @customerName, '1000000000', '1100000000');
    update [Customer] set AccountID = (case when @serial is null then ('1100000001') else @serial end) where CustomerID = @customerID;
END
end

这是在 C# 代码中的插入操作:

private static void Add(CustomerDTO Customer)
        {
            try
            {
                using (Entities context = new Entities())
                {
                    var data = Mapper.Map<CustomerDTO, Customer>(Customer);
                    context.Customers.Add(data);
                    context.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }       

stackoverflow 中有同样问题的解决方案,但是移除了触发器,有没有不移除触发器的解决方案??

【问题讨论】:

  • 那个触发器很乱。为什么要检查数据库中是否存在表?你不知道它是否存在?此外,该触发器假定只插入了一行,因此如果您尝试插入更多,它会爆炸。并且您应该始终在触发器开始时设置 NOCOUNT ON。修复后,错误可能会消失。
  • 我已经使用了 SET NOCOUNT ON 并删除了对表存在的检查,但什么也没有发生......为什么这个跳跳虎“很糟糕”? @DavidBrowne-微软

标签: sql sql-server entity-framework triggers


【解决方案1】:

关于编写(之后)触发器,首先要了解的是,您几乎总是需要将 SET NOCOUNT ON 放在它们的开头。否则,触发器的操作可能会更新、插入或删除行,并将作为行计数的一部分返回,这将导致您的应用程序代码认为有问题。 第二件事是您编写了一个单行触发器。您假设触发器触发时只有一行会受到影响。事实并非如此。您需要假设您正在编写插入和/或删除的触发器包含多行。 第三,你不能再在触发器中选择数据,除非你打开了一个你不应该打开的选项。 第四,不要再使用信息模式视图。使用适当的系统视图,即 sys.tables。

我已经修改了您需要的代码框架。你在连续剧上有点迷失了我。我认为在放置 ?? 的地方需要一个表达式或查找。部分。但这应该足以让您入门。

CREATE OR ALTER TRIGGER TR_dbo_Customer_AddAccountForCustomer
ON dbo.Customer 
AFTER INSERT
AS
BEGIN
    IF EXISTS (SELECT 1 FROM sys.tables AS t 
                        INNER JOIN sys.schemas AS s
                        ON s.schema_id = t.schema_id 
                        WHERE s.[name] = N'dbo'
                        AND t.[name] = N'Acc_Accounts')
    BEGIN
        SET NOCOUNT ON;

        INSERT INTO dbo.Acc_Accounts (AccountID, [Name], AccountTypeID, ParentAccountID)
        SELECT ??, i.NameAr, 1000000000, 1100000000
---    (SELECT '11' + RIGHT('00000000'+ CAST(MAX(CAST(REPLACE(AccountID, '11', '') AS bigint)) + 1 AS VARCHAR(10)), 8)
---    FROM [Acc_Accounts] WHERE '11' = LEFT(AccountID,2));
---    ((case when @serial is null then ('1100000001') else @serial end), 
        FROM inserted AS i;

        UPDATE c 
        SET c.AccountID = CASE WHEN ??? IS NULL 
                               THEN '1100000001'
                               ELSE ??? 
                          END 
        FROM dbo.Customers AS c
        INNER JOIN inserted AS i 
        ON i.customerID = c.CustomerID;
    END;
END;
GO

【讨论】:

  • 谢谢@Greg Low...问题出在:“select @serial”,我是选择变量来测试返回值并忘记在执行触发器之前对其进行注释
  • 很高兴听到。不过不要忽略其他点。特别是,您需要确保它不是像您那样的单行触发器。使用多行 INSERT 对其进行测试。
猜你喜欢
  • 1970-01-01
  • 2020-07-04
  • 2013-02-08
  • 1970-01-01
  • 2016-01-27
  • 1970-01-01
  • 2016-10-13
  • 2021-03-08
  • 1970-01-01
相关资源
最近更新 更多