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

时间:2014-06-09 09:59:19

标签: c# asp.net nhibernate commit

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

我正在关注this tutorial,但我无法让代码生效。

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

如果修改是正确的,请告诉我吗?

我已将方法this.CommitChanges();添加到所有SaveSaveOrUpdateDelete方法。

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
}

我在SQL事件探查器上只获得了这3个语句

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'

取消评论this.CommitChanges()关于删除Methode

之后的声明
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

请问您的代码中有什么问题?我错过了什么?

1 个答案:

答案 0 :(得分:0)

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

问题是,每个操作集(网络请求,工作单元)应该封装在 transcation 中 - 使用显式Commit或{ {1}}致电。

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

  使用本机ID生成的

... 对象在保存时插入 ...

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

最重要的设置是Rollback * FlushMode(引自9.6. Flush文档:)

  

...除非您明确ISession,否则绝对无法保证Session何时执行ADO.NET调用,只保证它们执行的顺序。 但是,NHibernate确保Flush()方法永远不会返回陈旧数据;他们也不会返回错误的数据。

因此,正是我们上面所经历的 - FlushMode设置为Auto ...没有显式事务提交或回滚,但数据是持久的。 但这很可能是事故...... 因为缺少交易

FlushMode

以下是FlushModes (请参阅下面的引用,代码段)

ISession.Find(..)

当我们从工厂创建会话时,我们可以指定其中一个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 } 或 - 使用FlusMode.Never并在事务Commit()之前调用Session.Flush()。

因为 - 我们应该成为FlushMode.Commit显式调用的驱动程序。所以,不要依赖NHiberante发布INSERT的事实。这是后来处理的必要条件。总是在Transacton中包装这组操作,使用FlushMode.Commit ......这将是我的建议

注意:另请查看此Does criteria.List(Type) transaction management,以获取更多链接,以了解甚至是读取操作的交易优势。