2015-05-28 1 views
4

Внутри функции контроллера api для веб-приложений я использую две службы, и поскольку они делают независимые вещи, я хочу, чтобы они использовали разные единицы работы (транзакции).web api controller and castle windsor lifestyle

Все необходимые компоненты (единица работы, репозитории) вводятся через виндзор замка с помощью LifestylePerWebRequest.

Из того, что я понимаю, решение заключается в использовании LifeStyleScoped но у меня есть две проблемы:

  1. я хочу LifeStyleScoped только для этого конкретного случая, а не вообще
  2. я не могу найти ни одного примера того, как использовать LifeStyleScoped внутри контроллера.

Любые другие предложения или примеры кода будут оценены по достоинству.

Редактировать: Я не упоминал, что unitofwork не вводится явно в контроллер. Контроллеры вводят две службы, и эти службы используют единицу работы, которая создается через виндзорский замок.

public class SomeController : ApiController 
{ 
    private readonly IService _service1; 
    private readonly IService _service2; 

    public SomeController (IService service1, IService service2) 
    { 
     _service1= service1; 
     _service2= service2; 
    } 

    public IHttpActionResult SomeAction() 
    { 
     _service1.DoSomething(); 
     _service2.DoSomething(); 
    } 
} 

public Service : IService 
{ 
    public Service(IUnitOfWork uow) { 

    } 
} 

ответ

4

Если вы используете Castle.Windsor в приложении Web API, вы, вероятно, уже используют IDependencyResolver, которые можно подключить, чтобы использовать собственный масштаб Виндзора, подобный этому:

class WindsorDependencyResolver : IDependencyResolver 
{ 
    private readonly IWindsorContainer _container; 

    public WindsorDependencyResolver(IWindsorContainer container) 
    { 
     _container = container; 
    } 

    public object GetService(Type t) 
    { 
     return _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null; 
    } 

    public IEnumerable<object> GetServices(Type t) 
    { 
     return _container.ResolveAll(t).Cast<object>().ToArray(); 
    } 

    public IDependencyScope BeginScope() 
    { 
     return new WindsorDependencyScope(_container); 
    } 

    public void Dispose() 
    { 
    } 
} 

class WindsorDependencyScope : IDependencyScope 
{ 
    private readonly IWindsorContainer _container; 
    private readonly IDisposable _scope; 

    public WindsorDependencyScope(IWindsorContainer container) 
    { 
     _container = container; 
     _scope = container.BeginScope(); 
    } 

    public object GetService(Type t) 
    { 
     return _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null; 
    } 

    public IEnumerable<object> GetServices(Type t) 
    { 
     return _container.ResolveAll(t).Cast<object>().ToArray(); 
    } 

    public void Dispose() 
    { 
     _scope.Dispose(); 
    } 
} 

Web API создаст новую область для каждого запроса и удалит ее после завершения запроса. Поэтому, если вы используете эту технику, вы, вероятно, можете покончить с вашим LifestylePerWebRequest и просто пойти с LifestyleScoped, тем самым решив необходимость иметь две регистрации для каждого компонента.

Вторая задача заключается в следующем: как вы получаете вторую независимую единицу работы? Очевидно, что все обязательные и необязательные зависимости между контроллерами будут неявно разрешаться в пределах одного и того же ILifetimeScope, поэтому просто наивно объявить зависимость конструктора для второго IUnitOfWork не будет работать.

Есть несколько способов, вы можете сделать это, но если вы готовы жить с Service Locator Anti-Pattern вы можете просто создать свой собственный масштаб, так как:

public class SomeController : ApiController 
{ 
    private readonly IUnitOfWork _uow; 

    public SomeController (IUnitOfWork uow) 
    { 
     _uow = uow; 
    } 

    public IHttpActionResult SomeAction() 
    { 
     // Get a second UoW 
     using (var separatelyScopedResolver = GlobalConfiguration.Configuration.DependencyResolver.BeginScope()) 
     { 
      var anotherUoW = separatelyScopedResolver.GetService(typeof (IUnitOfWork)); 
      // Do something with this UoW... 
      anotherUoW.Save(); 
     } 

     // Do something with the default UoW... 
     _uow.Save(); 

     // Et cetera... 
    } 
} 
+0

Спасибо за предложения и код образцы. Я дам ему попробовать, как только я снова буду работать (это завтра), и я дам вам знать, как это работает для меня. – Khronos

+0

, пожалуйста, проверьте мои изменения. Вторая услуга, потому что вводится в контроллер, уже использует первый блок работы, который создал замок. Какие-либо предложения? – Khronos

+0

Простейшим решением было бы удалить вторую службу из конструктора и получить ее динамически в вызове GetService выше (вместо IUnitOfWork). В качестве альтернативы, если это не одно требование или вы хотите избежать Locator, вы можете создать собственный IScopeAccessor http://docs.castleproject.org/(S(sgex5w45y1suwnquu1b0gd55))/Print.aspx?NS=Windsor&Page=Windsor.Implementing % 20custom% 20scope & AspxAutoDetectCookieSupport = 1 –