Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The proper way to do Dependency Injection in a Windows Client (WPF) Application

I am used to IoC/DI in web applications - mainly Ninject with MVC3. My controller is created for me, filled in with all dependencies in place, subdependencies etc.

However, things are different in a thick client application. I have to create my own objects, or I have to revert to a service locator style approach where I ask the kernel (probably through some interface, to allow for testability) to give me an object complete with dependencies.

However, I have seen several places that Service Locator has been described as an anti-pattern.

So my question is - if I want to benefit from Ninject in my thick client app, is there a better/more proper way to get all this?

  • Testability
  • Proper DI / IoC
  • The least amount of coupling possible

Please note I am not just talking about MVVM here and getting view models into views. This is specifically triggered by a need to provide a repository type object from the kernel, and then have entities fetched from that repository injected with functionality (the data of course comes from the database, but they also need some objects as parameters depending on the state of the world, and Ninject knows how to provide that). Can I somehow do this without leaving both repositories and entities as untestable messes?

If anything is unclear, let me know. Thanks!

EDIT JULY 14th

I am sure that the two answers provided are probably correct. However, every fiber of my body is fighting this change; Some of it is probably caused by a lack of knowledge, but there is also one concrete reason why I have trouble seeing the elegance of this way of doing things;

I did not explain this well enough in the original question, but the thing is that I am writing a library that will be used by several (4-5 at first, maybe more later) WPF client applications. These applications all operate on the same domain model etc., so keeping it all in one library is the only way to stay DRY. However, there is also the chance that customers of this system will write their own clients - and I want them to have a simple, clean library to talk to. I don't want to force them to use DI in their Composition Root (using the term like Mark Seeman in his book) - because that HUGELY complicates things in comparison to them just newing up a MyCrazySystemAdapter() and using that.

Now, the MyCrazySystemAdapter (name chosen because I know people will disagree with me here) needs to be composed by subcomponents, and put together using DI. MyCrazySystemAdapter itself shouldn't need to be injected. It is the only interface the clients needs to use to talk to the system. So a client happily should get one of those, DI happens like magic behind the scenes, and the object is composed by many different objects using best practices and principles.

I do realize that this is going to be a controversial way of wanting to do things. However, I also know the people who are going to be clients of this API. If they see that they need to learn and wire up a DI system, and create their whole object structure ahead of time in their application entry point (Composition Root), instead of newing up a single object, they will give me the middle finger and go mess with the database directly and screw things up in ways you can hardly imagine.

TL;DR: Delivering a properly structured API is too much hassle for the client. My API needs to deliver a single object - constructed behind the scenes using DI and proper practices - that they can use. The real world some times trumps the desire to build everything backwards in order to stay true to patterns and practices.

like image 452
Rune Jacobsen Avatar asked Jul 09 '11 11:07

Rune Jacobsen


2 Answers

I suggest to have a look at MVVM frameworks like Caliburn. They provide integration with IoC containers.


Basically, you should build up the complete application in your app.xaml. If some parts need to be created later because you do not yet know everything to create them at startup then inject a factory either as interface (see below) or Func (see Does Ninject support Func (auto generated factory)?) into the class that needs to create this instance. Both will be supported natively in the next Ninject release.

e.g.

public interface IFooFactory { IFoo CreateFoo(); }
public class FooFactory : IFooFactory
{
    private IKernel kernel;
    FooFactory(IKernel kernel)
    {
        this.kernel = kernel;
    }

    public IFoo CreateFoo()
    {
        this.kernel.Get<IFoo>();
    }
}

Note that the factory implementation belongs logically to the container configuration and not to the implementation of your business classes.

like image 51
Remo Gloor Avatar answered Oct 01 '22 16:10

Remo Gloor


I don't know anything about WPF or MVVM, but your question is basically about how to get stuff out of the container without using a Service Locator (or the container directly) all over the place, right?
If yes, I can show you an example.

The point is that you use a factory instead, which uses the container internally. This way, you are actually using the container in one place only.

Note: I will use an example with WinForms and not tied to a specific container (because, as I said, I don't know WPF...and I use Castle Windsor instead of NInject), but since your basic question is not specificaly tied to WPF/NInject, it should be easy for you to "port" my answer to WFP/NInject.

The factory looks like this:

public class Factory : IFactory
{
    private readonly IContainer container;

    public Factory(IContainer container)
    {
        this.container = container;
    }

    public T GetStuff<T>()
    {
        return (T)container.Resolve<T>();
    }
}

The main form of your app gets this factory via constructor injection:

public partial class MainForm : Form
{
    private readonly IFactory factory;

    public MainForm(IFactory factory)
    {
        this.factory = factory;
        InitializeComponent();  // or whatever needs to be done in a WPF form
    }
}

The container is initialized when the app starts, and the main form is resolved (so it gets the factory via constructor injection).

static class Program
{
    static void Main()
    {
        var container = new Container();
        container.Register<MainForm>();
        container.Register<IFactory, Factory>();
        container.Register<IYourRepository, YourRepository>();

        Application.Run(container.Resolve<MainForm>());
    }
}

Now the main form can use the factory to get stuff like your repository out of the container:

var repo = this.factory.GetStuff<IYourRepository>();
repo.DoStuff();

If you have more forms and want to use the factory from there as well, you just need to inject the factory into these forms like into the main form, register the additional forms on startup as well and open them from the main form with the factory.

Is this what you wanted to know?


EDIT:
Ruben, of course you're right. My mistake.
The whole stuff in my answer was an old example that I had lying around somewhere, but I was in a hurry when I posted my answer and didn't read the context of my old example carefully enough.

My old example included having a main form, from which you can open any other form of the application. That's what the factory was for, so you don't have to inject every other form via constructor injection into the main form.
Instead, you can use the factory to open any new form:

var form = this.factory.GetStuff<IAnotherForm>();
form.Show();

Of course you don't need the factory just to get the repository from a form, as long as the repository is passed to the form via constructor injection.
If your app consists of only a few forms, you don't need the factory at all, you can just pass the forms via constructor injection as well:

public partial class MainForm : Form
{
    private readonly IAnotherForm form;

    // pass AnotherForm via constructor injection
    public MainForm(IAnotherForm form)
    {
        this.form = form;
        InitializeComponent();  // or whatever needs to be done in a WPF form
    }

    // open AnotherForm
    private void Button1_Click(object sender, EventArgs e)
    {
        this.form.Show();
    }
}

public partial class AnotherForm : Form
{
    private readonly IRepository repo;

    // pass the repository via constructor injection
    public AnotherForm(IRepository repo)
    {
        this.repo= repo;
        InitializeComponent();  // or whatever needs to be done in a WPF form

        // use the repository
        this.repo.DoStuff();
    }
}
like image 39
Christian Specht Avatar answered Oct 01 '22 14:10

Christian Specht