Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ninject binding based on string

Tags:

c#

ninject

I have a web service that's going to do things with some data being passed in (specifically InfoPath xml from a SharePoint doc library). I'm currently using Ninject to handle what form data "strategy" to load. Here's some code (question follows):

Web Service (Entry Point)

namespace Web.Services
{
    public bool AddForm(XmlDocument form, string formName)
    {
        IKernel kernel = new StandardKernel(new FormsModule());
        var ctx = kernel.Get<IPFormDataContext>(formName);

        return ctx.DoWork(form);
    }
}

Ninject Related Things

namespace Core.Modules
{
    public class FormsModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IPFormDataContext>().ToSelf().Named("FormA");
            Bind<IPFormDataContext>().ToSelf().Named("FormB");
            // Snip

            Bind<IPFormDataStrategy>().To<FormAStratgey>()
                .WhenParentNamed("FormA");
            Bind<IPFormDataStrategy>().To<FormBStrategy>()
                .WhenParentNamed("FormB");
            // Snip
        }
    }
}

Pattern Related Things

namespace Core.Forms
{
    public class IPFormDataContext
    {
        private IPFormDataStrategy _ipFormDataStrategy;

        public IPFormDataContext(IPFormDataStrategy strategy)
        {
            _ipFormDataStrategy = strategy;
        }

        public bool DoWork(XmlDocument form)
        {
            return _ipFormDataStrategy.DoWork(form);
        }
    }

    public abstract class IPFormDataStrategy
    {
        public abstract bool DoWork(XmlDocument form);
    }
}

namespace Core.Forms.FormStrategies
{
    class FormAStrategy : IPFormDataStrategy
    {
        public override bool DoWork(XmlDocument form)
        {
            // Deserialize form using (xsd.exe generated) FormAData
            // and perform some operation on the resulting data.
            return resultOfWork;
        }
    }
}

FormBStrategy is much the same, as is the 7 other strategies I didn't list. I'm trying to find a way to pass in a form xml to the webservice and call the correct form deserialization based on the form type that's coming in.

The code above "works"; but it feels like I'm doing some sort of service location in Ninject, which from what I'm reading is a bad thing. But I can't think of a proper way to accomplish this. I'm not dead set on using Ninject, or any IOC/DI framework for that matter.

Is what I'm doing ... wrong? Could I get pointed in the right direction?

like image 782
Slipfish Avatar asked Aug 05 '13 22:08

Slipfish


1 Answers

If the classes that you present in your example code are accurate (i.e. there is not a bunch more methods and properties). Then the simplest possible solution might work, and you can get rid of a number of classes / dependencies on classes.

A simple solution, that does not rely on a framework/container would be:

public static class FormsProcessing
{
    private static ConcurrentDictionary<string, Func<FormProcessor>> _registeredProcessors = new ConcurrentDictionary<string, Func<FormProcessor>>();

    public delegate bool FormProcessor(XmlDocument form);

    public static void RegisterProcessor(string formKey, Func<FormProcessor> formsProcessorFactory)
    {
        _registeredProcessors.AddOrUpdate(formKey, formsProcessorFactory, (k, current) => formsProcessorFactory);
    }

    public static FormProcessor GetProcessorFor(string formKey)
    {
        Func<FormProcessor> processorFactory;
        if (_registeredProcessors.TryGetValue(formKey, out processorFactory);
            return processorFactory();
        return null;
    }

    public static bool Process(string formKey, XmlDocument form)
    {
        var processor = GetProcessorFor(formKey);
        if (null == processor)
            throw new Exception(string.Format("No processor for '{0}' forms available", formKey));
        return processor(form);
    }
}

Usage:

namespace Web.Services
{
    public class MyServiceClass
    {
        public bool AddForm(XmlDocument form, string formName)
        {
            return FormsProcessing.Process(formName, form);
        }
    }
}

It is simple and explicit, and does not need or expose any dependency on some structure of IPFormDataContext and IPFormDataStrategy classes. The only explicit dependency you have is on a delegate that has the FormProcessor signature.

Similar to a container, you will need to perform the registrations somewhere:

FormsProcessing.RegisterProcessor("FormA", () => new FormAStrategy().DoWork);
FormsProcessing.RegisterProcessor("FormB", () => new FormBStrategy().DoWork);

Alternatively it would be easy to add some form of (convention based) auto registration by scanning assemblies for the convention (e.g. an interface signature).

like image 92
Alex Avatar answered Sep 30 '22 18:09

Alex