简体   繁体   English

在运行时将命令的不同实现注入命令

[英]Inject different implementations of an Interface to a command at runtime

I have an interface in my project that 2 classes implement it: 我的项目中有一个接口,有2个类实现它:

public interface IService
{
   int DoWork();
}

public class Service1:IService
{
    public int DoWork()
    {
       return 1;
    }
}  

public class Service2:IService
{
    public int DoWork()
    {
       return 2;
    }
}    

I have a command handler that depends on IService too: 我有一个依赖于IService的命令处理程序:

public CommandHandler1:ICommandHandler<CommandParameter1>
{
     IService _service;  
     public CommandHandler1(IService service)
     {
          _service = service
     }  
     public void Handle()
     { 
          //do something
          _service.DoWork();
          //do something else 
     }
}

public interface ICommandHandler<TCommandParameter> 
                 where TCommandParameter :ICommandParameter
{
    void Handle(TCommandParameter parameter);
}
public interface ICommandParameter
{
}

I want to inject Service1 or Service2 to my CommandHandler1 based on user selection. 我要注入Service1Service2到我的CommandHandler1基于用户选择。 suppose that I have an enum and user could select a value from it: 假设我有一个enum ,用户可以从中选择一个值:

public enum Services
{  
    Service_One,
    Service_Two 
}

If user selects Service_One I want inject Service1 to my command handler and If he selects Service_Two I want inject Service2 to the command handler. 如果用户选择Service_One我想将Service1注入我的命令处理程序,如果他选择Service_Two我想将Service2注入命令处理程序。

I know that I can use named instances, and then call ObjectFactory.GetInstance<IService>().Named("Service1") for example, but Is there any way to implement this by StructureMap and prevent using Service Locator pattern? 我知道我可以使用命名实例,然后调用ObjectFactory.GetInstance<IService>().Named("Service1") ,但有没有办法通过StructureMap实现它并防止使用Service Locator模式?

Prevent building your object graphs using runtime conditions. 使用运行时条件阻止构建对象图。 Object graphs should be fixed. 应该修复对象图。 Use runtime decisions to determine the path through the object graph. 使用运行时决策来确定通过对象图的路径。

What you seem to be missing here is an abstraction that allows delegating the request to the correct IService implementation; 你似乎在这里缺少的是一个抽象,它允许将请求委托给正确的IService实现; let's call it IServiceDispatcher : 我们称之为IServiceDispatcher

interface IServiceDispatcher {
    int DoWork(Services data);
}

sealed class ServiceDispatcher : IServiceDispatcher {
    private readonly IService service1;
    private readonly IService service2;

    // NOTE: Feel free to inject the container here instead, as long as
    // this class is part of your composition root.
    public ServiceDispatcher(IService service1, IService service2) {
        this.service1 = service1;
        this.service2 = service2;
    }
    public int DoWork(Services data) {
        return this.GetService(data).DoWork();
    }

    private IService GetService(Services data) {
        switch (data) {
            case Services.Service_One: return this.service1;
            case Services.Service_Two: return this.service2;
            default: throw new InvalidEnumArgumentException();
        }
    }
}

Now your CommandHandler1 can depend on IServiceDispatcher : 现在你的CommandHandler1可以依赖于IServiceDispatcher

public CommandHandler1 : ICommandHandler<CommandParameter1> {
     private readonly IServiceDispatcher serviceDispatcher;
     public CommandHandler1(IServiceDispatcher serviceDispatcher) {
          this.serviceDispatcher = serviceDispatcher;
     }  

     public void Handle(CommandParameter1 commandParameter) { 
          //do something
          this.serviceDispatcher.DoWork(commandParameter.Service);
          //do something else 
     }
}

Do note that IServiceDispatcher is a really ugly name that technically describes what's going on. 请注意, IServiceDispatcher是一个非常难看的名称,从技术上描述了正在发生的事情。 This is a bad idea, because the interface should functionally describe what you want. 这是一个坏主意,因为界面应该在功能上描述你想要的东西。 But since you didn't supply any domain-specific context to your question, this is the best name I can come up with ;-) 但是,由于您没有为您的问题提供任何特定于域的上下文,这是我能提出的最佳名称;-)

This may not be the best approach but it should work. 这可能不是最好的方法,但应该有效。

Add a property to each service that specifies the ServiceTypes it represents: 向每个指定其所代表的ServiceTypes服务添加属性:

public interface IService
{
    public ServiceTypes Type { get; }

    public int DoWork();
}

Implement the property in each class: 在每个类中实现属性:

public class Service1 : IService
{
    public ServiceTypes Type { get { return ServiceTypes.Service_One; } }

    public void DoWork()
    {
        return 1;
    }
}

Then, register all the implementations of your service in the container and inject them in your handler. 然后,在容器中注册服务的所有实现,并将它们注入到处理程序中。 From there, select the implementation based on a property from the command: 从那里,根据命令中的属性选择实现:

container.For<IService>().Use<Service1>("service1");
container.For<IService>().Use<Service2>("service2");

Add the required ServiceType in the command class: 在命令类中添加所需的ServiceType

public class Command1
{
    // Other command properties

    public ServiceTypes Service { get; set; }
}

And in the command handler: 并在命令处理程序中:

public class CommandHandler : ICommandHandler<Command1>
{
    private readonly IEnumerable<IService> _services;

    public CommandHandler(IService[] services)
    {
        _servies = services;
    }

    public void Handle(Command1 command)
    {
        var service = _services.Single(s => s.Type == command.Service);
        service.DoWork();
    }
}

I would create a factory, that has reference to IContext and uses it to resolve the concrete service dependency. 我会创建一个工厂,它引用IContext并使用它来解决具体的服务依赖。

public interface ICommandFactory
{
    Command1 CreateCommand(Services serviceType);
}

public class CommandFactory : ICommandFactory
{
    private readonly IContext _context;

    public CommandFactory(IContext context)
    {
        _context = context;
    }

    public Command1 CreateCommand(Services serviceType)
    {
        IService service;
        switch(serviceType)
        {
            case Services.Service_One: service = _context.GetInstance<Service1>();
                break;
            case Services.Service_Two: service = _context.GetInstance<Service2>();
                break;
            default:
                throw new ArgumentOutOfRangeException("serviceType", serviceType, null);
        }

        return new Command1(service);
    }
}

And then, you register and use it like this: 然后,您注册并使用它:

var container = new Container(_ =>
{
    _.For<ICommandFactory>().Use(context=>new CommandFactory(context));
});

var factory = container.GetInstance<ICommandFactory>();

var command = factory.CreateCommand(Services.Service_One);
command.Handle();

First, the responsibility of picking the right service is separate from the command itself. 首先,选择正确服务的责任与命令本身是分开的。 It also allows command to have different dependencies on top of the service itself, just call _context.GetInstance<TypeOfDependency>() . 它还允许命令在服务本身之上具有不同的依赖关系,只需调用_context.GetInstance<TypeOfDependency>()

About this being same as Service Locator. 关于这与服务定位器相同。 The major problem of service locator is that it hides dependencies. 服务定位器的主要问题是它隐藏了依赖关系。 This is not case here, because the one calling the command explicitly states dependency on the CommandFactory class. 这不是这种情况,因为调用该命令的那个显式地声明了对CommandFactory类的依赖。 And if interface is introduced for the factory class (turning it into AbstractFactory pattern), then the implementation itself can become part of the dependency resolution strategy. 如果为工厂类引入了接口(将其转换为AbstractFactory模式),则实现本身可以成为依赖项解析策略的一部分。 Eg. 例如。 it will be in same place as the dependency framework itself. 它将与依赖框架本身位于同一位置。 Thanks to this there is no Service Locator (static or interface) within the domain model. 由于这个原因,域模型中没有服务定位器(静态或接口)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在多个构造函数参数中注入具有相同接口的不同实现 - Inject different implementations that have same interface in multiple constructor parameters 如何在没有条件/上下文DI的情况下将不同的实现注入相同的接口? - How to inject different implementations to same interface without conditional/context DI? 具有不同类型的接口的实现? - Implementations of an Interface with Different Types? 将不同的实现注入相同的接口,然后在正确的项目/程序集中选择正确的实现 - Inject different implementations into same interface then pick up right implementation in right project / assembly 如何将一个接口的多个实现注入一个实现中 - How to Inject multiple Implementations of an Interface into one Implementation DI:不同控制器的相同接口的不同实现 - DI: Different implementations of the same interface for the different controllers 使用Autofac将接口的不同实现注入到不同的控制器中 - Injecting different implementations of an interface into different controllers with Autofac 温莎城堡:我如何将接口的所有实现注入ctor? - Castle Windsor: How do I inject all implementations of interface into a ctor? 使用简单注入器将两个特定的接口实现注入到构造函数中 - Inject two specific interface implementations into constructor with Simple Injector 如何从公共接口注入Spring.NET的多个实现? - How to inject with Spring.NET multiple implementations from common interface?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM