2010-01-05 20 views
14

Estoy almacenando algunos datos usando NHibernate, y tengo que insertar gran cantidad de datos como parte de esta acción, es decir, en la misma transacción. El código se ve así:Uso de la transacción NHibernate en SqlBulkCopy

using (ISession session = NHibernateHelper.OpenSession()) 
using (ITransaction transaction = session.BeginTransaction()) 
{ 
    session.SaveOrUpdate(something); 
    // ... 


    SqlBulkCopy bulkCopy = new SqlBulkCopy(
    (SqlConnection)session.Connection, 
    SqlBulkCopyOptions.CheckConstraints | SqlBulkCopyOptions.FireTriggers, 
    ???transaction??? 
    ); 
    //... 

    transaction.Commit(); 
} 

Sé que podría usar TransactionScope o hacerlo de otra manera. Pero insisto en este patrón. Vamos a pretender que, por el bien del acceso a bases de datos independientes (si extraigo e inyecto una operación de inserción masiva arbitraria). ¿Hay alguna manera de obtener la instancia SqlTransaction de NHibernate.ITransaction?

Gracias

+0

¿La respuesta a continuación le soluciona su pregunta? – Meligy

Respuesta

22

Como era de esperar, Ayende tackled this one as well, pero es bastante grody.

El quid de la cuestión es que usted sabe que puede dar de alta normales ADO.NET IDbCommand casos en los que la transacción NHibernate, así:

var cmd = new SqlCommand(); 
if (session.Transaction != null && session.Transaction.IsActive) 
    session.Transaction.Enlist (cmd); 

Pero SqlBulkCopy no es una IDbCommand, y que requiere un constructor particular, SqlTransaction (por lo que ya se ha saltado el barco en independencia del proveedor de todos modos). Así engañar - el ejemplo podría ser algo como esto:

using (var session = NHibernateHelper.OpenSession()) 
using (var transaction = session.BeginTransaction()) { 
    using (var cmd = new SqlCommand()) { 
     transaction.Enlist (cmd); 

     var bulk = new SqlBulkCopy ((SqlConnection)session.Connection, 
            SqlBulkCopyOptions.CheckConstraints | SqlBulkCopyOptions.FireTriggers, 
            (SqlTransaction)cmd.Transaction); 
    } 
    // ... 
    transaction.Commit(); 
} 

Usted, sin duda, quieren algo de comprobación de errores, moldes fuertes, etc. allí. No estoy al tanto de una forma más moderna/menos aterradora de hacer esto, desafortunadamente (incluso para obtener un IDbTransaction de un ITransaction).

+0

Aunque es un poco hack, probablemente sea lo más cercano a lo que necesito. ¡Buen trabajo! Gracias. – Elephantik

0

Verificar este post de Ayene:
http://ayende.com/Blog/archive/2009/08/22/nhibernate-perf-tricks.aspx

Se muestra cómo se puede hacer que el uso de cualquiera de las dos opciones NHibernate StatelessSession o SqlBulkCopy. Se muestra un ejemplo de código siguiente manera:

var dt = new DataTable("Users"); 
    dt.Columns.Add(new DataColumn("Id", typeof(int))); 
    dt.Columns.Add(new DataColumn("Password", typeof(byte[]))); 
    dt.Columns.Add(new DataColumn("Username")); 
    dt.Columns.Add(new DataColumn("Email")); 
    dt.Columns.Add(new DataColumn("CreatedAt", typeof(DateTime))); 
    dt.Columns.Add(new DataColumn("Bio")); 

    for (int i = 0; i < count; i++) 
    { 
     var row = dt.NewRow(); 
     row["Id"] = i; 
     row["Password"] = Guid.NewGuid().ToByteArray(); 
     row["Username"] ="User " + i; 
     row["Email"] = i + "@example.org"; 
     row["CreatedAt"] =DateTime.Now; 
     row["Bio"] = new string('*', 128); 
     dt.Rows.Add(row); 
    } 

using (var connection = 
((ISessionFactoryImplementor)sessionFactory).ConnectionProvider.GetConnection()) 
{ 
    var s = (SqlConnection)connection; 
    var copy = new SqlBulkCopy(s); 
    copy.BulkCopyTimeout = 10000; 
    copy.DestinationTableName = "Users"; 
    foreach (DataColumn column in dt.Columns) 
    { 
     copy.ColumnMappings.Add(column.ColumnName, column.ColumnName); 
    } 
    copy.WriteToServer(dt); 
} 
+0

Lo siento pero no puedo ver una respuesta al problema de la transacción. Crear una instancia de SqlBulkCopy no es un problema. – Elephantik

Cuestiones relacionadas