I'm using Autofac 2.5 in asp.net and I'm having an issue where lifetime scope components are getting resolved as dependencies of single instance components thus destroying my thread safety. This is a problem with the registrations but I thought Autofac viewed this as a violation and would throw an exception.
private class A{}
private class B
{
public B(A a){}
}
[Test]
[ExpectedException()]
public void SingleInstanceCannotResolveLifetimeDependency()
{
var builder = new ContainerBuilder();
builder.RegisterType<A>()
.InstancePerLifetimeScope();
builder.RegisterType<B>()
.SingleInstance();
using (var container = builder.Build())
{
using (var lifetime = container.BeginLifetimeScope())
{
//should throw an exception
//because B is scoped singleton but A is only scoped for the lifetime
var b = lifetime.Resolve<B>();
}
}
}
Is there a way to have Autofac throw a Dependency resolution exception if this happens?
UPDATE Even though this is the correct behavior for Autofac - SingleInstance is just Root lifetime scoped - it can be potentially dangerous in a web environment. Making sure that all of your developers get the correct registrations can be a pain. Here is a small extension method for Autofac that checks an instance lookup to make sure that lifetime scoped instances do not get resolved in the root scope. I know it has helped us weed lifecycle issues out of our web project.
public static class NoLifetimeResolutionAtRootScopeExtensions
{
/// <summary>
/// Prevents instances that are lifetime registration from being resolved in the root scope
/// </summary>
public static void NoLifetimeResolutionAtRootScope(this IContainer container)
{
LifetimeScopeBeginning(null, new LifetimeScopeBeginningEventArgs(container));
}
private static void LifetimeScopeBeginning(object sender, LifetimeScopeBeginningEventArgs e)
{
e.LifetimeScope.ResolveOperationBeginning += ResolveOperationBeginning;
e.LifetimeScope.ChildLifetimeScopeBeginning += LifetimeScopeBeginning;
}
private static void ResolveOperationBeginning(object sender, ResolveOperationBeginningEventArgs e)
{
e.ResolveOperation.InstanceLookupBeginning += InstanceLookupBeginning;
}
private static void InstanceLookupBeginning(object sender, InstanceLookupBeginningEventArgs e)
{
var registration = e.InstanceLookup.ComponentRegistration;
var activationScope = e.InstanceLookup.ActivationScope;
if (registration.Ownership != InstanceOwnership.ExternallyOwned
&& registration.Sharing == InstanceSharing.Shared
&& !(registration.Lifetime is RootScopeLifetime)
&& activationScope.Tag.Equals("root"))
{
//would be really nice to be able to get a resolution stack here
throw new DependencyResolutionException(string.Format(
"Cannot resolve a lifetime instance of {0} at the root scope.", registration.Target))
}
}
}
Just apply this when you create your container and you will get exceptions thrown when lifetime scoped services are resolved in the root scope.
container.NoLifetimeResolutionAtRootScope();
Yes - you need to name the child scope, and associate the component A
with it explicitly. Otherwise, as you observe, a A
instance is created in the root (container) scope.
// Replace `A` registration with:
builder.RegisterType<A>().InstancePerMatchingLifetimeScope("child");
And...
// Replace scope creation with:
using (var lifetime = container.BeginLifetimeScope("child")) {
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With