Реализация единицы работы с WCF WebApi - PullRequest
3 голосов
/ 09 января 2012

Одна из проблем, с которой я сталкиваюсь с новым WCA WebApi, заключается в том, что я не могу чисто реализовать шаблон UnitOfWork.

В качестве быстрого фона шаблон работает, запуская единицу работы в начале запроса, выполняя некоторую работу, а затем либо откатывая, либо фиксируя единицу работы.

Довольно легко настроить код, который будет «запускать» единицу работы, используя функциональность HttpMessageHandler. А через библиотеку Task <> я могу написать продолжение, которое выполняется после обработки запроса. Однако я не всегда могу определить, произошла ли ошибка, чтобы я мог выполнить откат.

WCF имеет низкоуровневую поддержку отказов, и в традиционной конечной точке службы WCF вы можете проверить, не вышел ли канал из строя (например, изнутри IMessageInspector). Но WebApi, похоже, предотвращает такое поведение.

WebApi предоставляет контракт HttpErrorHandler. Однако это очень ограничено, поскольку у вас нет доступа к контексту экземпляра или экземпляру службы, поэтому у меня нет доступа к моей единице работы.

Интересно, какие еще подходы использовались для реализации здесь модели единиц работы?

Ответы [ 4 ]

4 голосов
/ 10 января 2012

Педро и Даррел оба дали отличные предложения. В решении, которое я наконец-то нашел, я использовал обработчик сообщений:

public class UnitOfWorkHandler : DelegatingHandler 
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    {
        var unitOfWork = new UnitOfWork();
        unitOfWork.Begin();
        return base.SendAsync(request, cancellationToken).ContinueWith(result => 
            {
                if (result.Result is HttpResponseMessage && ((HttpResponseMessage)result.Result).IsSuccessStatusCode) 
                {
                    unitOfWork.Commit();
                } 
                else 
                {
                    unitOfWork.Rollback();
                }
                return result.Result;
            });
    }
}

Я бы использовал предложение Даррела о сохранении ссылки на UnitOfWork в коллекции свойств HttpRequestMessage, однако, поскольку продолжение Задачи реализовано как замыкание, я могу просто сослаться на созданную мной единицу работы в ее внешняя область применения.

2 голосов
/ 10 января 2012

1) Вы можете использовать обработчики операций или обработчики сообщений для установки и демонтажа единицы работы (UoW) a) Обработчики сообщений имеют область действия конечной точки, поэтому они применяются ко всем операциям.b) Обработчики операций имеют область действия и могут быть полезны, если только некоторые операции требуют UoW.

2) Как заявил Даррел, вся контекстная информация, касающаяся HTTP-запроса, должна быть добавлена ​​в пакет свойств HttpRequestMessage.

3) В модели Web API нет ошибок (ошибка - это конструкция SOAP).Вы, вероятно, должны полагаться на статус ответа HTTP, чтобы проверить, была ли операция успешной (2xx) или нет (4xx, 5xx).

2 голосов
/ 10 января 2012

Существует коллекция свойств, которая зависает от HttpRequestMessage, в которую вы, вероятно, можете вставить свою единицу работы, чтобы получить к ней доступ по обратному пути.

0 голосов
/ 03 сентября 2013

Мой сценарий:

  • Контроллер (необходимо репо)
  • Репо (необходимо IUnitOfWork)
  • Фильтр действий (нужен IUnitOfWork также)

Проблема:

DI в действии фильтра меня беспокоит некоторое время.Мне нравится подход Джозефа , мне действительно нравится.Создание моей единицы работы (или начало) перед каждым запросом кажется мне естественным.Поскольку фильтры действий могут кэшироваться, они не используют ту же область зависимости, что и контроллеры. Следовательно, требуется установка Setter.

Решение:

Сконфигурируйте Structuremap так же, как если бы у меня не было фильтров действий.Примерно так:

public class NHibernateRegistry : Registry
{
    public NHibernateRegistry()
    {
        For<ISessionFactory>).Singleton().Use(
            NhSessionFactoty.Instance.SessionFactory);
        For<IUnitOfWork>().HybridHttpOrThreadLocalScoped().Use<NhUnitOfWork>();
        For(typeof(IBaseRepository<,>)).Use(typeof(BaseRepository<,>));
    }
}

NhSessionFactoty инкапсулирует мою конфигурацию гибернации и позволяет получить один ISessionFactory для использования в моем приложении.NhUnitOfWork реализует мой IUnitOfWork интерфейс и управляет сессией и управлением транзакциями.

Атрибут фильтра действий (адаптирован из ответ Джозефа ):

public class UnitOfWorkAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        /* Check this line, it's a problem solver!! */
        var uow = (IUnitOfWork)actionContext.Request.GetDependencyScope()
            .GetService(typeof(IUnitOfWork));
        uow.Begin();
    }

    public override void OnActionExecuted(
        HttpActionExecutedContext actionExecutedContext)
    {
        var uow = (IUnitOfWork)actionExecutedContext.Request.GetDependencyScope()
            .GetService(typeof(IUnitOfWork));

        try
        {
            if (actionExecutedContext.Exception == null)
                uow.Commit();
            else
                uow.Rollback();
        }
        catch
        {
            uow.Rollback();
            throw;
        } 
        /*finally
        {
            uow.Dispose();
        }*/ // Resources are released in Application_EndRequest (Globals.cs)
    }
}

инаконец, мой базовый контроллер:

[UnitOfWork]
public class ControllerBaseUow : ApiController {}
/* Then, in my case, I inject Repositories via contructor */

Он работает, потому что в OnActionExecuting мы получаем ту же область зависимости, которая используется в контроллерах.

, тогда вы работаете, как обычно, с DI:)

...