【问题标题】:web api controller and castle windsor lifestyleweb api 控制器和城堡温莎生活方式
【发布时间】:2015-06-03 17:37:52
【问题描述】:

在一个 web api 控制器函数中,我使用两个服务,因为它们做独立的事情,我希望它们使用不同的工作单元(事务)。

所有必要的组件(工作单元、存储库)都通过 Castle Windsor 与 LifestylePerWebRequest 注入。

据我了解,解决方案是使用 LifeStyleScoped,但我有两个问题:

  1. 我只希望 LifeStyleScoped 用于这种特殊情况,而不是一般情况
  2. 我找不到一个如何在控制器中使用 LifeStyleScoped 的示例。

任何其他建议或代码示例将不胜感激。

编辑:我没有提到 unitofwork 没有显式注入控制器中。在控制器中注入了两个服务,这些服务使用通过 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) {

    }
}

【问题讨论】:

    标签: c# asp.net-web-api castle-windsor


    【解决方案1】:

    如果您在 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 声明构造函数依赖项是行不通的。

    有多种方法可以做到这一点,但如果您准备好使用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...
        }
    }
    

    【讨论】:

    • 感谢您的建议和代码示例。我会在我再次上班(即明天)时尝试一下,我会告诉你这对我有用。
    • 请检查我的编辑。第二个服务,因为是在控制器中注入的,已经使用了第一个工作单元城堡创建的。有什么建议吗?
    • 最简单的解决方案是从构造函数中删除第二个服务并在上面的 GetService 调用中动态检索它(而不是 IUnitOfWork)。或者,如果这不是一次性要求,或者您想避免使用服务定位器,您可以创建自定义 IScopeAccessor docs.castleproject.org/(S(sgex5w45y1suwnquu1b0gd55))/…
    • 您好 Phil,我正在按照您的建议使用此代码,但它仍然使用相同的服务。有任何想法吗? foreach (var saleDTO in saleDTOs) { using (var separateScopedResolver = GlobalConfiguration.Configuration.DependencyResolver.BeginScope()) { var service = (ISaleCreationService)separatelyScopedResolver.GetService(typeof(ISaleCreationService)); response.Add(service.MakeOneSale(saleDTO)); } }
    • 我建议发布另一个问题,说明您如何注册工作单元和服务以及您的服务代码以及您想要实现的目标。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-14
    • 1970-01-01
    相关资源
    最近更新 更多