Castle Windsor依赖注入,基于客户定制实施

时间:2016-01-12 22:39:09

标签: c# dependency-injection castle-windsor

我们有一种情况,我们使用Castle Windsor依赖注入将IService注入到wep api控制器中,如下所示:

public FooController : ApiController
{
    private IService _service;

    public FooController(IService service)
    {
        _service = service;
    }
}

注册这样的服务:

 container.Register(Component.For<IService>().ImplementedBy<Service>().LifestyleTransient());

服务类似于:

public Service : IService
{
    public virtual string Logic()
    {
        return "Service Logic";
    }
}

问题是,一些客户的业务逻辑与基础业务逻辑完全不同,因此我们需要使用Service的另一种实现来满足客户的需求。

因此,如果有2个客户(Customer1和Customer2),则Customer1应使用默认的Service,但Customer2应使用名为Customer2Service的自定义实现,该实现将从Service继承并覆盖像这样需要:

public Customer2Service : Service
{
    public override string Logic()
    {
        var response = base.Logic();

        return "Customer 2 Logic " + response;
    }
}

问题

  1. 首先,这似乎是解决客户特定代码这一特定问题的正确架构吗?
  2. 有没有办法使用依赖注入来解决这个问题? (我们不想要一个巨大的switch语句,只根据每个操作中的客户名称选择使用哪种服务)
  3. 我们尝试了什么

    我们尝试将依赖注入更改为基于属性,如下所示:

    public FooController : ApiController
    {
        private IService _service;
    
        public FooController()
        {
        }
    
        public HttpResponseMessage FooAction(string customer)
        {
            _service = container.Resolve<IService>(customer);
    
            ...
        }
    }
    
    container.Register(Component.For<IService>().ImplementedBy<Service>().LifestyleTransient());
    container.Register(Component.For<IService>().ImplementedBy<Customer2Service>().LifestyleTransient().Named("Customer2"));
    

    问题在于,我们必须为每个客户提供定制服务,即使他们不需要它也可能会变得笨拙。否则,解决方案会抛出一个异常,说明命名的依赖关系不存在。

2 个答案:

答案 0 :(得分:0)

您可以在这里使用工厂:

class ServiceFactory : IServiceFactory
{
    public IService GetService(string customer)
    {
        switch (customer)
        {
            case "special":
                return container.Resolve<IService>("special");
            default:
                return container.Resolve<IService>("default");
        }
    }
}

然后您可以将工厂注入您的控制器并从工厂获得IService。 在switch语句中,您只声明特殊情况,所有其他情况都是默认子句。

答案 1 :(得分:0)

我建议将客户特定的实施保留在不同的项目中。例如,您可以拥有一个名为CoreServices且具有基本功能的项目:

namespace CoreServices.Services
{
    public interface IServiceA
    {
        string Test { get; }
    } 

    public interface IServiceB
    {
        string Test { get; }
    }

    public class ServiceA : IServiceA
    {
        public virtual string Test 
        {
            get { return "CoreServices: ServiceA"; } 
        }
    }

    public class ServiceB : IServiceB
    {
        public virtual string Test
        {
            get { return "CoreServices: ServiceB"; }
        }
    }
}

另一个项目名称CostomerAServices,其中只定制了ServiceA

namespace CustomerAServices.Services
{
    public class ServiceA : CoreServices.Services.ServiceA
    {
        public override string Test
        {
            get { return "CustomerAServices: ServiceA"; }
        }
    }
}

然后,通过运行此代码,您将获得所需内容:

string customizedAssemblyName = "CustomerAServices"; // Get from config
container.Register(Classes
    .FromAssemblyNamed(customizedAssemblyName)
    .InNamespace(customizedAssemblyName + ".Services")
    .WithServiceAllInterfaces());

container.Register(Classes
   .FromAssemblyNamed("CoreServices")
   .InNamespace("CoreServices.Services")
   .WithServiceAllInterfaces());

首先,您将注册自定义版本和基本版本。因此,如果您运行此代码:

var serviceA = container.Resolve<IServiceA>();
System.Console.WriteLine(serviceA.Test);

var serviceB = container.Resolve<IServiceB>();
System.Console.WriteLine(serviceB.Test);

输出将是:

CustomerAServices: ServiceA
CoreServices: ServiceB

您可以详细了解自定义温莎注册herehere