Нужно ли добавлять CommitChange () в каждый метод Save, SaveOrUpdate и Delete?

1

Я начал внедрять веб-страницу asp.net с использованием NHibernate в качестве сопоставления базы данных.

Я следую этому руководству, но мне не удается заставить код работать.

Ниже приведен мой сценарий, который я изменил в абстрактном классе.

Не могли бы вы сообщить мне, если это будет правильно?

Я добавил метод this.CommitChanges(); для всех методов Save, SaveOrUpdate и Delete.

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 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 
                            // 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'

Заявление после un-comment this.CommitChanges() на Delete 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

Не могли бы вы сообщить, что происходит в моем коде? Я что-то пропустил?

Теги:
nhibernate
commit

1 ответ

0

Я бы сказал: Код, показанный в вопросе, приводит к неправильным, нежелательным результатам. Но проблема не в стороне NHibernate (например, некоторая ошибка в реализации).

Проблема в том, что каждый набор операций (веб-запрос, блок работы) должен быть инкапсулирован в трансконте - с явным вызовом Commit или Rollback.

NHibernate иногда просто должен выполнить некоторые операционные состояния WRITE * (см. 9.6. Документация по флешу :)

... объекты, использующие встроенную идентификацию, вставляются, когда они сохраняются...

Тот факт, что NHibernate должен выдать этот вызов, не означает, что такая операция должна сохраняться. В этом случае это только результат неизбежного - получить идентификатор сгенерированной БД. Это все еще может случиться, что мы должны вернуться к работе позже...

Наиболее важным параметром является FlushMode ISession * (ISession из 9.6. Документация по ISession :)

... За исключением случаев, когда вы проявляете Flush(), абсолютно никаких гарантий о том, когда сеанс выполняет вызовы 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.Never и вызвать Session.Flush() непосредственно перед транзакцией Commit().

Потому что - мы должны быть драйвером явного вызова Flush(). Итак, не полагайтесь на то, что NHiberante выпустил INSERT. Это было необходимо для последующей обработки. Всегда завершайте набор операций в Transacton, используйте FlushMode.Commit... это будет мое предложение

ПРИМЕЧАНИЕ. Также проверьте это. Управление транзакциями criteria.List(Type), чтобы узнать больше о преимуществах транзакций даже для операций чтения.

  • 0
    Итак, исходя из вашей идеи, я должен снять комментарий this.CommitChange () для этих определений Save, SaveOrUpdate и Delete Methode?
  • 0
    Подумайте о том факте, что он работает без явной фиксации как случайность . Это было бы мое предложение. Это неправильно, потому что мы не просили NHibernate сделать это: 1) ни вызывая Flush () или Commit () 2), ни устанавливая Session.FlushMode равным всегда. Это означает ... потому что NHibernate нужно было сделать INSERT (для получения идентификатора), это неправильно ... если также не выполнена полная операция. Мы должны использовать транзакцию на запрос или единицу работы. Но только полная (и полностью успешная) операция должна заканчиваться на Flush () и Commit (). Это немного помогает?
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню