在.NET 4.7.2中在WebForms中连接简单注入器 [英] Wiring up Simple Injector in WebForms in .NET 4.7.2

查看:69
本文介绍了在.NET 4.7.2中在WebForms中连接简单注入器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

随着.NET 4.7.2中的更改,现在可以在Web窗体中进行构造函数注入。我已经使用了Web窗体的Simple Injector,但是想要一些有关是否可能缺少的陷阱的信息。

With the changes in .NET 4.7.2, constructor injection is now possible in Web Forms. I have gotten Simple Injector working with Web Forms, but would like some input as to if there any "gotchas" I might be missing.

首先,我已经注册了页面本身,该页面取自此处

First I have the registration of the Pages themselves which is taken from here.

public static void RegisterWebPages(this Container container)
{
    var pageTypes = 
        from assembly in BuildManager.GetReferencedAssemblies().Cast<Assembly>()
        where !assembly.IsDynamic
        where !assembly.GlobalAssemblyCache
        from type in assembly.GetExportedTypes()
        where type.IsSubclassOf(typeof(Page))
        where !type.IsAbstract && !type.IsGenericType
        select type;

    foreach (Type type in pageTypes)
    {
        var reg = Lifestyle.Transient.CreateRegistration(type, container);
        reg.SuppressDiagnosticWarning(
            DiagnosticType.DisposableTransientComponent,
            "ASP.NET creates and disposes page classes for us.");
        container.AddRegistration(type, reg);
    }
}

使用从上面的链接就好了。

This has worked when using the property injection method from the link above just fine. I am including it here for completeness.

当我第一次连接它时,一个 OutputCacheModule 具有内部构造函数。使用此处中的代码,我能够解决该问题以及任何其他可能存在的问题来自内部构造函数。这是实现该代码的完整性。

When I wired it up the first time, there was an issue with one OutputCacheModule having an internal constructor. Using the code from here I was able to fix that issue and any others that might have arisen from internal constructors. Here is the code for that implementation for completeness.

public class InternalConstructorResolutionBehavior : IConstructorResolutionBehavior
{
    private IConstructorResolutionBehavior original;

    public InternalConstructorResolutionBehavior(Container container)
    {
        this.original = container.Options.ConstructorResolutionBehavior;
    }

    public ConstructorInfo GetConstructor(Type implementationType)
    {
        if (!implementationType.GetConstructors().Any())
        {
            var internalCtors = implementationType.GetConstructors(
                BindingFlags.Instance | BindingFlags.NonPublic)
                .Where(c => !c.IsPrivate)
                .ToArray();

            if (internalCtors.Length == 1) return internalCtors.First();
        }

        return original.GetConstructor(implementationType);
    }
}

现在有了背景知识了,这是问题的实质。这是我连接的自定义激活器。

Now with the backstory out of the way, here is the meat of the question. This is the custom activator I have wired up.

public class SimpleInjectorWebFormsActivator : IServiceProvider
{
    private readonly Container container;

    public SimpleInjectorWebFormsActivator(Container container)
    {
        this.container = container;
        this.container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
        this.container.Options.ConstructorResolutionBehavior =
            new InternalConstructorResolutionBehavior(this.container);
    }

    public object GetService(Type serviceType)
    {
        return container.GetInstance(serviceType);
    }
}

问题是, GetService 方法够吗?关于如何对WebForms使用新的扩展点,目前几乎没有。有一个Autofac示例比简单的一行传递到Simple Injector复杂得多,但是由于我不熟悉Autofac,所以我不知道其中有多少用于容器。

The question is, is the GetService method enough? There is very little out there right now about how to use the new extension point for WebForms. There is an Autofac example that is significantly more complex than my simple one line pass through to Simple Injector, but as I am not familiar with Autofac I don't know how much of that is for the container.

现在该解决方案有效。页面加载没有错误。容器将调用传递给验证。

Right now the solution works. Pages load without error. The container passes the call to Verify.

够了还是还有更多的工作要做?我缺少任何陷阱吗?我对ether Simple Injector或WebForms的更深层次的内部运作不是很熟悉,所以担心我可能会丢失一些巨大的东西。

Is this enough or is there more work to be done? Are there any "gotchas" that I am missing? I am not very familiar with the deeper inner workings of ether Simple Injector or WebForms, so I'm worried I might be missing something huge.

截至目前,这里有

推荐答案

IMO,对Web窗体中的此新功能的考虑不是特别周密通过。主要问题是Web窗体违反了 IServiceProvider 合同。

IMO, this new feature in Web Forms is not particularly well thought through. The main problem is that Web Forms breaks the IServiceProvider contract.

IServiceProvider.GetService 方法定义如果没有这样的服务,应返回 null 。但是一旦您实际返回 null ,例如当您无法构造该类型时,Web窗体会从其堆栈的深处抛出 NullReferenceException

The IServiceProvider.GetService method defines that null should be returned if no such service exists. But once you actually return null, e.g. when you can’t construct that type, Web Forms throws a NullReferenceException from deep down its stack.

Web另一方面,表单符合 IServiceProvider 的抽象,因为 SimpleInjector,插入Simple Injector只是一个语句。容器实际上实现了 IServiceProvider

Would Web Forms, on the other hand, have conformed to the IServiceProvider abstraction, plugging in Simple Injector would have been a matter of a single statement, since SimpleInjector.Container actually implements IServiceProvider:

// WARNING: This won’t work
HttpRuntime.WebObjectActivator = container; 

此外,当 IServiceProvider 是通过 HttpRuntime.WebObjectActivator 设置的,Web Forms几乎可以为所有它调用它,甚至是它自己的内部对象,这对我来说意义不大。

On top of this, when an IServiceProvider is set through HttpRuntime.WebObjectActivator, Web Forms will call it for almost everything, even for its own internal objects, which, to me, makes little sense.

因此,与其提供与 IServiceProvider 兼容 IServiceProvider 实现,而不是合同,您将必须提供与ASP.NET Web窗体兼容的特殊 IServiceProvider 实现(因此,会破坏

Therefore, instead of supplying an IServiceProvider implementation that is compatible to the IServiceProvider contract, you will have to provide a special ASP.NET Web Forms-compatible IServiceProvider implementation (which therefore breaks the contract).

请注意,大多数DI容器实际上实现了 IServiceProvider ,但是您会看到大多数失败,因为

Note that most DI Containers actually implement IServiceProvider, but you would see most of them fail, because of this contract breach.

适配器实现如下所示:

class SimpleInjectorWebFormsServiceActivator : IServiceProvider
{
    private const BindingFlags flag =
        BindingFlags.Instance | BindingFlags.NonPublic |
        BindingFlags.Public | BindingFlags.CreateInstance;

    private readonly Container container;

    public SimpleInjectorWebFormsServiceActivator(Container container) =>
        this.container = container;

    public object GetService(Type serviceType) =>
        serviceType.GetConstructors().Length > 0
            ? this.container.GetInstance(serviceType)
            : Activator.CreateInstance(serviceType, flag, null, null, null);
}

可以设置如下:

HttpRuntime.WebObjectActivator =
    new SimpleInjectorWebFormsServiceActivator(container);

此实现可验证类型是否包含公共构造函数,如果包含,则将调用委派给Simple Injector,这将构造类型。否则,它将使用 Activator.CreateInstance 来构造类型。

This implementation verifies whether the type contains public constructors and if so, it delegates the call to Simple Injector, which will construct the type. Otherwise, it will use Activator.CreateInstance to construct the type.

请注意,使用此实现您不需要自定义 IConstructorSelectionBehavior ,因此您可以完全删除 InternalConstructorResolutionBehavior

Do note that using this implementation you don’t need custom IConstructorSelectionBehavior, so you can remove your InternalConstructorResolutionBehavior altogether.

这篇关于在.NET 4.7.2中在WebForms中连接简单注入器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆