在.NET 4.7.2中使用WebForms连接简单注入器

时间:2018-05-31 20:53:17

标签: c# dependency-injection webforms simple-injector

随着.NET 4.7.2的更改,现在可以在Web窗体中进行构造函数注入。我已经让Simple Injector使用Web窗体,但是想知道是否有任何"陷阱"我可能会失踪。

首先,我自己注册了页面,这些页面取自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);
    }
}

当从上面的链接使用属性注入方法时,这很有效。我把它包括在这里是为了完整。

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

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

现在有了背景故事,这就是问题的关键所在。这是我连接的自定义激活器。

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,我不知道容器中有多少。

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

这还不够或还有更多工作要做吗?有没有&#34;陷阱&#34;我失踪了?我不太熟悉以太简单注入器或WebForms的更深层次的内部工作,所以我担心我可能会遗漏一些巨大的东西。

截至目前,没有必要也没有计划任何范围内的容器。

2 个答案:

答案 0 :(得分:10)

IMO,Web Forms中的这一新功能并未得到特别深思。主要问题是Web窗体违反了c("gatcoin", "appcoins", "pareto", "betbox", "aidcoin") 合同。

IServiceProvider方法定义如果不存在此类服务,则应返回IServiceProvider.GetService。但是,一旦您真正返回null,例如当您无法构造该类型时,Web窗体会从其堆栈的深处抛出null

另一方面,Web表单是否符合NullReferenceException抽象,插入Simple Injector本来就是一个单一语句,因为IServiceProvider实际上实现了SimpleInjector.Container

IServiceProvider

除此之外,当// WARNING: This won’t work HttpRuntime.WebObjectActivator = container; 通过IServiceProvider设置时,Web窗体几乎可以调用它,即使对于它自己的内部对象也是如此,对我而言,这对我来说毫无意义。 / p>

因此,您不必向HttpRuntime.WebObjectActivator合同提供兼容IServiceProvider实现,而是必须提供一个特殊的ASP.NET Web窗体兼容{{ 1}}实现(因此打破合同)。

请注意,大多数DI容器实际上都会实现IServiceProvider,但由于合同违约,您会看到其中大多数都失败了。

适配器实现如下所示:

IServiceProvider

可以设置如下:

IServiceProvider

此实现验证类型是否包含公共构造函数,如果是,则将调用委托给Simple Injector,它将构造类型。否则,它将使用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); ,因此您可以完全删除Activator.CreateInstance

答案 1 :(得分:-1)

我们将发布一个使用Unity容器的适配器(Activator)nupkg(以及即将推出的博客),并且还将开源它。以下是实现Adapter(Activator)的一般指导。

  1. 如果IoC容器无法解析serviceType,您可以考虑缓存该类型。下次您可以通过反射直接创建实例以获得一些性能增益。
  2. 您可以保留在注册前注册的适配器(Activator),如果您的无法解析serviceType,请尝试使用该适配器(Activator)。
  3. 如果IoC容器实现了IDisposable,则适配器(Activator)应该实现可以调用Container.Dispose的IRegisteredObject。