I'm storing some data using NHibernate, and I need to insert huge amount of data as a part of this action - ie in the same transaction. Code looks like this:
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();
}
I know that I could use TransactionScope or do it otherwise. But I insist on this pattern. Let's pretend that for the sake of independent DB access (if I extract and inject arbitrary bulk insert operation). Is there a way how to get SqlTransaction
instance out of NHibernate.ITransaction
?
Thanks
Unsurprisingly, Ayende tackled this one as well , but it's pretty grody.
The gist of it is that you know you can enlist normal ADO.NET IDbCommand
instances in the NHibernate transaction, like so:
var cmd = new SqlCommand ();
if (session.Transaction != null && session.Transaction.IsActive)
session.Transaction.Enlist (cmd);
But SqlBulkCopy
isn't an IDbCommand
, and that particular constructor requires a SqlTransaction
(so you've already skipped the boat on provider-independence anyways). So cheat -- your example might look something like this:
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 ();
}
You'll undoubtedly want some error-checking, safe casts, etc. in there. I'm not aware of a more modern/less scary way to do this, unfortunately (even to get an IDbTransaction
from an ITransaction
).
Check this post from Ayene:
http://ayende.com/Blog/archive/2009/08/22/nhibernate-perf-tricks.aspx
He shows how you can do that using both options either NHibernate StatelessSession or SqlBulkCopy. It shows a sample code like this:
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);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.