0

我只是在学习 nHibernate,并且遇到了一个可能很容易解决的问题。

是的,到目前为止,我已经发现您不能/不应该将 nHibernate 事务嵌套在彼此之间;就我而言,当范围转到另一个例程并开始新事务时,我发现了这一点。

那么我应该做以下事情吗?

using (ITransaction transaction = session.BeginTransaction())
{
    NHibernateMembership mQuery =
        session.QueryOver<NHibernateMembership>()
            .Where(x => x.Username == username)
            .And(x => x.ApplicationName == ApplicationName)
            .SingleOrDefault();

    if (mQuery != null)
    {
        mQuery.PasswordQuestion = newPwdQuestion;
        mQuery.PasswordAnswer = EncodePassword(newPwdAnswer);
        session.Update(mQuery);

        transaction.Commit();
        passwordQuestionUpdated = true;
    }
}

// Assume this is in another routine elsewhere but being
// called right after the first in the same request
using (ITransaction transaction = session.BeginTransaction())
{
    NHibernateMembership mQuery =
        session.QueryOver<NHibernateMembership>()
            .Where(x => x.Username == username)
            .And(x => x.ApplicationName == ApplicationName)
            .SingleOrDefault();

    if (mQuery != null)
    {
        mQuery.PasswordQuestion = newPwdQuestion;
        mQuery.PasswordAnswer = EncodePassword(newPwdAnswer);
        session.Update(mQuery);

        transaction.Commit();
        passwordQuestionUpdated = true;
    }
}

注意:我知道它们只是一个副本,我只是在展示我的问题

第一个问题 这是它应该做的方式吗?每个操作的交易?

第二个问题 我需要调用 transaction.Commit(); 每次还是只在最后一组?

第三个问题 有没有更好的方法,自动或手动,来做到这一点?

第三个问题 我可以使用 session.Transaction.IsActive 来确定“当前会话”是否已经是事务的一部分 - 所以在这种情况下,我可以在最高级别进行“事务包装”,比如说 Web 表单代码,以及让例程在其中调用,然后在最后提交所有工作。这是一个有缺陷的方法吗?

我真的很想把它敲下来,所以我按我的意思开始;我不想找到 1000 行代码,因为我需要全部更改。

提前致谢。

编辑:

对,所以我写了一些代码来准确解释我的问题。

private void CallingRoutine()
{
    using(ISession session = Helper.GetCurrentSession)
    {
        using (ITransaction transaction = session.BeginTransaction())
        {
            //  RUN nHIbernate QUERY to get an OBJECT-1

            //  DO WORK on OBJECT

            //  Need to CALL an EXTERNAL ROUTINE to finish work
            ExternalRoutine();

            //  DO WORK on OBJECT-1 again

            //  *** At this point ADO exception triggers
        }
    }
}

private bool ExternalRoutine()
{
    using(ISession session = Helper.GetCurrentSession)
    {
        using (ITransaction transaction = session.BeginTransaction())
        {
            //  RUN nHIbernate QUERY to get an OBJECT-2

            //  DO WORK on OBJECT

            //  Determine result
            if(Data)
            {
                return true;
            }

            return false;
        }
    }
}

希望这能说明问题。这就是我理解编写事务的方式,但请注意 ADO 异常是如何发生的。我显然做错了什么。我打算如何编写这些例程?

例如,如果我要为某个提供程序编写一个辅助对象,并且在每个公开的例程中都有一个 nHibernate 查询运行——假设我对调用函数和数据一无所知,我将如何编写这些例程,关于事务——我的工作是有效和高效地使用 nHibernate 并返回结果。

这就是我通过编写事务我在 ExternalRoutine() 中所做的假设 - 假设这是 nHibernate 的唯一用途并明确地进行事务。

4

1 回答 1

0

如果可能,我建议使用 System.Transactions.TransactionScope:

using(var trans = new TransactionScope()) 
using(var session = .. create your session...) {
    ... do your stuff ...
    trans.Complete();
}

The trans.Complete will cause the session to flush and commit the transaction, in addition you can have nested transactionscopes. The only "downside" to this is that it will escalate to DTC if you have multiple connections (or enlisted resources such as MSMQ), but this is not necessarily a downside unless you're using something like MySQL which doesn't play nicely with DTC.

For simple cases I would probably use a transaction scope in the BeginRequest and commit it in EndRequest if there were no errors (or use a filter if u're using ASP.NET MVC), but that really depends a lot on what you're doing - as long as your operations are short (which they should be in a web app), this should be fine. The overhead of creating a session and transaction scope is not that big that it should cause problems for accessing static resources (or resources that don't need the session), unless you're looking at a really high volume / high performance website.

于 2012-06-14T09:34:52.237 回答