The correct way to inject dependencies in a Windows Client application (WPF)

I use IoC / DI in web applications - mostly Ninject with MVC3. My controller is made for me, filled with all the in-place dependencies, sub-dependencies, etc.

However, in a thick client application, everything is different. I have to create my own objects, or I need to return to the service locator style approach, where I ask the kernel (perhaps through some interface to allow verification) to provide me with an object with dependencies.

However, I saw several places where the Service Locator was described as an anti-pattern.

So my question is: if I want to capitalize on Ninject in my thick client application, is there a better / more correct way to get all this?

  • Testing
  • Correct DI / IoC
  • The smallest possible number of links

Please note: I'm not just talking about MVVM here and getting view models in views. This is specially caused by the need to provide an object of the repository type from the kernel, and then get entities from this repository, embedded with functionality (of course, the data 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 this). Can I somehow do this without leaving both repositories and entities as unchecked messes?

If something is unclear, let me know. Thanks!

EDIT JULY 14th

I am sure that the two answers provided are probably correct. However, every layer of my body is struggling with this change; Some of them are probably caused by a lack of knowledge, but there is also one specific reason why I have problems with the elegance of this way of doing things;

I did not explain this well enough in the original question, but the fact is that I am writing a library that will be used by several (4-5, and maybe later) WPF client applications. All these applications work on the same domain model, etc. Therefore, keeping it in the same library is the only way to stay dry. However, there is a chance that clients of this system will write their clients - and I want them to have a simple, clean library with which to talk. I don’t want to force them to use DI in their root of the composition (using a term such as Mark Seeman in his book) - because this HUGELY complicates things compared to them just by creating MyCrazySystemAdapter () and using this.

Now MyCrazySystemAdapter (a name chosen because I know that people will not agree with me here) should be compiled by subcomponents and compiled using DI. MyCrazySystemAdapter itself does not need to be entered. This is the only interface that customers should use to communicate with the system. Thus, the client should happily receive one of them, DI happens like magic behind the scenes, and the object consists of many different objects, using best practices and principles.

I understand that this will be a controversial way of wanting to do something. However, I also know the people who will be clients of this API. If they see that they need to learn how to connect the DI system and create their entire object structure in advance at their application entry point (Root Composition), instead of creating a new object, they will give me the middle finger and go directly to the database and spin things as you can hardly imagine.

TL; DR: Providing a properly structured API is too much trouble for the client. My API should provide a single object created behind the scenes using DI and the correct methods that they can use. The real world is several times greater than the desire to build everything back to stay true to patterns and practices.

+8
dependency-injection ninject service-locator
source share
2 answers

I suggest taking a look at MVVM frameworks such as Caliburn. They provide integration with IoC containers.


Basically, you should create a complete application in your app.xaml. If you need to create some parts later, because you still don’t know everything to create them at startup, then enter factory either as an interface (see below) or in Func (see Does Ninject support Func (automatically generated factory)? ) to the class that this instance should create. Both will be supported initially in the next release of Ninject.

eg.

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 is logically related to the container configuration, and not to the implementation of business classes.

+5
source share

I don’t know anything about WPF or MVVM, but your question is mainly about how to extract material from a container without using a service locator (or container directly) everywhere, right? If so, I can show you an example.

The fact is that instead you are using a factory that uses a container inside. Thus, you actually use the container in only one place.

Note. I will use the WinForms example and it is not bound to a specific container (because, as I said, I don’t know WPF ... and I use Castle Windsor instead of NInject), but since your main question is not WPF / NInject specific, it should be easy for you to “migrate” my response to WFP / NInject.

factory is as follows:

 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 application gets this factory through the constructor installation:

 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 application starts, and the main form is allowed (therefore, it gets the factory through 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 factory to get material similar to your repository from the container:

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

If you have other forms and you want to use the factory from there, you just need to enter the factory in these forms, as in the main form, register additional forms at startup and open them from the main form using factory.

Is this what you wanted to know?


EDIT:
Ruben, of course, you're right. My mistake.
All the material in my answer was an old example of me lying somewhere, but I was in a hurry when I sent my answer and did not carefully read the context of my old example.

In my old example, the main form was specified from which you can open any other application form. This is what the factory was for, so you don’t need to inject every other form through the constructor injection into the main form.
Instead, you can use factory to open any new form:

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

Of course, you do not need a factory to get the repository from the form if the repository is passed to the form through constructor injection.
If your application consists of only a few forms, you do not need a factory at all, you can simply pass the forms through the constructor injection:

 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(); } } 
+1
source share

All Articles