Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject different implementations of an Interface to a command at runtime

I have an interface in my project that 2 classes implement it:

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:

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. suppose that I have an enum and user could select a value from it:

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.

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?

like image 810
Masoud Avatar asked Aug 31 '15 06:08

Masoud


People also ask

Can we inject dependency in interface?

interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency.

What is dependency injection and why?

In software engineering, dependency injection is a design pattern in which an object or function receives other objects or functions that it depends on. A form of inversion of control, dependency injection aims to separate the concerns of constructing objects and using them, leading to loosely coupled programs.

When should I use dependency injection?

More specifically, dependency injection is effective in these situations: You need to inject configuration data into one or more components. You need to inject the same dependency into multiple components. You need to inject different implementations of the same dependency.

Why dependency injection c#?

Dependency Injection (DI) is a software design pattern that allows us to develop loosely coupled code. DI is a great way to reduce tight coupling between software components. DI also enables us to better manage future changes and other complexity in our software. The purpose of DI is to make code maintainable.


2 Answers

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; let's call it 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:

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

like image 54
Steven Avatar answered Nov 16 '22 01:11

Steven


This may not be the best approach but it should work.

Add a property to each service that specifies the ServiceTypes it represents:

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:

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();
    }
}
like image 44
RePierre Avatar answered Nov 16 '22 02:11

RePierre