Dependency Injection in WinForms using Ninject and Entity Framework
You create composition root as one entry point for your resolutions. You pass INjectModule as a parameter so that you can configure it it tests differently. One of the benefits of Composition Root is that not all of your assemblies will depend on NInject and you will have one single point to change resolution logic. It is really a cool pattern, when you might change IoC container or introduce some dynamic interception in future.
public class CompositionRoot
{
private static IKernel _ninjectKernel;
public static void Wire(INinjectModule module)
{
_ninjectKernel = new StandardKernel(module);
}
public static T Resolve<T>()
{
return _ninjectKernel.Get<T>();
}
}
Your module would look like this
public class ApplicationModule : NinjectModule
{
public override void Load()
{
Bind(typeof(IRepository<>)).To(typeof(GenericRepository<>));
}
}
In main method you pass ApplicationModule
as a parameter and resolve Form1
and start it.
[STAThread]
static void Main()
{
CompositionRoot.Wire(new ApplicationModule());
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(CompositionRoot.Resolve<Form1>());
}
In Form1
constructor you pass required repository with specific closed generic parameters
public partial class Form1 : Form
{
private IRepository<Process> _processRepository;
public Form1(IRepository<Process> productionRepository)
{
this._processRepository = productionRepository;
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(_processRepository.ToString());
}
}
Your repositories could be very complex, but I won't add any functionality to them, instead of ToString()
method so that we could see if a dependency was resolved correctly. Note there are no attributes whatsoever on repositories.
public interface IRepository<T>
{
}
public class GenericRepository<T> : IRepository<T>
{
public override string ToString()
{
return "MyRepository with type : "+typeof(T).Name;
}
}
Now when you run your application, you will see, that all has wired up and message box shows an enclosed type as Process
![XN16](https://i.stack.imgur.com/a9eMS.jpg?s=256&g=1)
XN16
Senior Software Developer in the MIS and Fabrication EPoS Energy Supplier EPoS Apprentice industry.
Updated on June 03, 2022Comments
-
XN16 about 2 years
I am currently working on a project where we are converting our old DataSet, Stored Procedure WinForm application to use Entity Framework so new websites can access the same object model and repositories.
Anyway, I am trying to implement Dependency Injection into the Forms so that we can use mocked Repositories for unit testing. I am using Ninject for the the simple reason I have used it before in MVC websites, however trying to implement this in the WinForm application is proving to be problematic to say the least, hampered even more by the lack of information on DI in WinForms on the web.
So far I have created the Ninject Factory and repositories, but I haven't had much luck injecting the repositories into forms.
Therefore can anyone help me or make any suggestions?
Below I have parts of my code that might help:
Ninject Factory:
public class NinjectFactory : NinjectModule { private IKernel _ninjectKernel; public NinjectFactory() { _ninjectKernel = new StandardKernel(); } public override void Load() { _ninjectKernel.Bind(typeof(IRepository<>)).To(typeof(GenericRepository<>)); _ninjectKernel.Bind(typeof(IProductionRepository)).To(typeof(ProductionRepository)); } }
Form with repositories:
Public Class TaskForm Inherits BaseBusinessDialogForm Private _repository As TaskRepository Private _task As Production.Task = Nothing Public Sub New(ByVal task As Production.Task) InitializeComponent() _repository = New TaskRepository(ConnectString) If task.TaskID = 0 Then _task = task Else _task = _repository.GetByID(task.TaskID) End If MyBase.BusinessObject = _task Me.TaskBindingSource.DataSource = MyBase.BusinessObject End Sub
Class that launches the MDI form which holds the above form:
Dim kernel As IKernel = New StandardKernel(New NinjectFactory()) ''Dim kernel As IKernel = New StandardKernel(New NinjectFactoryTest()) mfrmMDI = kernel.Get(Of Forms.MDI)() Application.DoEvents() mfrmMDI.ShowDialog()
I understand that my question is a bit vague, but I'm not sure where the problem lies or what I need to complete.
Thanks very much
-
XN16 over 11 yearsThanks very much, I have managed to implement a version of your example successfully.
-
FMM over 11 yearsI think you're missing the point of what a Composition Root is intended to do. Your "Composition Root" is actually a service locator, which is often considered an antipattern. A true composition root is the "top level" object in your running application's object graph, and should be the only object that you explicitly ask for from the kernel (factories excluded). Everything else should be resolved via injection when the root is constructed, or via instantiation via a factory.
-
Ilya Ivanov over 11 yearsMy
CompositionRoot
is not a service locator.Form1
askIRepository<Process>
as constructor argument, notCompositionRoot
.CompositionRoot
resolves a complete objects graph at the start of the application. You don't askCompositionRoot
from the kernel, but instead you ask the top objects fromCompositionRoot
and it provides a complete resolved object graphs for you. -
FMM over 11 yearsThe method
public static T Resolve<T>() { }
makes it a service locator, especially considering there's no appreciable logic in yourCompositionRoot
class to handle composing modules. There's nothing to "compose" in your composition root, there's only one module. We're likely arguing semantics here. In a simple winforms app, the main form serves as the composition root, since it is the "root" of the object graph and all objects reachable from it are instantiated either by constructor injection or instantiation via a factory. -
Ilya Ivanov over 11 yearsDoes
public T Get<T>()
inIKernel
makes it a service locator? If it is static it doesn't mean, that it is service locator. I don't want to useForm1
as a composition root because it has it's own logic, which you don't want to touch in tests. Also, in tests you should also create yet another CompositionRoot? -
Ilya Ivanov over 11 yearsI think i've got what you mean. Mark Seeman provide similar example of Service Locator at his blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx. But there are key differences - SL uses dictionary, which is crutial, also in constructor you call SL.Resolve<T> while call I call Root in one place and nowhere else. If any misunderstandings left - please note
-
Zoltán Tamási about 11 years@IlyaIvanov, this looks nice in this simple scenario. I would like to know what you think a best practice would be if my "main" form would have to create sub forms, also using DI. How would I avoid calling the CompositionRoot.Resolve method there (which would be treated as the "evil service locator")? Thank you for your update.
-
wezzix over 9 yearsThe creation of objects after the top level object has been created is usually done using factories. There is a factory extension to NInject which makes this convenient github.com/ninject/ninject.extensions.factory/wiki equivalent to Autofac DelegateFactory.
-
MoonKnight over 8 years@IlyaIvanov why are you not implementing the "Register, Resolve, Release" methodology that Seemann seems to suggest we must when using a DI Container (Section 3.3.2 in his book)? I am totally new to this and am following what you have provided above, but am slightly confused over the use of Factories against what you are doing here? Why do we need this? I also assume I am free to implement a "Release" method in
CompositionRoot
and implementIDisposible
which merely callsninjectKernel.Dispose()
? -
MoonKnight over 8 yearsPs. I have a question with an open bounty here stackoverflow.com/questions/35802180/…