Ninject binding based on string

c#, ninject

Solution

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

Problem

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?

Original source

Related problems