'Using Microsoft Extension Dependency Injection on WinForms in C#

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



Solution 1:[1]

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.

Solution 2:[2]

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.

Solution 3:[3]

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>();


Solution 4:[4]

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.

Solution 5:[5]

This is the best answer

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 SLaks
Solution 2
Solution 3
Solution 4 camleng
Solution 5 Pedro Reis