基于客户的自定义实现的 Castle Windsor 依赖注入

Castle Windsor Dependency Injection with custom implementation based on customer

我们有这样一种情况,我们使用 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 语句,它只是在每个操作中根据客户名称选择要使用的服务)

我们的尝试

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

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"));

问题是,我们必须为每个客户提供定制服务,即使他们不需要,这会变得很笨重。否则解析会抛出一个异常,指出命名依赖不存在。

你可以在这里使用工厂:

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 语句中,您只声明特殊情况,所有其他情况都由 default 子句声明。

我建议在不同的项目中保留客户特定的实现。例如,您可以有一个名为 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

您可以阅读更多关于定制温莎注册的信息here and here