2011-07-05 37 views
8

Acabo de recibir una excepción TooManyRowsAffectedException mientras trabajaba con NHibernate y he visto soluciones para eso inyectando un dosificador diferente como aquí TooManyRowsAffectedException with encrypted triggers, o modificando los desencadenantes en la base de datos para usar SET NOCOUNT ON (No puedo usar este desde No quiero modificar la base de datos, es muy compleja con más de cien tablas relacionadas entre sí y no quiero meterme con ella, ya que otras aplicaciones la usan). Lo que no entiendo es por qué ocurre esta excepción. Todo lo que hago es que tengo un objeto Sample que tiene un par de valores que verifico, y si los valores se ajustan a un criterio dado, configuro la fila Sample.IsDone en 'Y' (en nuestra base de datos todos los booleanos están representados por un char Y o N). El código es muy simple:¿Cómo decirle a NHibernate que un disparador actualiza otra tabla?

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 

La llamada de Flush arroja si la coloco dentro del lazo o afuera. ¿Hay algo que estoy haciendo mal o solo está relacionado con la forma en que se hace la base de datos? Intenté llamar a SaveOrUpdate() en la muestra antes de Flush(), pero no cambió nada. Sé que puedo evitar esta excepción, pero preferiría entender la fuente del problema.

Nota: En la excepción, me dice que el recuento real de filas es 2 y el esperado es 1. ¿Por qué actualiza 2 filas ya que solo cambio 1 fila?

¡Gracias a todos por su ayuda!

EDIT:

pude descubrir que la causa de ello es porque hay un gatillo en la base de datos de actualización de una fila en la tabla de contenedores (recipientes contienen muestras) cuando se actualiza la muestra. ¿Hay alguna forma de configurar NHibernate para que sepa de este activador y espera que se actualice la cantidad correcta de filas?

Respuesta

5

El único trabajo en torno a que he encontrado se muestra en el siguiente fragmento de código usando Fluido NHibernate. Es feo, ya que es difícil codificar SQL en su mapeo, pero funciona. La propiedad Check se establece en None para que los recuentos se ignoren. No sé si puede lograr esto usando archivos HBM rectos, pero probablemente haya una manera. Sería genial si hubiera una opción de configuración en NHibernate (o NH Fluido) para establecer el conteo de filas actualizado esperado o ignorarlo cuando sea necesario.

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

EDIT: Para aquellas personas desafortunadas que no son conscientes de Fluido NHibernate o Love Generation dolorosa de archivos HBM a mano, aquí es el archivo de HBM para esta correspondencia de la muestra:

<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> 
+0

No estoy usando Fluent NHibernate, ¿sabe dónde se aplica esto en la configuración de NHibernate? No puedo entender dónde debo escribir eso (no uso ClassMap, ni sé qué es, y no he visto funciones como SqlInsert o similares). – Carl

+0

Consulte la edición de mi respuesta. Con suerte, no estás creando los HBM a mano. Pero si lo es, definitivamente mire lo que Fluent NHibernate o la nueva capacidad de mapeo fluido en NHibernate 3.0 puede hacer por usted. –

+1

Gracias por eso. Sí, estoy creando los HBM a mano, pero no es un problema por ahora (no creo que esto sea particularmente difícil). – Carl

2

Compruebe con un generador de perfiles el sql que se ejecuta. anjlab sql profiler siempre fue el truco para mí.

Después de eso, compruebe si tiene factores desencadenantes en la tabla; es posible que estén causando algunos problemas.

EDITAR

Usted debe alterar su disparador como aquí: http://www.codewrecks.com/blog/index.php/2009/03/25/nhibernate-and-toomanyrowsaffectedexception/

+0

Gracias por el enlace.Tenías razón. Vi que hay un activador que actualiza la última columna de fecha de actualización de Container (que contiene muestras) cuando se actualiza una muestra. ¿Sabes si hay una manera de decirle a NHibernate que espere eso? – Carl

+0

Creo que eso es lo que va a suceder, ya que no he encontrado otra forma de hacerlo funcionar, pero dado que hay más de 100 clientes usando la base de datos con más de 10 aplicaciones diferentes, no sé si la modificación del gatillo puede hacer que algo deje de funcionar en otra aplicación. – Carl

1

Hay no se puede hacer nada para decirle a NHibernate que un activador actualizó a otra entidad en la base de datos, excepto que la ignore como lo muestra Sixto Saez en su respuesta. Sin embargo, puede usar EventListeners para realizar la acción desencadenante en el código. O configure SET NOCOUNT ON al comienzo del desencadenador y un SET NOCOUNT OFF al final si la actualización no tiene importancia para el resto de su transacción.

Cuestiones relacionadas