[英]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. 我要注入
Service1
或Service2
到我的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.