如何告诉NHibernate触发器更新另一个表?

时间:2011-07-05 13:41:22

标签: c# nhibernate exception triggers

我在使用NHibernate时遇到了TooManyRowsAffectedException,我通过注入像TooManyRowsAffectedException with encrypted triggers这样的不同批处理器,或者通过修改数据库中的触发器来使用SET NOCOUNT ON(我可以'使用这个,因为我不想修改数据库 - 它非常复杂,有超过一百个表都相关在一起,我不想乱用它,因为其他应用程序使用它)。我不明白的是为什么会发生这种异常。我所做的就是我有一个Sample对象,它有几个值我检查,如果值符合给定的条件,我将Sample.IsDone行设置为'Y'(在我们的数据库中,所有布尔值都由一个char Y或N)。代码非常简单:

IQueryable<Sample> samples = session.Query<Sample>().Where(s =­­> s.Value == desiredValue);
foreach (Sample sample in samples)
{
  sample.IsDone = 'Y';
  session.Flush(); // Throws TooManyRowsAffectedException
}
session.Flush(); // Throws TooManyRowsAffectedException

Flush调用抛出我是把它放在循环内还是外面。有什么我做错了或者它只与数据库的制作方式有关吗?我尝试在Flush()之前调用样本上的SaveOrUpdate(),但它没有改变任何东西。我知道我可以解决这个例外,但我更愿意了解问题的根源。

注意:在异常中,它告诉我实际的行数是2,预期的是1.为什么它更新2行,因为我只更改了1行?

感谢大家的帮助!

编辑:

我能够发现原因是因为在更新样本时,数据库中有一个触发器更新Container表中的行(容器包含样本)。有没有办法配置NHibernate,以便它知道这个触发器并期望更新正确的行数?

3 个答案:

答案 0 :(得分:5)

我发现的唯一解决方法是使用Fluent NHibernate在下面的代码片段中显示。它很难看,因为它很难将SQL编码到您的映射中,但它确实有效。 Check属性设置为None,以便忽略计数。我不知道你是否可以使用直接的HBM文件来完成这个,但可能有一种方法。 NHibernate(或Fluent NH)中有一个配置选项可以设置预期的更新行数,也可以在需要时忽略它。

public class OrderMap : ClassMap<Order>
{
    public OrderMap()
    {
        Id(c => c.Id, "order_id").GeneratedBy.Native();

        Table("order");

        Map(c => c.GroupId, "group_id");
        Map(c => c.Status, "status");
        Map(c => c.LocationNumber, "location");

        SqlInsert("insert into order (group_id, status, location) values (?, ?, ?)").Check.None();
        SqlUpdate("update order set group_id = ?, status = ?, location = ? where order_id = ?")).Check.None();
        SqlDelete("delete order where order_id = ?").Check.None();
    }
}

编辑: 对于那些不了解Fluent NHibernate或者喜欢手工生成痛苦的HBM文件的不幸的人,这里是这个样本映射的HBM文件:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="Your.DomainModel.Entities.Order, Your.DomainModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="order">
    <id name="Id" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" unsaved-value="0">
      <column name="order_id" />
      <generator class="identity" />
    </id>
    <property name="GroupId" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="group_id" />
    </property>
    <property name="Status" type="System.Int16, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="status" />
    </property>
    <property name="LocationNumber" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="loc_num" />
    </property>
    <sql-insert check="none">insert into order (group_id, status, location) values (?, ?, ?)</sql-insert>
    <sql-update check="none">update order set group_id = ?, status = ?, location = ? where order_id = ?</sql-update>
    <sql-delete check="none">delete order where order_id = ?</sql-delete>
  </class>
</hibernate-mapping>

答案 1 :(得分:2)

使用分析器检查执行的sql。 anjlab sql profiler总是对我有用。

在那之后检查你是否在那张桌子上有触发器 - 也许它们会造成一些麻烦。

修改

你应该改变你的触发器:http://www.codewrecks.com/blog/index.php/2009/03/25/nhibernate-and-toomanyrowsaffectedexception/

答案 2 :(得分:1)

没有什么可以告诉NHibernate触发器更新了数据库中的其他实体,除了像Sixto Saez在答案中所显示的那样忽略它。但是,您可以使用EventListeners在代码中执行触发器操作。或者,如果更新对您的其余交易无关紧要,请在触发开始时设置SET NOCOUNT,并在结束时设置SET NOCOUNT OFF。