带有加密触发器的TooManyRowsAffectedException

Jon*_*ers 13 sql-server nhibernate triggers

我正在使用nHibernate更新表中的2列,其中包含3个加密触发器.触发器不归我所有,我不能对它们进行更改,所以不幸的是我无法在它们内部设置NOCOUNT.

有没有其他方法可以解决在提交时抛出的TooManyRowsAffectedException?

更新1

到目前为止,我唯一能解决问题的方法就是绕过.Save例程

var query = session.CreateSQLQuery("update Orders set Notes = :Notes, Status = :Status where OrderId = :Order");
query.SetString("Notes", orderHeader.Notes);
query.SetString("Status", orderHeader.OrderStatus);
query.SetInt32("Order", orderHeader.OrderHeaderId);
query.ExecuteUpdate();
Run Code Online (Sandbox Code Playgroud)

它感觉很脏并且不容易伸展,但它没有陨石坑.

Mik*_*keD 22

我们遇到了与第三方Sybase数据库相同的问题.幸运的是,在深入研究NHibernate代码并与开发人员进行简要讨论后,似乎有一个简单的解决方案,不需要更改NHibernate.解决方案由Fabio Maulo在NHibernate开发人员组的这个主题中提供.

为了实现这一点,我们创建了自己的IBatcherFactory实现,继承自NonBatchingBatcher并覆盖AddToBatch()方法以删除对提供的IExpectation对象的VerifyOutcomeNonBatched()调用:

public class NonVerifyingBatcherFactory : IBatcherFactory
{
    public virtual IBatcher CreateBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
    {
        return new NonBatchingBatcherWithoutVerification(connectionManager, interceptor);
    }
}

public class NonBatchingBatcherWithoutVerification : NonBatchingBatcher
{
    public NonBatchingBatcherWithoutVerification(ConnectionManager connectionManager, IInterceptor interceptor) : base(connectionManager, interceptor)
    {}

    public override void AddToBatch(IExpectation expectation)
    {
        IDbCommand cmd = CurrentCommand;
        ExecuteNonQuery(cmd);
        // Removed the following line
        //expectation.VerifyOutcomeNonBatched(rowCount, cmd);
    }
}
Run Code Online (Sandbox Code Playgroud)

要对SQL Server执行相同的操作,您需要从SqlClientBatchingBatcher继承,重写DoExectuteBatch()并从Expectations对象中删除对VerifyOutcomeBatched()的调用:

public class NonBatchingBatcherWithoutVerification : SqlClientBatchingBatcher
{
    public NonBatchingBatcherWithoutVerification(ConnectionManager connectionManager, IInterceptor interceptor) : base(connectionManager, interceptor)
    {}

    protected override void DoExecuteBatch(IDbCommand ps)
    {
        log.DebugFormat("Executing batch");
        CheckReaders();
        Prepare(currentBatch.BatchCommand);
        if (Factory.Settings.SqlStatementLogger.IsDebugEnabled)
        {
            Factory.Settings.SqlStatementLogger.LogBatchCommand(currentBatchCommandsLog.ToString());
            currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
        }

        int rowsAffected = currentBatch.ExecuteNonQuery();

        // Removed the following line
        //Expectations.VerifyOutcomeBatched(totalExpectedRowsAffected, rowsAffected);

        currentBatch.Dispose();
        totalExpectedRowsAffected = 0;
        currentBatch = new SqlClientSqlCommandSet();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您需要将新类注入NHibernate.我知道有两种方法可以做到这一点:

  1. 在adonet.factory_class配置属性中提供IBatcherFactory实现的名称
  2. 创建一个实现IEmbeddedBatcherFactoryProvider接口的自定义驱动程序

鉴于我们的项目中已经有一个自定义驱动程序来解决Sybase 12 ANSI字符串问题,实现该接口是一个简单的变化,如下所示:

public class DriverWithCustomBatcherFactory : SybaseAdoNet12ClientDriver, IEmbeddedBatcherFactoryProvider
{
    public Type BatcherFactoryClass
    {
        get { return typeof(NonVerifyingBatcherFactory); }
    }

    //...other driver code for our project...
}
Run Code Online (Sandbox Code Playgroud)

可以使用connection.driver_class配置属性提供驱动程序名称来配置驱动程序.我们想使用Fluent NHibernate,可以使用Fluent完成,如下所示:

public class SybaseConfiguration : PersistenceConfiguration<SybaseConfiguration, SybaseConnectionStringBuilder>
{
    SybaseConfiguration()
    {
        Driver<DriverWithCustomBatcherFactory>();
        AdoNetBatchSize(1); // This is required to use our new batcher
    }

    /// <summary>
    /// The dialect to use
    /// </summary>
    public static SybaseConfiguration SybaseDialect
    {
        get
        {
            return new SybaseConfiguration()
                .Dialect<SybaseAdoNet12Dialect>();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在创建会话工厂时,我们使用这个新类,如下所示:

var sf = Fluently.Configure()
    .Database(SybaseConfiguration.SybaseDialect.ConnectionString(_connectionString))
    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<MyEntity>())
    .BuildSessionFactory();
Run Code Online (Sandbox Code Playgroud)

最后,您需要将adonet.batch_size属性设置为1以确保使用新的batcher类.在Fluent NHibernate中,这是在继承自PersistenceConfiguration的类中使用AdoNetBatchSize()方法完成的(有关此示例,请参阅上面的SybaseConfiguration类构造函数).

  • @MikeD谢谢,这对我很有帮助.使用SQL Server我必须复制整个`SqlClientBatchingBatcher`,因为从`DoExecuteBatch()`访问的一些字段是私有的.除此之外它工作得很好(显然使用SQL Server的正确类型) (2认同)

gbn*_*gbn 1

呃...你也许能够解密它们...

编辑:如果您无法更改代码、解密或禁用,则SQL Server 端没有代码选项。

但是,您可以尝试“禁止触发器选项的结果”,这对于 SQL 2005 和 SQL 2008 是可以的,但将在更高版本中删除。我不知道它是否会抑制行计数消息。