Sql server PetaPoco插入在具有触发器的表上失败

Sql server PetaPoco插入在具有触发器的表上失败,sql-server,petapoco,Sql Server,Petapoco,我们使用PetaPoco作为SQL 2008数据库的数据访问工具。我们在尝试插入/更新附加了触发器的表上的行时遇到问题 我们正在使用PetaPoco的db.Save(对象) 显示的错误是: 如果DML语句的目标表“The_table_with_a_trigger”包含OUTPUT子句而不包含INTO子句,则该语句不能具有任何已启用的触发器 如何使用PetaPoco在具有触发器的表上插入/更新数据?PetaPoco仅在Oracle DBs中创建一个输出参数以获取新的自动增量ID 对于Sql Ser

我们使用PetaPoco作为SQL 2008数据库的数据访问工具。我们在尝试插入/更新附加了触发器的表上的行时遇到问题

我们正在使用PetaPoco的db.Save(对象)

显示的错误是: 如果DML语句的目标表“The_table_with_a_trigger”包含OUTPUT子句而不包含INTO子句,则该语句不能具有任何已启用的触发器


如何使用PetaPoco在具有触发器的表上插入/更新数据?

PetaPoco仅在Oracle DBs中创建一个输出参数以获取新的自动增量ID

对于Sql Server,当您标记问题时,如果表具有自动增量ID,它只会添加一个
SELECT SCOPE\u IDENTITY()作为NewID

PetaPoco.cs中的相关代码:

cmd.CommandText = string.Format("INSERT INTO {0} ({1}) VALUES ({2})",
        EscapeTableName(tableName),
        string.Join(",", names.ToArray()),
        string.Join(",", values.ToArray())
        );

if (!autoIncrement) {
    DoPreExecute(cmd);
    cmd.ExecuteNonQuery();
    OnExecutedCommand(cmd);
    return true;
}

object id;
switch (_dbType) {
    case DBType.SqlServer:
        cmd.CommandText += ";\nSELECT SCOPE_IDENTITY() AS NewID;";
        DoPreExecute(cmd);
        id = cmd.ExecuteScalar();
        OnExecutedCommand(cmd);
        break;

尝试关闭自动增量ID并手动设置以查看问题是否消失

多亏了@Eduardo Molteni,您让我走上了解决此问题的正确道路。显然,SQL Server 2008 R2中的一个已知问题是,如果表具有触发器,则insert中的输出命令将失败。但是,如果表的AutoIncrement=true,PetaPoco会自动将OUTPUT子句插入任何insert的命令文本中

我的解决方案(针对SQL Server 2008 R2)如下所示:

1) 转到PetaPoco.DatabaseTypes.SqlServerDatabaseType.GetInsertOutputClause函数

删除(注释掉)
\\return String.Format(“插入的输出。[{0}]”,primaryKeyName)

这将从SQL insert语句中删除“输出”。现在,插入将发生在带有触发器的表上。但是,现在PetaPoco无法从新插入的行中检索新的主键(标识)

2) 转到PetaPoco.Database.Insert函数。线的正上方:

object id = _dbType.ExecuteInsert(this, cmd, primaryKeyName);
添加新行,使其看起来像这样:

 cmd.CommandText += ";\nSELECT SCOPE_IDENTITY() AS NewID;";
 object id = _dbType.ExecuteInsert(this, cmd, primaryKeyName);

新行(存在于PetaPoco中,但未被使用)将允许insert语句检索标识。

我认为
cmd.CommandText+=”;\n选择SCOPE_identity()作为NewID;“会更好@@IDENTITY可以给你一个在触发器中生成的id,而不是你的语句。

因为我肯定我不会是最后一个遇到这个问题的人

我在新项目中采用了PetaPoco,但我遇到了类似的问题,但恢复到scope_identity()是行不通的。因此,我:

1) 扩展了IProvider接口

    /// <summary>
    ///     Return an SQL expression that can be used with <seealso cref="GetInsertPostScript(string)"/>
    ///     and <seealso cref="GetInsertOutputClause(string)"/> to return a provider-generated value from an INSERT; typically an IDENTITY 
    ///     column in Microsoft SQL Server.
    /// </summary>
    /// <param name="primaryKeyName"></param>
    /// <returns></returns>
    string GetInsertPreamble(string primaryKeyName);

    /// <summary>
    ///     Return an SQL expression that can be used with <seealso cref="GetInsertPreamble(string)"/>
    ///     and <seealso cref="GetInsertOutputClause(string)"/> to return a provider-generated value from an INSERT; typically an IDENTITY 
    ///     column in Microsoft SQL Server.
    /// </summary>
    /// <param name="primaryKeyName"></param>
    /// <returns></returns>
    string GetInsertPostScript(string primaryKeyName);
3) 然后是SqlServerDatabaseProvider,包括更改现有的OUTPUT子句:

    public override string GetInsertOutputClause(string primaryKeyName)
    {
        return String.Format(" OUTPUT INSERTED.[{0}] into @result({0})", primaryKeyName);
    }

    public override string GetInsertPreamble(string primaryKeyName)
    {
        return string.Format("DECLARE @result TABLE({0} sql_variant); ", primaryKeyName);
    }

    public override string GetInsertPostScript(string primaryKeyName)
    {
        return string.Format("; SELECT {0} FROM @result; ", primaryKeyName);
    }
4) 最后,将以下内容合并到Database.cs中:

                    ...string outputClause = string.Empty;
                    string insertPreamble = string.Empty;
                    string insertPostScript = string.Empty;
                    if (autoIncrement)
                    {
                        insertPreamble = _provider.GetInsertPreamble(primaryKeyName, tableName);
                        outputClause = _provider.GetInsertOutputClause(primaryKeyName);
                        insertPostScript = _provider.GetInsertPostScript(primaryKeyName, tableName);
                    }

                    cmd.CommandText = string.Concat(
                        $"{insertPreamble}",
                        $"INSERT INTO {_provider.EscapeTableName(tableName)} ({(string.Join(",", names.ToArray()))})",
                        $"{outputClause}",
                        $" VALUES ({(string.Join(",", values.ToArray()))})",
                        $"{insertPostScript}"
                    ) ;

                    if (!autoIncrement)
                    {....

这将命令更改为使用命名表变量,在“序言”中声明,由输出子句填充并在“PostScript”中选择。

您认为需要使用该DB和/或您的代码来帮助您吗?我不这么认为。在PetaPoco内部,对于带有触发器的表来说,这似乎是一个问题。我猜PetaPoco创建插入命令的方式与触发器冲突。我希望有PetaPoco经验的人能够解释修复或解决方法。
                    ...string outputClause = string.Empty;
                    string insertPreamble = string.Empty;
                    string insertPostScript = string.Empty;
                    if (autoIncrement)
                    {
                        insertPreamble = _provider.GetInsertPreamble(primaryKeyName, tableName);
                        outputClause = _provider.GetInsertOutputClause(primaryKeyName);
                        insertPostScript = _provider.GetInsertPostScript(primaryKeyName, tableName);
                    }

                    cmd.CommandText = string.Concat(
                        $"{insertPreamble}",
                        $"INSERT INTO {_provider.EscapeTableName(tableName)} ({(string.Join(",", names.ToArray()))})",
                        $"{outputClause}",
                        $" VALUES ({(string.Join(",", values.ToArray()))})",
                        $"{insertPostScript}"
                    ) ;

                    if (!autoIncrement)
                    {....