我需要在每个Save, SaveOrUpdate和Delete方法中添加CommitChange()吗?

本文关键字:添加 方法 CommitChange Delete SaveOrUpdate Save | 更新日期: 2023-09-27 18:07:32

我已经开始使用NHibernate作为数据库映射实现asp.net网页。

我正在遵循本教程,但我有麻烦让代码工作。

下面是我在抽象类中修改的脚本。

请告诉我修改的是否正确?

我将this.CommitChanges();方法添加到所有Save, SaveOrUpdateDelete方法中。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NHibernate;
using NHibernate.Criterion;
using Tec.Core.DataInterfaces;
using Tec.Data.SessionManagement;

    namespace Tec.Data
    {
        public abstract class NHibernateRepository<T, IdT> : IRepository<T, IdT>
        {
            public T GetById(IdT id, bool shouldLock)
            {
                T entity;
                if (shouldLock)
                {
                    entity = (T)NHibernateSession.Load(persitentType, id, LockMode.Upgrade);
                }
                else
                {
                    entity = (T)NHibernateSession.Load(persitentType, id);
                }
                return entity;
            }

            public List<T> GetAll()
            {
                return GetByCriteria();
            }

            public List<T> GetByCriteria(params ICriterion[] criterion)
            {
                ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);
                foreach (ICriterion criterium in criterion)
                {
                    criteria.Add(criterium);
                }
                return criteria.List<T>() as List<T>;
            }
            public List<T> GetByExample(T exampleInstance, params string[] propertiesToExclude)
            {
                ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);
                Example example = Example.Create(exampleInstance);
                foreach (string propertyToExclude in propertiesToExclude)
                {
                    example.ExcludeProperty(propertyToExclude);
                }
                criteria.Add(example);
                return criteria.List<T>() as List<T>;
            }

            public T GetUniqueByExample(T exampleInstance, params string[] propertiesToExclude)
            {
                List<T> foundList = GetByExample(exampleInstance, propertiesToExclude);
                if (foundList.Count > 1)
                {
                    throw new NonUniqueResultException(foundList.Count);
                }
                if (foundList.Count > 0)
                {
                    return foundList[0];
                }
                else
                {
                    return default(T);
                }
            }

            public T Save(T entity) {
                NHibernateSession.Save(entity);
                //this.CommitChanges(); // manually added
                return entity;
            }

            public T SaveOrUpdate(T entity)
            {
                NHibernateSession.SaveOrUpdate(entity);
                //this.CommitChanges(); 
                return entity;
            }
            public void Delete(T entity) {
                NHibernateSession.Delete(entity);
                //this.CommitChanges(); //Record is delete from database only if I enable this
                                        statement           
            }

            public void CommitChanges(){
                if (NHibernateSessionFactory.Instance.HasOpenTransaction()) {
                    NHibernateSessionFactory.Instance.CommitTransaction();
                } else {
                    // If there's no transaction, just flush the changes
                    NHibernateSessionFactory.Instance.GetSession().Flush();
                }
            }

            private ISession NHibernateSession
            {
                get
                {
                    return NHibernateSessionFactory.Instance.GetSession();
                }
            }
            private Type persitentType = typeof(T);
        }


        //----------------------------------------------------------------------------------//


        public class NHibernateSessionFactory
        {
            public static NHibernateSessionFactory Instance
            {
                get
                {
                    return Nested.NHibernateSessionFactory;
                }
            }

            private NHibernateSessionFactory()
            {
                InitSessionFactory();
            }

            private class Nested
            {
                static Nested() { }
                internal static readonly NHibernateSessionFactory NHibernateSessionFactory = new NHibernateSessionFactory();
            }

            private void InitSessionFactory()
            {
                sessionFactory = new Configuration().Configure().BuildSessionFactory();
            }

            public void RegisterInterceptor(IInterceptor interceptor)
            {
                ISession session = ContextSession;
                if (session != null && session.IsOpen)
                {
                    throw new CacheException("You cannot register an interceptor once a session has already been opened");
                }
                GetSession(interceptor);
            }
            public ISession GetSession()
            {
                return GetSession(null);
            }

            private ISession GetSession(IInterceptor interceptor)
            {
                ISession session = ContextSession;
                if (session == null)
                {
                    if (interceptor != null)
                    {
                        session = sessionFactory.OpenSession(interceptor);
                    }
                    else
                    {
                        session = sessionFactory.OpenSession();
                    }
                    ContextSession = session;
                }
                return session;
            }

            public void CloseSession()
            {
                ISession session = ContextSession;
                if (session != null && session.IsOpen)
                {
                    session.Flush();
                    session.Close();
                }
                ContextSession = null;
            }
            public void BeginTransaction()
            {
                ITransaction transaction = ContextTransaction;
                if (transaction == null)
                {
                    transaction = GetSession().BeginTransaction();
                    ContextTransaction = transaction;
                }
            }
            public void CommitTransaction()
            {
                ITransaction transaction = ContextTransaction;
                try
                {
                    if (HasOpenTransaction())
                    {
                        transaction.Commit();
                        ContextTransaction = null;
                    }
                }
                catch (HibernateException)
                {
                    RollbackTransaction();
                    throw;
                }
            }
            public bool HasOpenTransaction()
            {
                ITransaction transaction = ContextTransaction;
                return transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack;
            }
            public void RollbackTransaction()
            {
                ITransaction transaction = ContextTransaction;
                try
                {
                    if (HasOpenTransaction())
                    {
                        transaction.Rollback();
                    }
                    ContextTransaction = null;
                }
                finally
                {
                    CloseSession();
                }
            }

            private ITransaction ContextTransaction
            {
                get
                {
                    if (IsInWebContext())
                    {
                        return (ITransaction)HttpContext.Current.Items[TRANSACTION_KEY];
                    }
                    else
                    {
                        return (ITransaction)CallContext.GetData(TRANSACTION_KEY);
                    }
                }
                set
                {
                    if (IsInWebContext())
                    {
                        HttpContext.Current.Items[TRANSACTION_KEY] = value;
                    }
                    else
                    {
                        CallContext.SetData(TRANSACTION_KEY, value);
                    }
                }
            }

            private ISession ContextSession
            {
                get
                {
                    if (IsInWebContext())
                    {
                        return (ISession)HttpContext.Current.Items[SESSION_KEY];
                    }
                    else
                    {
                        return (ISession)CallContext.GetData(SESSION_KEY);
                    }
                }
                set
                {
                    if (IsInWebContext())
                    {
                        HttpContext.Current.Items[SESSION_KEY] = value;
                    }
                    else
                    {
                        CallContext.SetData(SESSION_KEY, value);
                    }
                }
            }
            private bool IsInWebContext()
            {
                return HttpContext.Current != null;
            }
            private const string TRANSACTION_KEY = "CONTEXT_TRANSACTION";
            private const string SESSION_KEY = "CONTEXT_SESSION";
            private ISessionFactory sessionFactory;
        }

    }
下面是我的测试方法:
public void AddUser()
{
   // create three people
   User jose = new User();
   jose.UserName = "Jose";
   jose.UserLogin = "28";
   mUser.SaveOrUpdate(jose);// Record is addded to database, 
                            // some people said it's 
                            // because on auto-increment identity 
   User maria = new User();
   maria.UserName = "Maria";
   maria.UserLogin = "29";
   mUser.SaveOrUpdate(maria);
   User mario = new User();
   mario.UserName = "Mario";
   mario.UserLogin = "27";
   mUser.SaveOrUpdate(mario);
   // delete Mario
   mUser.Delete(mario); //Record is not deleted from database
}

,我只得到这3条语句在SQL分析器上执行

exec sp_reset_connection
exec sp_executesql N'INSERT INTO TEC.dbo.tblSysUser (UserName, UserLogin) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar(4000),@p1 nvarchar(4000)',@p0=N'Jose',@p1=N'28'
exec sp_reset_connection
exec sp_executesql N'INSERT INTO TEC.dbo.tblSysUser (UserName, UserLogin) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar(4000),@p1 nvarchar(4000)',@p0=N'Maria',@p1=N'29'
exec sp_reset_connection
exec sp_executesql N'INSERT INTO TEC.dbo.tblSysUser (UserName, UserLogin) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar(4000),@p1 nvarchar(4000)',@p0=N'Mario',@p1=N'27'

Delete method上的uncomment this.CommitChanges()后的语句

exec sp_reset_connection
exec sp_executesql N'INSERT INTO TEC.dbo.tblSysUser (UserName, UserLogin) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar(4000),@p1 nvarchar(4000)',@p0=N'Jose',@p1=N'28'
exec sp_reset_connection
exec sp_executesql N'INSERT INTO TEC.dbo.tblSysUser (UserName, UserLogin) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar(4000),@p1 nvarchar(4000)',@p0=N'Maria',@p1=N'29'
exec sp_reset_connection
exec sp_executesql N'INSERT INTO TEC.dbo.tblSysUser (UserName, UserLogin) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar(4000),@p1 nvarchar(4000)',@p0=N'Mario',@p1=N'27'
exec sp_reset_connection
exec sp_executesql N'DELETE FROM TEC.dbo.tblSysUser WHERE UserID = @p0',N'@p0 bigint',@p0=64

你能告诉我我的代码是怎么回事吗?我错过什么了吗?

我需要在每个Save, SaveOrUpdate和Delete方法中添加CommitChange()吗?

我会说:问题中显示的代码导致了错误的,不希望的结果。但问题不在NHibernate端(例如:实现中的一些bug)

问题是,每个操作集(web请求,工作单元)应该封装在事务中-带有显式的CommitRollback调用。

NHibernate有时必须执行一些WRITE操作语句*(引自9.6)。刷新文档:)

使用本地ID生成的对象在保存时插入

事实上,NHibernate必须发出调用,并不意味着,这样的操作应该持续。在这种情况下,它的结果只是不可避免的——获取DB生成的ID。这仍然可能发生,我们应该稍后回滚操作…

最重要的设置是FlushModeISession *(引用自9.6)。刷新文档:)

…除非显式地使用Flush(),否则绝对不能保证Session何时执行ADO。. NET调用时,只显示它们执行的顺序。然而,NHibernate保证ISession.Find(..)方法永远不会返回过时的数据;也不会返回错误的数据。

所以,正如我们上面所经历的- FlushMode设置为Auto…没有显式事务Commit或Rollback,但数据被持久化。但这很可能是事故…因为缺少事务

FlushMode

以下是可用的FlushModes (参见下面的引用,代码片段)

/// <summary>
/// Represents a flushing strategy.
/// </summary>
/// <remarks>
/// The flush process synchronizes database state with session state by detecting state
/// changes and executing SQL statements
/// </remarks>
[Serializable]
public enum FlushMode
{
    /// <summary>
    /// Special value for unspecified flush mode (like <see langword="null" /> in Java).
    /// </summary>
    Unspecified = -1,
    /// <summary>
    /// The <c>ISession</c> is never flushed unless <c>Flush()</c> is explicitly
    /// called by the application. This mode is very efficient for read only
    /// transactions
    /// </summary>
    Never = 0,
    /// <summary>
    /// The <c>ISession</c> is flushed when <c>Transaction.Commit()</c> is called
    /// </summary>
    Commit = 5,
    /// <summary>
    /// The <c>ISession</c> is sometimes flushed before query execution in order to
    /// ensure that queries never return stale state. This is the default flush mode.
    /// </summary>
    Auto = 10,
    /// <summary>
    /// The <see cref="ISession"/> is flushed before every query. This is
    /// almost always unnecessary and inefficient.
    /// </summary>
    Always = 20
}

当我们从工厂创建会话时,我们可以分配其中一个FlushModes。我强烈建议使用FlushMode.Commit 或-使用FlusMode。永远不要在事务Commit()之前调用Session.Flush()。

因为-我们应该是显式调用Flush()的驱动程序。因此,不要依赖于nhibernate发出INSERT的事实。这是以后处理的必需品。总是在transaction中包装操作集,使用FlushMode.Commit…这就是我的建议

注意:还要检查Does criteria.List(Type)事务管理,以获得更多讨论事务甚至读操作的优势的链接。