Как сообщить NHibernate, что триггер обновляет другую таблицу? - PullRequest
8 голосов
/ 05 июля 2011

Я только что получил TooManyRowsActedException при работе с NHibernate, и я нашел обходные пути для этого, введя другой дозатор, как здесь TooManyRowsActedException с зашифрованными триггерами , или изменив триггеры в базе данных для использования SET NOCOUNTON (я не могу использовать эту, так как я не хочу изменять базу данных - она ​​очень сложная, с более чем сотней таблиц, связанных друг с другом, и я не хочу связываться с ней, так как другие приложения используют ее).Я не понимаю, почему это исключение происходит.Все, что я делаю, - это то, что у меня есть объект Sample, у которого есть пара значений, которые я проверяю, и, если значения соответствуют заданным критериям, я устанавливаю строку Sample.IsDone в «Y» (в нашей базе данных все логические значения представленысимвол 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 вызывает, помещаю ли я его в цикл или снаружи.Есть ли что-то, что я делаю неправильно или это связано только с тем, как создается база данных?Я попытался вызвать SaveOrUpdate () в образце до Flush (), но это ничего не изменило.Я знаю, что могу обойти это исключение, но я бы предпочел понять источник проблемы.

Примечание. В исключении указано, что фактическое число строк равно 2, а ожидаемое равно 1. Почемуобновить 2 строки, так как я изменяю только 1 строку?

Спасибо всем за помощь!

РЕДАКТИРОВАТЬ:

Я смог выяснить, что причина в том, что естьявляется триггером в базе данных, обновляющим строку в таблице контейнеров (контейнеры содержат образцы) при обновлении образца.Есть ли способ настроить NHibernate так, чтобы он знал об этом триггере и ожидал, что нужное количество строк будет обновлено?

Ответы [ 3 ]

5 голосов
/ 06 июля 2011

Единственная работа, которую я нашел, показана во фрагменте кода ниже с использованием 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>
2 голосов
/ 05 июля 2011

Проверьте с помощью профилировщика SQL, который выполняется. anjlab sql profiler всегда помогал мне.

После этого проверьте, есть ли у вас триггеры на этой таблице - возможно, они вызывают некоторые проблемы.

EDIT

Вы должны изменить свой триггер, как здесь: http://www.codewrecks.com/blog/index.php/2009/03/25/nhibernate-and-toomanyrowsaffectedexception/

1 голос
/ 07 июля 2011

Вы ничего не можете сделать, чтобы сообщить NHibernate, что триггер обновил другую сущность в базе данных, кроме как игнорировать ее, как показывает Сиксто Саез в своем ответе. Однако вы можете использовать EventListeners для выполнения действия триггера в коде. Или установите SET NOCOUNT ON в начале триггера и SET NOCOUNT OFF в конце, если обновление не имеет значения для остальной части вашей транзакции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...