Привет я использую транзакцию на запрос (сессия в поле зрения) шаблон для веб-приложения asp.net. У меня есть несколько точек в приложении, где я хочу Сохранить управляемый объект NHibernate и затем сделай еще несколько вставок и обновлений с помощью общего sql. Они вставляют/обновляют, зависят от идентификатора, который возьмет сохраненный объект NH.
Проблема состоит в том, что сгенерированный идентификатор не существует в объеме транзакций. Если я вызываю сброс/фиксацию, идентификатор сохраняется, но если вставляет/обновляет сбой, я должен откатывать, но вспыхнувший/зафиксированный объект не будет. В настоящее время я делаю ручную вставку для этих случаев, но это - что-то, что я хочу изменить. Так, есть ли способ выполнить SQL-оператор (в уже открытой транзакции) после Сохранения (), но не вызывая сброс/фиксацию?
Править: Я добавляю пример полупсевдокода, я получил 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)
то, что я стреляю, впоследствии потерпит полный провал, поскольку идентификатор не существует.
Если я делаю сброс/фиксацию между Save()
и SqlCommand
FooClass(fc)
_cannot_be_rolled_back_ и это являются плохой плохой вещью. В настоящее время, чтобы это работало, я делаю ваниль sql, вставляют использование SqlCommand
, и я хочу изменить это. (Почему? потому что я не хочу делать ваниль, вставляет, они восприимчивы к ошибкам из-за изменений схемы/модели, и я получил OR/M для этого),
Как? я хочу уведомить NHibernate так или иначе для выполнения SqlCommand
к соответствует Save()
вставьте (черт, это может сделать весь SqlCommands, который это собрало), но без него фиксирование или сбрасывание!.
В настоящее время я также ищу подготовленный sql оператор, который производит nhibernate, когда сбрасывать/фиксировать сохраненный объект. Возможно, я могу просто взять ту строку и выполнить ее в моем SqlCommand, который включается в список в Транзакцию.
может я не понимаю, но вы не можете так сделать ...
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()
Похоже, вам нужен уровень изоляции «грязного чтения» во время транзакции. Это позволит вам читать «незарегистрированный», но «сохраненный» контент. Однако я недостаточно хорошо владею NHibernate, чтобы дать какое-либо представление о том, как это сделать там.
Я уверен, что кто-то еще может понять, как сделать уровень изоляции «грязного чтения» для NH.
Я использую Castle ActiveRecord поверх NHibernate, но принцип должен быть таким же, мой 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
Пробовали ли вы использовать прямые возможности NHibernate по работе с SQL? Вместо разбиения на SQLCommand, о котором NHibernate ничего не знает, вы можете использовать ISession.CreateSQLQuery("native SQL here"). Если вы затем сделаете это внутри транзакции после метода Save, NHibernate выполнит все операторы по порядку, когда произойдет Commit. Таким образом:
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: Hibernate NHibernate - Native SQL
Разве вы не можете использовать вложенные транзакции или области транзакций для поддержки этого?
По сути, вы просите nh назначить идентификатор перед его использованием, сгенерированные идентификаторы назначаются только тогда, когда изменения сбрасываются, изменения сбрасываются только поэтому на границах транзакции вы не можете создать внутреннюю область транзакции, чтобы обернуть связанное с nh постоянство, вызывая сброс и генерацию идентификатора, но все же позволяя внешней транзакции завершиться сбоем и откатить всю работу, если одна из хранимых процедур не удалась.
Как выполнять вложенные транзакции в NHibernate? В есть образец кода, демонстрирующий подход с использованием областей видимости.