如何在WinForms中使用依赖注入

bmp9r5qi  于 2023-01-17  发布在  其他
关注(0)|答案(3)|浏览(202)

如何在Winforms C#中定义依赖注入?
接口类别:

public interface ICategory
{
    void Save();
}

类类别存储库:

public class CategoryRepository : ICategory
{

    private readonly ApplicationDbContext _context;

    public CategoryRepository(ApplicationDbContext contex)
    {
        _context = contex;
    }
    public void Save()
    {
        _context.SaveChanges();
    }
}

表格1:

public partial class Form1 : Form
{
    private readonly  ICategury _ic;
    public Form1(ICategury ic)
    {
        InitializeComponent();
    _ic=ic
    }

    private void button1_Click(object sender, EventArgs e)
    {
    Form2 frm= new Form2();
    frm.show();
}
 }

表格2:

public partial class Form2 : Form
{
    private readonly  ICategury _ic;
    public Form2(ICategury ic)
    {
        InitializeComponent();
    _ic=ic
    }
 }

有问题?

  1. Program.cs中依赖项注入的定义
Application.Run(new Form1());
  1. Form 2调用时依赖项注入的定义
Form2 frm= new Form2();
frm.show();
zujrkrfu

zujrkrfu1#

如何在Windows窗体(WinForms)中使用依赖项注入(DI)

要在WinForms .NET 5或6中使用DI,您可以执行以下步骤:
1.创建WinForms .NET应用程序
1.安装Microsoft.Extensions.Hosting包(它提供了一系列有用的功能,如DI、日志记录、配置等)
1.添加新接口IHelloService.cs

public interface IHelloService
{
    string SayHello();
}

1.为您的服务HelloService.cs添加新实现:

public class HelloService : IHelloService
{
    public string SayHello()
    {
        return "Hello, world!";
    }
}

1.修改Program.cs

//using Microsoft.Extensions.DependencyInjection;
static class Program
{
    [STAThread]
    static void Main()
    {
        Application.SetHighDpiMode(HighDpiMode.SystemAware);
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        var host = CreateHostBuilder().Build();
        ServiceProvider = host.Services;

        Application.Run(ServiceProvider.GetRequiredService<Form1>());
    }
    public static IServiceProvider ServiceProvider { get; private set; }
    static IHostBuilder CreateHostBuilder()
    {
        return Host.CreateDefaultBuilder()
            .ConfigureServices((context, services)=>{
                services.AddTransient<IHelloService, HelloService>();
                services.AddTransient<Form1>();
            });
    }
}

现在您可以将IHelloService注入Form1并使用它:

//using Microsoft.Extensions.DependencyInjection;
public partial class Form1 : Form
{
    private readonly IHelloService helloService;

    public Form1(IHelloService helloService)
    {
        InitializeComponent();
        this.helloService = helloService;
        MessageBox.Show(helloService.SayHello());
    }
}

如果要使用DI显示Form2,首先需要将其注册为services.AddTransient<Form2>();,然后根据Form2的用法,可以使用以下选项之一:

  • 如果在Form1的整个生命周期中只需要Form2的一个示例,那么可以将其作为依赖项注入到Form1的构造函数中,然后存储该示例,并在需要时显示它。

但请注意:当你打开Form1的时候,它只会被初始化一次,并且不会被再次初始化。你也不应该释放它,因为它是唯一传递给Form1的示例。

public Form1(IHelloService helloService, Form2 form2)
{ 
     InitializeComponent();
         form2.ShowDialog();
}
  • 如果您需要Form2的多个示例,或者需要多次初始化它,那么您可能会得到一个如下所示的示例:
using (var form2 = Program.ServiceProvider.GetRequiredService<Form2>())
     form2.ShowDialog();
t3irkdon

t3irkdon2#

除了Reza的回答之外,还有另一种方法,我的回答是originally published on my blog,但是,由于Stack Overflow是我们大多数人的主要信息来源,所以Reza回答下面的评论中链接的博客文章很容易被忽略。
接下来,这个解决方案基于Local Factory模式。
我们将从表单工厂开始

public interface IFormFactory
{
    Form1 CreateForm1();
    Form2 CreateForm2();
}

public class FormFactory : IFormFactory
{
    static IFormFactory _provider;

    public static void SetProvider( IFormFactory provider )
    {
        _provider = provider;
    }

    public Form1 CreateForm1()
    {
        return _provider.CreateForm1();
    }

    public Form2 CreateForm2()
    {
        return _provider.CreateForm2();
    }
}

从现在起,此工厂将成为创建窗体的主要客户端接口。客户端代码将不再只调用

var form1 = new Form1();

不,这是禁止的。相反,客户应该打电话

var form1 = new FormFactory().CreateForm1();

(and类似地用于其它形式)。
注意,当工厂被实现时,它自己并不做任何事情!相反,它将创建委托给一个神秘的提供者,这个提供者必须被注入到工厂中。这背后的想法是,提供者将被注入一次,在Composition Root中,这是代码中的一个地方,靠近启动。并且在应用程序堆栈中处于很高的位置,因此所有的依赖项都可以在那里解决,所以表单工厂不需要知道最终将向其注入哪个提供程序。
这种方法有一个显著的优势--根据实际需求,可以注入不同的提供者,例如,您可以为实际应用程序注入基于DI的提供者(我们稍后将编写它),为单元测试注入存根提供者。
无论如何,让我们有一个具有依赖项的表单:

public partial class Form1 : Form
{
    private IHelloWorldService _service;

    public Form1(IHelloWorldService service)
    {
        InitializeComponent();

        this._service = service;
    }
}

这个表单依赖于一个服务,而这个服务将由构造函数提供。如果Form 1需要创建另一个表单Form 2,它会按照我们已经讨论过的方法来做:

var form2 = new FormFactory().CreateForm2();

然而,当表单不仅需要依赖服务,而且只需要一些自由参数(字符串、整型等)时,事情就变得更加复杂了。

public Form2( string something, int somethingElse ) ...

但现在你需要更像

public Form2( ISomeService service1, IAnotherService service2, 
              string something, int somethingElse ) ...

我们真的应该好好研究一下。再看一遍,一个现实生活中的生物可能需要

  • 由容器解析的一些参数
  • 表单创建者应该提供的其他参数(容器不知道!)。

我们该怎么办?
为了得到一个完整的示例,接下来让我们修改表单工厂

public interface IFormFactory
{
    Form1 CreateForm1();
    Form2 CreateForm2(string something);
}

public class FormFactory : IFormFactory
{
    static IFormFactory _provider;

    public static void SetProvider( IFormFactory provider )
    {
        _provider = provider;
    }

    public Form1 CreateForm1()
    {
        return _provider.CreateForm1();
    }

    public Form2 CreateForm2(string something)
    {
        return _provider.CreateForm2(something);
    }
}

让我们看看表单是如何定义的

public partial class Form1 : Form
{
    private IHelloWorldService _service;

    public Form1(IHelloWorldService service)
    {
        InitializeComponent();

        this._service = service;
    }

    private void button1_Click( object sender, EventArgs e )
    {
        var form2 = new FormFactory().CreateForm2("foo");
        form2.Show();
    }
}

public partial class Form2 : Form
{
    private IHelloWorldService _service;
    private string _something;

    public Form2(IHelloWorldService service, string something)
    {
        InitializeComponent();

        this._service = service;
        this._something = something;

        this.Text = something;
    }
}

你能看出其中的规律吗?

  • 每当表单(例如Form 1)只需要依赖项服务时,其在FormFactory中的创建方法为空(依赖项将由容器解析)。
  • 每当表单(例如Form 2)需要依赖服务和其他自由参数时,其在FormFactory中的创建方法包含与这些自由参数对应的参数列表(服务依赖性将由容器解析)

现在最后是Composition Root。让我们从服务开始

public interface IHelloWorldService
{
    string DoWork();
}

public class HelloWorldServiceImpl : IHelloWorldService
{
    public string DoWork()
    {
        return "hello world service::do work";
    }
}

请注意,虽然接口应该位于堆栈中的某个位置(以便每个人都能识别),但是可以在任何位置自由提供实现(表单不需要引用实现!)然后,按照起始代码,最终提供表单工厂并设置容器

internal static class Program
{
    [STAThread]
    static void Main()
    {
        var formFactory = CompositionRoot();

        ApplicationConfiguration.Initialize();
        Application.Run(formFactory.CreateForm1());
    }

    static IHostBuilder CreateHostBuilder()
    {
        return Host.CreateDefaultBuilder()
            .ConfigureServices((context, services) => {
                services.AddTransient<IHelloWorldService, HelloWorldServiceImpl>();
                services.AddTransient<Form1>();
                services.AddTransient<Func<string,Form2>>(
                    container =>
                        something =>
                        {
                            var helloWorldService = 
                                container.GetRequiredService<IHelloWorldService>();
                            return new Form2(helloWorldService, something);
                        });
            });
    }

    static IFormFactory CompositionRoot()
    {
        // host
        var hostBuilder = CreateHostBuilder();
        var host = hostBuilder.Build();

        // container
        var serviceProvider = host.Services;

        // form factory
        var formFactory = new FormFactoryImpl(serviceProvider);
        FormFactory.SetProvider(formFactory);

        return formFactory;
    }
}

public class FormFactoryImpl : IFormFactory
{
    private IServiceProvider _serviceProvider;

    public FormFactoryImpl(IServiceProvider serviceProvider)
    {
        this._serviceProvider = serviceProvider;
    }

    public Form1 CreateForm1()
    {
        return _serviceProvider.GetRequiredService<Form1>();
    }

    public Form2 CreateForm2(string something)
    {
        var _form2Factory = _serviceProvider.GetRequiredService<Func<string, Form2>>();
        return _form2Factory( something );
    }
}

首先注意如何使用Host.CreateDefaultBuilder创建容器,这是一项简单的任务,然后注意如何注册服务以及如何在其他服务中注册表单。
对于没有任何依赖项的表单来说,这很简单,它只是

services.AddTransient<Form1>();

然而,如果表单同时需要服务和自由参数,那么它就注册为......表单创建函数,即返回实际表单的任意自由参数的Func。

services.AddTransient<Func<string,Form2>>(
    container =>
        something =>
        {
            var helloWorldService = container.GetRequiredService<IHelloWorldService>();
            return new Form2(helloWorldService, something);
        });

这很聪明,我们使用一个本身使用工厂函数的注册机制来注册表单工厂函数(是的,一个工厂使用另一个工厂,一个Factception。如果你在这里感到困惑,请随意休息一下)我们注册的函数,Func〈string,Form 2〉有一个参数,something(对应于表单构造函数的free参数),但它的其他依赖项由容器(这正是我们想要的)解决。
这就是为什么实际的表单工厂需要注意它所解析的内容。

return _serviceProvider.GetRequiredService<Form1>();

另一个分两步解析,我们首先解析工厂函数,然后使用创建的方法参数将其提供给函数:

var _form2Factory = _serviceProvider.GetRequiredService<Func<string, Form2>>();
return _form2Factory( something );

就是这样。无论何时创建表单,

new FormFactory().CreateForm1();

用于“简单”表单(仅具有服务依赖项)或仅

new FormFactory().CreateForm2("foo");

用于既需要服务依赖性又需要其他自由参数的表单。

jckbn6z7

jckbn6z73#

我只是想在这里添加它作为IFormFactory的一个替代模式,这是我通常使用的方法。
这样做的好处是,您不需要为添加的每个窗体和每组参数不断更改IFormFactory接口。
在应用程序启动时加载所有窗体,并将参数传递给show方法或其他可以在窗体上定义的基方法。

internal static class Program
{
    public static IServiceProvider ServiceProvider { get; private set; }
  
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        ServiceProvider = CreateHostBuilder().Build().Services;
        Application.Run(ServiceProvider.GetService<Form1>());
    }
   
    static IHostBuilder CreateHostBuilder()
    {
        return Host.CreateDefaultBuilder()
            .ConfigureServices((context, services) => {
                services.AddSingleton<IFormFactory,FormFactory>();
                services.AddSingleton<IProductRepository, ProductRepository>();

                 //Add all forms
                var forms = typeof(Program).Assembly
                .GetTypes()
                .Where(t => t.BaseType ==  typeof(Form))
                .ToList();

                forms.ForEach(form =>
                {
                    services.AddTransient(form);
                });
            });
    }
}

表单工厂

public interface IFormFactory
{
    T? Create<T>() where T : Form;
}

public class FormFactory : IFormFactory
{
    private readonly IServiceScope _scope;

    public FormFactory(IServiceScopeFactory scopeFactory) 
    {
        _scope = scopeFactory.CreateScope();
    }

    public T? Create<T>() where T : Form
    {
        return _scope.ServiceProvider.GetService<T>();
    }
}

表格一

public partial class Form1 : Form
{
    private readonly IFormFactory _formFactory;
    public Form1(IFormFactory formFactory)
    {
        InitializeComponent();
        _formFactory = formFactory;
    }

    private void button_Click(object sender, EventArgs e)
    {
        var form2 = _formFactory.Create<Form2>();
        form2?.Show(99);
    }
}

表格二

public partial class Form2 : Form
{
    private readonly IProductRepository _productRepository;

    public Form2(IProductRepository productRepository)
    {
        InitializeComponent();
        _productRepository = productRepository;
    }

    public void Show(int recordId)
    {
        var product = _productRepository.GetProduct(recordId);

        //Bind your controls etc
        this.Show();
    }
}

相关问题