Webmaster: There aren't many shared articles about Winform. This technology is relatively mature and has everything it needs. Today, I'm sharing an article about dependency injection in Winform written by another account. I hope developers working on related projects can gain new insights.
1. Introduction
What is dependency injection? Dependency injection is a specific coding technique. For me, the most obvious benefit is reducing code coupling.
2. Purpose
In ASP.NET Core, the container is already set up; you just need to add services to the container. However, in Winform, the default approach still uses the new keyword (even though I've upgraded to .NET 6). Recently, I was adding custom features to an open-source project, and I took the opportunity to upgrade the original .NET Framework to .NET Core. That's when I decided to give dependency injection a try.
I haven't written much C/S code. If there's anything wrong, please correct me.
3. Steps
Example environment for this article: VS2022, .NET 6, Windows Forms Application
4. Preparation
The example includes the following code:
- Forms: Form1, Form2
- Services: IUserservice, Userservice, IOrderService, OrderService
public interface IUserservice
{
string GetName();
}
public class UserService : IUserservice
{
public string GetName()
{
return "IUserservice";
}
}
public interface IOrderService
{
string GetName();
}
public class OrderService : IOrderService
{
public string GetName()
{
return "IOrderService";
}
}
5. Scenario
In Form1, inject IUserservice via the constructor and call the GetName method of IUserservice in the Load event. When the button on the page is clicked, Form2 is displayed. In Form2, inject IOrderService via dependency injection and call the GetName method of IOrderService in the Load event. If the operation can be performed multiple times without errors, it is successful.
6. Getting Started
Reference the component:
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
Added a ServiceProviderHelper helper class:
public static class ServiceProviderHelper
{
/// <summary>
/// Global service provider
/// </summary>
public static IServiceProvider ServiceProvider { get; private set; } = null!;
/// <summary>
/// Initialize and build the ServiceProvider object
/// </summary>
/// <param name="serviceProvider"></param>
/// <exception cref="ArgumentNullException"></exception>
public static void InitServiceProvider(ServiceProvider serviceProvider)
{
ArgumentNullException.ThrowIfNull(serviceProvider, nameof(serviceProvider));
ServiceProvider = serviceProvider;
}
/// <summary>
/// Get Form service
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static Form GetFormService(Type type)
{
var service = ServiceProvider.GetRequiredService(type);
if (service is Form fService)
{
return fService;
}
else
{
throw new ArgumentException($"{type.FullName} is not a Form");
}
}
/// <summary>
/// Get service
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static T GetService<T>() where T : class
{
return ServiceProvider.GetRequiredService<T>();
}
}
Modify the Program method:
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static void Main()
{
// .NET 6 syntax: previously three lines combined into one
ApplicationConfiguration.Initialize();
// Create service container
var services = new ServiceCollection();
// Add service registrations
ConfigureServices(services);
// Build the ServiceProvider object
ServiceProviderHelper.InitServiceProvider(services.BuildServiceProvider());
// Get the required service
var main = ServiceProviderHelper.ServiceProvider.GetRequiredService<Form1>();
Application.Run(main);
}
/// <summary>
/// Inject services
/// </summary>
/// <param name="services"></param>
public static void ConfigureServices(IServiceCollection services)
{
// For batch injection, you can use Scrutor or write your own
services.AddScoped<IUserservice, UserService>();
services.AddScoped<IOrderService, OrderService>();
// Other forms can also be injected here
services.AddSingleton(typeof(Form1));
services.AddTransient(typeof(Form2));
}
}
Perform injection in Form1 and Form2 respectively:
private readonly IUserservice _userservice;
public Form1(IUserservice userservice)
{
InitializeComponent();
_userservice = userservice;
}
private readonly IOrderService _orderService;
public Form2(IOrderService orderService) : this()
{
_orderService = orderService;
}
Click the button on Form1 to display Form2:
private void button1_Click(object sender, EventArgs e)
{
var form2 = ServiceProviderHelper.GetFormService(typeof(Form2));
form2.Show();
}
Running the operation several times did not reveal any anomalies.
7. References
Example of implementing dependency injection in Winform based on .NET Core 3.1: http://www.ty2y.com/study/znetcore3.1sjywinformsxylzrsl.html