16

My knowledge in DI is very limited. I am developing a WinForm project in a solution which every else where in the solution, Microsoft Extension Dependency Injection have been used.

I need to pass some dependency into constructor of MainForm:

public partial class MainForm : Form
{
   public MainForm(ISomeThing someThing)
   {
   }
}

In the Main method, an instance of MainForm is passed to Run method:

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new MainForm());
}

I tried to use DI to instantiate an instance for Mainform by having a service provider:

private static IServiceProvider ServiceProvider { get; set; }

and then assigning to it an object as follows:

static void ConfigureServices()
{
   var services = new ServiceCollection();
   services.AddTransient<ISomeThing, SomeThing>();
   ServiceProvider = services.BuildServiceProvider();
}

And then call ConfigureServices() in Main() as follows:

static void Main()
{
   Application.EnableVisualStyles();
   Application.SetCompatibleTextRenderingDefault(false);
   ConfigureServices();
   Application.Run(ServiceProvider.GetService(MainForm));
}

However, I get a compilation error: "MainForm is a type, which is not valid in the given context"

I found the following links which uses similar approach using SimpleInjector or Unity but I do not know how to use this with this kind of DI? or I have to use other DI?

Thanks

6 Answers 6

11

You're trying to get the System.Type instance that corresponds to that class.

That's what the typeof() keyword does:

GetService(typeof(MainForm))

Note that you'll also need to cast the result to Form.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for you comment. I used like this per your suggestion var form = (MainForm) ServiceProvider.GetService(typeof(MainForm)); Application.Run(form); however, the form object is null. Can you please advise
@AminMerati: You need to add it to your ServiceProvider.
6

I would modify it as follows..

You do NOT need to pass anything using a parameter to constructor of MainForm.

In the Program class, an instance of Something is registered as implementor of ISomething, but the Application.Run() remains unchanged,

using Microsoft.Extensions.DependencyInjection;  // provides DI
using SomeThing; // implements ISomeThing interface and SomeThing class 

/*Program.cs */ 
public static class Program
{
  //..
  public static IServiceProvider ServiceProvider { get; set; }

  static void ConfigureServices()
  {
    var services = new ServiceCollection();
    services.AddTransient<ISomeThing, SomeThing>();
    ServiceProvider = services.BuildServiceProvider();
  }

  [STAThread]
  static void Main()
  {  
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    ConfigureServices();
    Application.Run(new MainForm());
  }
}

Now in MainForm, the constructor will inquire the registered dependency and save it as a private property, like

    public class MainForm: Form
    {

      // ..

       private readonly IMySomeThing _mySomething;

       public MainForm()
       {
        _mySomething =   (IMySomeThing)Program.ServiceProvider.GetService(typeof(IMySomeThing));
       }     

    }

After your MainForm ctor has finished, private field _mySomething can be accessed (or its methods can be called) from anywhere in MainForm. Which class implements IMySomething is controlled by the registrations with ServiceProvider. This allows to change behaviour of classes by replacing certain owned classes.

I use DI to simplify certain unit tests. For example, in a unit test, a dependency implementing some complicated subtask can be replaced by a simple true/false return substitute. This testing method is commonly referred to as "mocking". It allows for unit testing of mid level and high level procedures.

3 Comments

This completely hides a dependency of the class, and is moving towards Service Locator pattern which is not always good. Suppose someone else wants to use this MainForm for another project, He has no idea that he should provide IMySomething, and everything would break for him. And this is my opinion, all dependencies of the class should be provided through constructor.
Maybe I used that pattern because dependency injection is very suitablefor it. The above code is just a basic example of using DI. It won't break, there is always a unimplemented message of course, when the Serviceprovider did not happen to register ISomething. When someone else proceeds using MainForm, the ServiceProvider is copied along. When parts of it are needed, or all is needed.. You'll never "parachute" arbitrary classes into a Form, without the required services, and see what happens.
What i was trying to say is that, from the user point of view, there is a parameter less constructor, so I should be able to create an object of it. and let just say the consuming library is not using dependency injection (or using another dependency injection service). He won't have an easy of using it, On the other hand, if you use constructor injection, The consumer knows that we need ISomething, so you're adding a dependency if dependency injection container that you are using, which it doesn't need.
5

I've added a helper function to my code to make this more readable:

public static T? GetService<T>() where T : class
{
   return (T?)ServiceProvider.GetService(typeof(T));
}



//was:
// thing = (iThing)Program.ServiceProvider.GetService(typeof(iThing));

//now:
thing = Program.GetService<iThing>();


Comments

0

You can also use the generic version of the method GetService<T>.

You can use it as such: serviceProvider.GetService<MainForm>();

I also like to use GetRequiredService<T> rather than GetService<T> so it throws an exception and I can find out if something isn't registered during development.

Comments

0

To clariy, if you include

services.AddSingleton<MainForm, MainForm>()

you can then

var mainForm = ServiceProvider.GetService<MainForm>();
Application.Run(mainForm);

or perhaps better

var mainForm = ServiceProvider.GetRequiredService<MainForm>();
Application.Run(mainForm);

As the DI container is bringing the MainForm to life the required ISomeThing dependency will be injected

Comments

-1

This is the best answer

Application.Run(new MainForm((ISomeThing)ServiceProvider.GetService(typeof(ISomeThing))));

3 Comments

Can you expand as to why this is the "best" answer to a 3+ year old question that already has an accepted answer? It might be, but without something more than a 1 liner of code, it doesn't seem to contribute meaningfully.
The answers adress two different aspects. My answer (and Pedro's confirmation) chooses a class for ISomething via assignment in ConfigureServices() which is IMHO the usual way people use this technology for ISomething. The acknowledged answer injects the form class itself, which is an interesting option for DI, but it does not provide an answer how to set the ISomething class. Our answers cover two different DI-aspects of the question.
Actually, registering both MainForm and Something should be the right answer. This way you only need to GetService(typeof(MainForm)) which is more concise and also shows the benefit in registering concrete classes

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.