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.
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);
}
}
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.
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.
As of right now there is no need nor plans for there to be any scoped containers.