【问题标题】:TooManyRowsAffectedException with encrypted triggers带有加密触发器的 TooManyRowsAffectedException
【发布时间】:2010-11-24 04:07:34
【问题描述】:

我正在使用 nHibernate 更新具有 3 个加密触发器的表中的 2 列。触发器不归我所有,我无法对其进行更改,所以很遗憾我无法在其中设置 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();

感觉很脏,不易伸展,但不会坑坑洼洼。

【问题讨论】:

    标签: sql-server nhibernate triggers


    【解决方案1】:

    呃...你可能是able to decrypt them...

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

    但是,您可以尝试“disallow results from triggers Option”,这对于 SQL 2005 和 SQL 2008 来说是可以的,但在以后的版本中将被删除。我不知道它是否会抑制行计数消息。

    【讨论】:

    • 不幸的是,破解加密不是一个可行的解决方案。
    【解决方案2】:

    我们在使用第 3 方 Sybase 数据库时遇到了同样的问题。幸运的是,在深入研究了 NHibernate 代码并与开发人员进行了简短讨论之后,似乎有一个不需要更改 NHibernate 的简单解决方案。 Fabio Maulo 在this thread in the NHibernate developer group 中给出了解决方案。

    为了为 Sybase 实现这一点,我们创建了自己的 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);
        }
    }
    

    要对 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();
        }
    }
    

    现在您需要将新类注入 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...
    }
    

    可以通过使用 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>();
            }
        }
    }
    

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

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

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

    【讨论】:

    • 我遇到了同样的问题(使用 Sql Server)。然而,在本例中,我们没有使用批处理,因此从 VerifyOutcomeNonBatched 方法抛出了 TooManyRowsAffectedException,该方法由 AbstractEntityPersister.UpdateOrInsert 调用(通过 Update 和 Check 方法)。您是否必须在解决方案中涵盖这种情况?
    • @andy 我不知道允许覆盖非批量检查的扩展点,但我不是 NHibernate 代码库方面的专家。当我在 NH 邮件列表中询问我最初的问题时,Fabio 概述了使用批处理工厂的解决方案,因此我转而使用批处理。进行此更改后,我没有遇到任何问题。
    • 谢谢@MikeD。切换到批处理以便我可以使用此解决方案可能是我的一个选择。除了在配置中添加“adonet.batch_size”会话工厂属性之外,启用批处理是否涉及任何其他内容?
    • @MatthewTalbert 我知道 NHibernate 在最新版本中改变了很多。请随时编辑。我希望这个答案能够继续帮助人们。
    • @MikeD 谢谢,这对我很有帮助。使用 SQL Server,我必须复制整个 SqlClientBatchingBatcher,因为从 DoExecuteBatch() 访问的一些字段是私有的。除此之外,它运行良好(显然为 SQL Server 使用了正确的类型)
    【解决方案3】:

    将“Disallow Results from Triggers”选项设置为 1 对我们有用(默认值为 0)。

    请注意,此选项在 Microsoft SQL Server 的未来版本中将不可用,但在它不再可用后,它的行为就像设置为 1 一样。因此,现在将其设置为 1 可以解决问题并给出您的行为与未来版本中的行为相同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-14
      • 1970-01-01
      • 2018-03-05
      • 2020-12-02
      • 2021-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多