V
V
vilix2016-12-22 15:41:59
C++ / C#
vilix, 2016-12-22 15:41:59

How to avoid IoC service locator anti-pattern in this example?

I have a test working code in C# + WCF + ninject (wcf is only involved here because the planned project is on wcf).
In short: I have a SimpleController entity in the database, I want to get the desired implementation of the IControllerProccesor interface by the SimpleController parameter.
Here's how it's currently implemented:

//program.cs
...
private static StandardKernel CreateKernel()
{
    var kernel = new StandardKernel();

    kernel.Bind<IDbConnectionFactory>().ToMethod(c =>
        new OrmLiteConnectionFactory(
            conString, 
            SqlServerDialect.Provider))
        .InSingletonScope();

    kernel.Bind<IControllerProccessor>().To<ControllerProccessor>()
        .WhenInjectedInto<HelloWorldService>().WithConstructorArgument(kernel);

    kernel.Bind<IControllerProccessor>().To<Vendor1Proccessor>()
        .Named("vendor1"); 

    kernel.Bind<IControllerProccessor>().To<Vendor2Proccessor>()
        .Named("vendor2"); 

    return kernel;
}
...

//IControllerProccessor.cs
public interface IControllerProccessor
{
    SimpleController Ctr { get; set; }
    bool sendMsg(string msg);
}

//Vendor1Proccessor.cs
public class Vendor1Proccessor : IControllerProccessor
{
    public SimpleController Ctr {get; set;}

    public bool sendMsg(string msg)
    {
        //specific to vendor code, for example calls to vendor1 SDK
        Console.WriteLine("Controller id: {0} vendor:{1} recivied msg: {2}",
            Ctr.Id,
            "Vendor1Class",
            msg);
        return true;
    }
}

//Vendor2Proccessor.cs
public class Vendor2Proccessor : IControllerProccessor
{
    public SimpleController Ctr { get; set; }

    public bool sendMsg(string msg)
    {
        //specific to vendor code, for example calls to vendor1 SDK
        Console.WriteLine("Controller id: {0} vendor:{1} recivied msg: {2}",
            Ctr.Id,
            "Vendor2Class",
            msg);
        return true;
    }
}

//ControllerProccessor.cs
public class ControllerProccessor : IControllerProccessor
{
    public SimpleController Ctr {get; set;}

    private readonly IKernel kernel;

    public ControllerProccessor(IKernel _kernel)
    {
        kernel = _kernel;
    }

    public bool sendMsg(string msg)
    {
        var param = new Ninject.Parameters.PropertyValue("Ctr", Ctr);
        return kernel.Get<IControllerProccessor>(Ctr.Vendor, param).sendMsg(msg);
    }
}

//HelloWorldService.cs
public class HelloWorldService : IHelloWorldService
{
    private readonly IDbConnectionFactory dbFactory;
    private readonly IControllerProccessor ctrProccessor;

    public HelloWorldService(IDbConnectionFactory _dbFactory, IControllerProccessor _ctrProccesor)
    {
        dbFactory = _dbFactory;
        ctrProccessor = _ctrProccesor;
    }

    public bool sendMsgToAllControllers(string msg)
    {
        var db = dbFactory.Open();
        var controllers = db.Select<SimpleController>();

        foreach(var ctr in controllers)
        {
            ctrProccessor.Ctr = ctr;
            ctrProccessor.sendMsg(msg);
        }
        db.Close();

        return true;
    }

}

//SimpleController.cs
[DataContract]
[Alias("SimpleController")]
public class SimpleController
{
    [AutoIncrement]
    [DataMember]
    public int? Id { get; set; }
    [DataMember]
    public string Vendor { get; set; }
}

The main question is whether the lines below are an anti-pattern and how can this whole thing be refactored in DI style? Or is this not an anti pattern in my case? After all, the ControllerProccesor is declared in the composition root and the kernel is used only in it, that is, the kernel, in fact, does not go beyond the composition root.
var param = new Ninject.Parameters.PropertyValue("Ctr", Ctr);
        return kernel.Get<IControllerProccessor>(Ctr.Vendor, param).sendMsg(msg);

The second question: can this be refactored somehow better? for example, so as not to write lines above in each method. Here, of course, you can create a new ControllerProccesor(ctr, kernel) for each ctr - but then the container goes beyond the composition root - although it may be more convenient, but it’s not in vain that they write that this is an anti pattern, which means that in the future it may come out.
In the future, all bindings will be in runtime, and IControllerProccesor implementations will be in runtime-connected .dlls (except for ControllerProccesor - which is essentially like a wrapper for all other processors) - i.e. in the end I will get a plugin system.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
D
Dmitry Kovalsky, 2016-12-22
@vilix

Make a factory for getting IControllerProccessor objects.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question