I'm trying to use autofac DI in my application. I created a wrapper class to abstract away all the autofac dlls:
FrameworkDependencyResolver : Logger, IFrameworkDependencyResolver
In this class I hold the container builder, and register all my dependencies in the application root. After registering my types I build it and hold the container:
Icontainer _container; ContainerBuilder _builder
public FrameworkDependencyResolver()
{
_builder = new ContainerBuilder();
}
Deep in my application i want to use the FrameworkDependencyResolver object to resolve protocols and open connections to external applications, therefore I registered this object as IFrameworkDependencyResolver with the following code:
_builder.RegisterInstance(obj).As<T>();
Obj is thr FrameworkDependencyResolver, T is the interface
In my starter thread, I resolve object that takes the FrameworkDependencyResolver in his ctor, and it works perfectly, resolvings are fine, however when I resolve an inner layer(on new thread) that takes the FrameworkDependencyResolver in it's ctor and try to resolve a registered protocol object I face deadlock.
Exmaple:
main:
var rootResolver = new FrameworkDependencyResolver();
rootResolver.RegisterType<IClass3, Class3>(Lifecycles.Singleton);
rootResolver.RegisterType<IClass2, Class2>(Lifecycles.Singleton);
rootResolver.RegisterType<Container, TestContainer>(Lifecycles.Singleton);
rootResolver.RegisterObject<IFrameworkDependencyResolver, FrameworkDependencyResolver>(rootResolver);
rootResolver.BuildContainer();
rootResolver.Resolve<TestContainer>();
Console.ReadKey();
TestContainer code:
public TestContainer(IFrameworkDependencyResolver resolver) : base(resolver){}
protected override void InitializeContainer()
{
_class2 = DependencyResolver.Resolve<IClass2>();
Thread.Sleep(20000);
Console.WriteLine("Container initialize finished");
}
Class2 code:
public class2(IFrameworkDependencyResolver resolver)
{
_resolver = resolver;
var thread = new Thread(startMethod);
thread.Start();
Console.WriteLine("Class2 ctor ended");
}
void StartMethod()
{
_class3 = _resolver.Resolve<IClass3>();
Console.WriteLine("Start method finished");
}
The output of this simple example program is:
Class2 ctor ended
Container initialize ended
Start method finished
Meaning that the thread I created is waiting for the main thread to finish and only than it can resolve. I want to prevent this and make it possible to resolve anytime from every thread. Please help me understand what is causing this. Thank you
Edit: The problem is not solved because autofac resolves singletons from the root scope..I believe my problem is similar to the one described here : Autofac resolving a singleton creates a bottleneck but I don't really understand the solution
Edit 2: for the bottleneck issue I learned that ctors should not contain logic at all. I also learned I probably shouldn't pass around my IFrameworkDependencyResolver object and should probably use Func<>.
My application structure: I have a layer in my application that handles connection requests and for every kind of request creates a different kind of protocol (a different protocol object)
// For example lets say a protocol takes in ctor these 3 services + runtime configuration object:
public Protocol1(IFramingAgent, IFramingAlgorithm, IFramingParser, configObject configuration)
Each service is registered with key because each protocol uses a different one
And here is my terrible class:
public class ProtocolsLayer : Layer
{
private IFrameworkDependencyResolver _resolver;
private IConfigurationService _configService;
public ProtocolsLayer(IFrameworkDependencyResolver resolver, IConfigurationService configurationService)
{
_resolver = resolver;
_configService = configurationService;
}
void HandleConnection1()
{
// What I have at the moment (terrible):
// Resolve the fitting services (All keyed - key is received by the type, Resolve and ResolveWithParameters used here are my wrappers)
var agent = _resolver.Resolve<IFramingAgent>(typeof(Protocol1FramingAgent));
var algo = _resolver.Resolve<IFramingAlgorithm>(typeof(Protocol1FramingAlgorith));
var parser = _resolver.Resolve<IFramingParser>(typeof(Protocol1FramingParser));
// A parameter I get and pass to each protocol at runtime
var protocolConfig = _configService.GetConfig<Protocol1Configuration>();
// Finally resolve the protocol with it's parameters:
protocol = _resolver.ResolveWithParameters<IProtocol>(typeof(Protocol1), new List<object>{
agent, resolver, parser, protocolConfig
});
//...
// Theres gotta be a better way!!
}
void HandleConntection2()
{
// Same as in protocol1
}
void HandleConnection3()
{
// Same as in protocol1
}
}
Take in mind that I don't want references to autofac, meaning I can't use IIndex<> which I heard off.
Thanks!
I follow the same strategy in my application to wrap DI library with my classes to have ability to change it later on if I need to.
I followed the same approach, with only one difference in your code you create
ContainerBuilder
in your class constructor and keep reference to it, this is the probleminstead, you may need remove away the
ContainerBuilder
instance, and just depend onAutofac.ILifetimeScope
as constructor dependency for yourFrameworkDependencyResolver
, this dependency will be just injected by autofac runtime with correct lifetime scope. then at any level on your code, you can just depend onFrameworkDependencyResolver
as you needEDIT
after i saw your update, i would recommend that you separate registration of your service from resolving instances, i.e make new class like
FrameworkDependencyRegister
and keep the other one for resolving and follow the steps answer above in my opinion abstracting registration might be too much unneeded abstraction, you can just write one method to do this stuff using normal autofac APIsI made a sample to reproduce your issue : https://dotnetfiddle.net/WOGwoD
If I summarize, your issue is that Autofac
Resolve
for only thread at a time.Let take another code sample to reproduce the issue :
The output of this program is the following :
If you change the lifetime scope from
SingleInstance
toInstancePerDependency
(the default one) for a single registration, the output will be :We can see that Autofac lock the
IContainer
for Shared registration while it is activating a Shared registration. Thelock
statement is Line 258 of LifetimeScope.cs. I think this behavior is here to prevent issue with complex dependency graph. ie : What happens ifFoo1
has a nested dependency onFoo2
?You won't be able to bypass this behavior of
Autofac
.To change this behavior, you will need to change the way your code works. A constructor is not intended to take time. I recommend you to change your constructor to do only required things, if some of initialization process takes time I would defer it or refactor the code to ensure that constructor takes only few milliseconds to complete.
Your core code should not rely on dependency injection component. In your case, it looks like you use the
IFrameworkDependencyResolver
interface to lazy load component or to have a factory component. You should rely onLazy<T>
ofFunc<T>
instead. See implicit relation type for more information.