Принудительное выполнение запроса без 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 6

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