首页 > 解决方案 > 用户控件 Windows 窗体中的简单注入器属性注入

问题描述

我必须创建一个使用 Windows 窗体的小应用程序。我想使用 Simple Injector 作为 IoC 容器。

Simple Injector 的文档写道:

不能在用户控件中使用构造函数注入。用户控件需要有一个默认构造函数。相反,使用属性注入将依赖项传递给您的用户控件。

我已按照说明进行操作,我的程序如下所示:

internal static class Program
{
    [STAThread]
    private static void Main()
    {
        App.EnableVisualStyles();
        App.SetCompatibleTextRenderingDefault(false);
        App.Run(Bootstrap().GetInstance<FormMain>());
    }

    private static Container Bootstrap()
    {
        var container = new Container();

        container.Options.PropertySelectionBehavior =
            new ImportPropertySelectionBehavior();

        ConfigureServices(container);

        AutoRegisterWindowsForms<Form>(container);
        AutoRegisterWindowsForms<UserControl>(container);

        container.Verify();

        return container;
    }

    private static void ConfigureServices(Container container)
    {
        container.RegisterSingleton<IFormOpener, FormOpener>();
        container.RegisterSingleton<IUserService, UserService>();
        container.RegisterSingleton<IDatabaseService, DatabaseService>();
    }

    private static void AutoRegisterWindowsForms<T>(Container container)
        where T: ContainerControl
    {
        var types = container.GetTypesToRegister<T>(typeof(Program).Assembly);

        foreach (var type in types)
        {
            var registration =
                Lifestyle.Transient.CreateRegistration(type, container);

            registration.SuppressDiagnosticWarning(
                DiagnosticType.DisposableTransientComponent,
                "Forms should be disposed by app code; not by the container.");

            container.AddRegistration(type, registration);
        }
    }

我的问题是在使用拖放设计UI时,使用new操作符自动生成用户控件,并且属性注入不起作用。我不想将容器直接注入到用户控件的GetInstance的表单中,因为它是一种反模式,表单设计也不起作用。有没有办法解决这个问题?谢谢大家

编辑 我以我的方式解决了这个问题。我创建了一个包含 GetService 方法的静态类,如下所示:

public static class ServiceFactory
{
    public static Container container;

    public static TService GetService<TService>() where TService: class
    {
        return container.GetInstance<TService>();
    }

}

容器需要在程序初始化时被赋值:

private static Container Bootstrap()
{
    var container = new Container();

    ServiceFactory.container = container;

    ConfigureServices(container);

    AutoRegisterWindowsForms<Form>(container);
    AutoRegisterWindowsForms<UserControl>(container);

    container.Verify();

    return container;
}

我将服务注入到用户控件中,如下所示:

private readonly IDatabaseService _databaseService;

public CustomUserControl()
{
    InitializeComponent();
    _databaseService = ServiceFactory.GetService<IDatabaseService>();
}

标签: c#winformssimple-injector

解决方案


我的问题是在使用拖放设计UI时,使用new操作符自动生成用户控件,并且属性注入不起作用。

这正是文档提到属性注入的原因。属性注入允许应用于外部创建的实例(因为用户控件是由 UI 框架创建的,而不是 DI 容器)。

然而,文档没有提到的是如何使用依赖项初始化创建的实例。Simple Injector 包含一个InitializeInstance用于此的。以下代码允许初始化使用显式属性注入的用户控件:

private static Container container;

public static void InitializeControl(UserControl control)
{
    var producer = container.GetRegistration(control.GetType(), true);
    producer.Registration.InitializeInstance(control);
}

不幸的是,这远非完美,因为它需要表单和用户控件依赖于 DI 容器。为了防止这种情况,您可以通过方法注入将依赖项从包含表单传递给用户控件,但这样做的缺点是您很容易必须传递许多依赖项,从而导致使用表单的构造函数爆炸。

或者,与文档所述相反,可以从 DI 容器解析用户控件并规避使用Ron在此处所述的设计器。但是,这可能会产生其自身的后果,而我对此没有经验。


推荐阅读