1

I had a class before I introduced dependency inject that looked like this:

public class Widget
{
    public Widget Create()
    {
        using (DataProvider dataProvider = new DataProvder())
        {
            dataProvider.AddInput("name", name);
            dataProvider.AddInput("path", path);
            dataProvider.AddInput("dateCreated", DateTime.UtcNow);

            using (var reader = _dataProvider.ExecuteReader("usp_widget_create"))
            {
                reader.Read();
                return new Widget(reader);
            }
        }
    }
}

Then I introduced dependency inject using Ninject and my class now looks like this so I don't create a dependency to the DataProvider class.

public class Widget
{
    IDataProvider _dataProvider;

    public Widget(IDataProvider dataProvider)
    {
        _dataProvider = dataProvider;
    }

    public Widget Create()
    {
        _dataProvider.AddInput("name", name);
        _dataProvider.AddInput("path", path);
        _dataProvider.AddInput("dateCreated", DateTime.UtcNow);

        using (var reader = _dataProvider.ExecuteReader("usp_widget_create"))
        {
            reader.Read();
            return new Widget(reader);
        }
    }
}

The problem with the new class is that one, the DataProvider won't get disposed and two, the properties of the DataProvider won't be reset. So if I call Create from the same object twice it will duplicate the parameters and throw an exception. I know I can explicitly fix the second issue by clearing the parameters, but I feel like there must be a more universal approach and I am missing something.

How should this situation be resolved?

Hungry Beast
  • 3,677
  • 7
  • 49
  • 75

1 Answers1

1

The pattern that very much you want to utilize is the Dependency Injection with Abstract Factory Pattern. This factory pattern allows you dynamically create instances whenever you required. And it is a very common thing to do so.

Define a Factory that allows you to create Provide instances.

public interface IProviderFactory
{
    T Create<T>();
}

The implementation can be simple as below.

public class ProviderFactory : IProviderFactory
{
    private readonly IKernel _kernel;

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

    public T Create<T>()
    {
        var instance = _kernel.Get<T>();
        return instance;
    }
}

Now change your Widget to accept the Factory instead of the IDataProvider

public class Widget
{
    readonly IProviderFactory _factory;

    public Widget(IProviderFactory factory)
    {
        _factory = factory;
    }

    public Widget Create()
    {
        var provider = _factory.Create<IDataProvider>();

        provider.AddInput("name", "name");
        provider.AddInput("path", "path");
        provider.AddInput("dateCreated", DateTime.UtcNow);

        //.....
    }
}

Note that each invocation of Create() would resolve a new instance of the IDataProvider.

var provider = _factory.Create();

The Kernal Registration is something like below..

        using (IKernel Kernel = new StandardKernel())
        {
            Kernel.Bind<IDataProvider>().To<DataProvider>();
            Kernel.Bind<IProviderFactory>()
             .To<ProviderFactory>().WithConstructorArgument(typeof(IKernel), Kernel);
            var widget = Kernel.Get<Widget>();
            widget.Create();
            widget.Create();
        }

Something along line should provide you the direction to a possible solution.

Community
  • 1
  • 1
Spock
  • 7,009
  • 1
  • 41
  • 60
  • My providers are in my business layer and my Ninject reference and declarations are at the top layer of my stack in the presenter (wpf app). I was hoping to keep all layers, except the topmost, from referencing Ninject. Is calling Ninject in all layers unavoidable? – Hungry Beast Nov 15 '14 at 13:41
  • 1
    Use the ninject.extensions.factory instead. `IProviderFactory` stays the same and is bound with `Bind().ToFactory()`. You can get rid of the implementation. Alternatively, use the same extension but just inject a `Func` – BatteryBackupUnit Nov 15 '14 at 17:22
  • That sounds like what I'm getting after but I think I'd need to see an example to fully understand. Sorry, I'm new to this. – Hungry Beast Nov 15 '14 at 19:02
  • 1
    @Chris your approach is good where you attempting keep layers DI container free. Even better if you abstract you container (i.e IContainer) so your presenter does not have direct reference to Ninject IMO. Another option would be to expose your DI container directly within a different project and have your provider implemented there. Either way I believe this approach would be able to fix the issue you currently have. – Spock Nov 15 '14 at 20:22