Принудительное выполнение запроса без flush / commit
Привет я использую шаблон transaction-per-request (session-in-view) для asp.net веб-приложение. У меня есть пара пунктов в приложении, где я хочу сохранить управляемую сущность NHibernate, а затем сделать еще пару вставок и обновлений с помощью обычного sql. Эти вставки / обновления зависят от идентификатора, который будет принимать сохраненная сущность NH.
Проблема заключается в том, что сгенерированный идентификатор не существует в области транзакций. Если я заставляю флеш / фиксацию, идентификатор сохраняется, но если вставки / обновления терпят неудачу, я должен откатить, но сброшенная/зафиксированная сущность не будет. В настоящее время я делаю ручную вставку для этих случаев, но это то, что я хочу изменить. Итак, есть ли способ выполнить инструкцию SQL (внутри уже открытой транзакции) после Save (), но без принудительного flush/commit?
EDIT: я добавляю пример полу-псевдокода, я получил 4 неверных ответа, поэтому я думаю, что люди не понимают (как работает NHibernate) По первому требованию я выдаю a
nhsession.BeginTransaction()
Тогда в какой-то момент я делаю
FooClass fc = new FooClass("value");
nhsession.Save(fc);
ITransaction trans = nhsession.Transaction;
SqlCommand sc = new SqlCommand("some insert/update query that depends on fc's id", (SqlConnection)nhsession.Connection);
sc.Parameters.Add("id", fc.Id); //NHibernate generates the id, note i'm using assigned/hi-lo so no round trip to the db takes place
transaction.Enlist(sc);
try {
sc.ExecuteNonQuery();
}
catch (SqlException ex){
transaction.RollBack();
nhsession.Close();
}
И в конце запроса я выдаю a CommitTransaction()
и nhsession.Close()
Теперь это абсолютно ничего не даст: FooClass (fc)
не был сброшен/отправлен в базу данных. Операция Save()
, которую выполнил NH, до этого момента находится в памяти. Это означает, что команда sql не была выдана nhibernate, и это означает, что SqlCommand (sc)
, который я запускаю после этого, с треском провалится, поскольку id не существует.
Если я сделаю флеш / коммит между Save()
и SqlCommand
FooClass(fc)
_cannot_be_rolled_back_, и это плохо, плохо.
В настоящее время, чтобы это работало, я делаю vanila sql insert с помощью SqlCommand
, и я хочу изменить это. (Почему? поскольку я не хочу делать ванильные вставки, они подвержены ошибкам из-за изменений схемы/модели, и я получил OR/M для этого)
Как? я хочу уведомить NHibernate как-то выполнить SqlCommand
, чтобы он соответствовал Save()
insert (черт, он может выполнять все команды SqlCommands, которые он собрал), но без него совершение или смывание!.
В настоящее время я также ищу подготовленный SQL-оператор, который nhibernate производит при очистке/фиксации сохраненного объекта. Может быть, я могу просто взять эту строку и запустить ее в моей команде SqlCommand, которая включена в транзакцию.
5 ответов:
Может быть, я не понимаю, но разве вы не можете сделать это так?..
using(var tx = session.BeginTransaction()) { session.Save(entity); session.Flush(); //perform non NH operations with entity.id tx.Commit(); }//uncommitted transaction would be rolled back on dispose()
Не можете ли вы использовать вложенные транзакции или области транзакций для поддержки этого?
По существу, вы просите nh назначить идентификатор перед его использованием, сгенерированные идентификаторы назначаются только при сбросе изменений, изменения сбрасываются только на границах транзакций, поэтому вы не можете создать внутреннюю область транзакций, чтобы обернуть связанную с nh персистентность, заставляющую сброситься и генерировать id, но все еще позволяющую внешней транзакции потерпеть неудачу и откатить всю работу, если один из хранимые процедуры не работают.
Как сделать вложенные транзакции в NHibernate? имеет некоторый пример кода, который демонстрирует подход с использованием областей.
Звучит так, как будто вы хотите получить грязный уровень изоляции чтения во время транзакции. Это позволит вам читать незафиксированное, но сохраненное содержимое. Однако я недостаточно бегло говорю по-Нхибернатски, чтобы дать какое-либо представление о том, как это сделать.
Я уверен, что кто-то еще может подключиться к тому, как сделать грязный уровень изоляции чтения для NH.
Я использую замок ActiveRecord поверх NHibernate, но princple должен быть таким же, мой DTC отключен, поэтому я не уверен, что это работает, но это должно быть правильное направление.
Imports Castle.ActiveRecord imports NHibernate.Expression <ActiveRecord> _ Public Class Test1 Inherits ActiveRecordBase(Of Test1) Private _id As Guid <PrimaryKeyAttribute(PrimaryKeyType.Guid)> _ Public overridable Property Id as Guid Get return _id End Get Set(value As guid) _id = value End Set End Property Private _prop1 As String <[Property]> _ Public overridable Property prop1 as String Get return _prop1 End Get Set(value As String) _prop1 = value End Set End Property End Class <ActiveRecord> _ Public Class Test2 Inherits ActiveRecordBase(Of Test2) Private _id As Guid <PrimaryKey(PrimaryKeyType.Guid)> _ Public overridable Property Id as Guid Get return _id End Get Set(value As guid) _id = value End Set End Property Private _prop1 As String <[Property]> _ Public overridable Property prop1 as String Get return _prop1 End Get Set(value As String) _prop1 = value End Set End Property Private _t1 As Test1 <BelongsTo()> _ Public overridable Property t1 as Test1 Get return _t1 End Get Set(value As test1) _t1 = value End Set End Property End Class
Imports Castle.ActiveRecord Imports NHibernate.Expression Imports System.Data.SqlClient Imports System.Transactions imports System.Configuration Public Module Module1 Public Class modMain Public Shared Sub Main() Castle.ActiveRecord.ActiveRecordStarter.Initialize() Using T As New System.Transactions.TransactionScope(ondispose.Rollback) Dim x As New Test1() x.prop1 = "Hello" x.Save using c As New SqlConnection() c.ConnectionString = ConfigurationManager.ConnectionStrings("Development").ConnectionString Dim cmd As SqlCommand = c.CreateCommand() cmd.CommandText = "insert into test2(prop1, t1) values(@prop1, @t1)" Dim p As SqlParameter = cmd.CreateParameter() p.Direction = parameterdirection.Input p.DbType = dbtype.Guid p.ParameterName = "@prop1" p.Value = "Test" cmd.Parameters.Add(p) p = cmd.CreateParameter() p.Direction = parameterdirection.Input p.DbType = dbtype.Guid p.ParameterName = "@t1" p.Value = x.Id c.Open cmd.ExecuteNonQuery end using t.Complete end using End Sub End Class End Module
Вы пробовали использовать функции прямого SQL NHibernate? Вместо того, чтобы прорываться в SQLCommand, о котором NHibernate ничего не знает, вы можете использовать ISession.CreateSQLQuery ("родной SQL здесь"). Если вы затем сделаете это внутри транзакции после вашего метода сохранения, NHibernate должен выполнить все инструкции по порядку, когда произойдет фиксация. Таким образом:
using(var tx = session.BeginTransaction()) { try { FooClass fc = new FooClass("value"); nhsession.Save(fc); var nativeQuery = nhsession.CreateSQLQuery("some insert/update query that depends on fc's id"); // Add parameters to nativeQuery using SetXXX methods nativeQuery.SetXXX(...); // Save nativeQuery nativeQuery.Save(); // I think... tx.Commit(); } catch (...) { tx.Rollback(); } }
Смотрите это для другого аналогичного вопроса относительно использования NH native query: Hibernate NHibernate-Native SQL