Когда совершать транзакции NHibernate в приложении ASP.NET MVC 2? - PullRequest
5 голосов
/ 26 августа 2010

Сначала немного предыстории: я новичок в ASP.NET MVC 2 и NHibernate.Я запускаю свое первое приложение и хочу использовать NHibernate, потому что я из веб-приложений JSP + Struts 1 + Hibernate.

Кажется, никто не говорит об этом, поэтому я думаю, это должно быть довольно очевидно,Тем не менее я ломаю голову, потому что не могу найти решение, которое бы решало следующие задачи:

1) Я хочу использовать стратегию «сеанс на запрос».Таким образом, каждый раз, когда пользователь делает запрос, он получает сеанс Nhibernate, запускает транзакцию, а когда запрос завершается, транзакция фиксируется, и сеанс NHibernate закрывается (и возвращается в пул, если таковой имеется).Это гарантирует, что мои транзакции являются атомарными.

2) Когда возникает исключение базы данных (нарушение PK, уникальное нарушение и т. Д.), Я хочу перехватить это исключение, откатить транзакцию и дать пользователю явное сообщение: если онобыло ли нарушение PK, затем это сообщение и то же самое со всеми ошибками целостности.

Итак, в чем моя проблема?Я пришел из мира Java, где использовал фильтр, чтобы открыть сеанс, запустить транзакцию, обработать запрос, затем зафиксировать транзакцию и закрыть сеанс.Это работает, за исключением случаев, когда возникает исключение из БД, и к тому времени, когда вы находитесь в фильтре, невозможно изменить страницу назначения, поскольку ответ уже зафиксирован.

Таким образом, пользователь видит страницу успеха, когда в действительности транзакция была отменена.Чтобы избежать этого, я должен написать много проверок целостности данных в Java, чтобы предотвратить все исключения целостности, потому что я не мог обработать их правильно.Это плохо, потому что я делаю работу вместо того, чтобы оставлять ее в базе данных (или, может быть, я ошибаюсь, и мне всегда приходится писать весь этот код целостности данных в моем приложении?).

Итак, я 'мы нашли интерфейс IHttpModule, который, как я предполагаю, в значительной степени похож на javax.servlet.Filter (исправьте меня, если я ошибаюсь), поэтому я предполагаю, что у меня снова может возникнуть та же проблема.

Где я должен разместить свои коммиты, чтобы убедиться, что мои транзакции являются атомарными, и когда они генерируют исключения, я могу их перехватить, изменить страницу назначения и дать пользователю исчерпывающее сообщение?

Пока чтоЕдинственное возможное решение, которое я придумала, - это оставить мой IHttpModule для запуска и закрытия транзакции и поместить вызовы коммита в последнюю строку моих методов контроллеров, таким образом, имея возможность перехватывать исключения там, а затем возвращать подходящее представление ссообщение.Теперь мне нужно будет скопировать эти строки обработки и обработки исключений во все мои методы контроллера, которые требуют фиксации.И есть проблема разделения проблем, которую мои контролеры должны знать о БД, что мне совсем не нравится.

Есть ли лучший способ?

Ответы [ 2 ]

1 голос
/ 27 августа 2010

Если вы используете ASP.NET MVC, вы можете использовать ActionFilter для достижения того же эффекта.

Что-то вроде (это взломано из разных частей моей архитектуры):

public class TransactionalAttribute : ActionFilterAttribute, IAuthorizationFilter, IExceptionFilter
{

    ITransaction transaction = NullTransaction.Instance;
    public IsolationLevel IsolationLevel { get; set; } 

    public TransactionalAttribute() 
    {
        IsolationLevel = IsolationLevel.ReadCommitted;
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        try
        {
            transaction.Commit();
            transaction = NullTransaction.Instance;
        }
        catch (Exception exception)
        {
            Log.For(this).FatalFormat("Problem trying to commit transaction {0}", exception);
        }

    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (transaction == NullTransaction.Instance) transaction = UnitOfWork.Current.BeginTransaction(IsolationLevel);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result != null) return;

        transaction.Commit();
        transaction = NullTransaction.Instance;
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        transaction = UnitOfWork.Current.BeginTransaction(IsolationLevel);
    }

    public void OnException(ExceptionContext filterContext)
    {
        try
        {
            transaction.Rollback();
            transaction = NullTransaction.Instance;
        }
        catch (Exception exception)
        {
            Log.For(this).FatalFormat("Problem trying to rollback transaction {0}", exception);
        }
    }

    private class NullTransaction : ITransaction
    {
        public static ITransaction Instance { get { return Singleton<NullTransaction>.Instance; } }

        public void Dispose()
        {

        }

        public void Commit()
        {
        }

        public void Rollback()
        {
        }
    }
}
0 голосов
/ 17 сентября 2010

Хорошо подумав об этом и обсудив его с коллегами, я пришел к решению, которое удовлетворяет почти всем моим требованиям.

Я реализовал решение с помощью своих Java-проектов, и оно отлично работало.Я просто приведу идею, чтобы каждый мог использовать ее в любой среде.

Решение состоит в том, чтобы поместить вызов commit в последнюю строку метода контроллера, внутри блока try-catch.Если возникает исключение ограничения, вы можете получить имя нарушенного ограничения.С именем вы можете сказать пользователю, что именно пошло не так.Я использовал файл свойств, чтобы сохранить сообщение для показа пользователю, ограничение которого было нарушено.Ключи файла свойств - это имена ограничений, а значения - сообщения о нарушении ограничений.

Йо может реорганизовать commit-handle_exception-find_constraint_message в метод, вот что я сделал.

На данный момент это решает мою проблему написания кода для проверки целостности базы данных, и я считаю, что это довольно элегантносообщения о нарушении ограничений в файле свойств.Теперь мне все еще не нравится идея, что моим контроллерам нужно вызывать коммит, но это намного лучше, чем писать проверки целостности, что база данных уже выполняет .

Я буду продолжать использоватьфильтр, как сказал Дэвид Кемп, только то, что фильтр откроет только (n) сеанс гибернации и транзакцию, а затем, в конце запроса, закроет сеанс.

Комментарии приветствуются,Спасибо.

...