Web API控制器和Castle Windsor生命周期

4
在Web API控制器函数中,我使用了两个服务,由于它们执行的是独立的任务,我希望它们使用不同的工作单元(事务)。
所有必要的组件(工作单元、仓储库)都通过Castle Windsor进行注入,并采用LifestylePerWebRequest模式。
据我所知,解决方案是使用LifeStyleScoped,但我有两个问题:
1. 我只想针对这个特定情况使用LifeStyleScoped,而不是普遍使用。 2. 我找不到如何在控制器内使用LifeStyleScoped的单一示例。
如果有其他建议或代码示例将不胜感激。
编辑:我没有提到工作单元并没有在控制器中显式注入。两个服务被注入到控制器中,并且这些服务使用通过Castle Windsor创建的工作单元。
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) {

    }
}
1个回答

4
如果您正在Web API应用程序中使用Castle.Windsor,您可能已经在使用IDependencyResolver,可以将其连接到使用Windsor自己的范围,类似于以下内容:
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的构造函数依赖关系将无法工作。
有许多方法可以做到这一点,但如果您准备好接受服务定位器反模式,您可以像这样创建自己的范围:
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...
    }
}

谢谢您的建议和代码示例。我会在明天上班后尽快尝试,并告诉您它对我是否有效。 - Vagelis Ouranos
第二个服务已经被注入到控制器中,因此它已经使用了Castle创建的第一个工作单元。有什么建议吗? - Vagelis Ouranos
最简单的解决方案是从构造函数中删除第二个服务,并在上面的GetService调用中动态检索它(而不是IUnitOfWork)。或者,如果这不是一次性要求,或者您想避免服务定位器,则可以创建自定义IScopeAccessor http://docs.castleproject.org/(S(sgex5w45y1suwnquu1b0gd55))/Print.aspx?NS=Windsor&Page=Windsor.Implementing%20custom%20scope&AspxAutoDetectCookieSupport=1 - Phil Degenhardt
你好Phil,我按照你的建议使用了这段代码,但它仍然使用相同的服务。有什么想法吗?foreach (var saleDTO in saleDTOs) { using (var separatelyScopedResolver = GlobalConfiguration.Configuration.DependencyResolver.BeginScope()) { var service = (ISaleCreationService)separatelyScopedResolver.GetService(typeof(ISaleCreationService)); responses.Add(service.MakeOneSale(saleDTO)); } } - Vagelis Ouranos
我建议您发布另一个问题,展示您如何注册工作单元和服务以及您正在尝试实现什么目标。 - Phil Degenhardt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接