Reputation: 2510
Problem:
Assume the class:
public class MyAwesomeClass
{
private IDependCls _dependCls;
public MyAwesomeClass(IDependCls dependCls)
{
_dependCls = dependCls;
}
}
And somewhere else I need to get an instance of that class, like so:
public class SomewhereElse
{
public void AwesomeMethod()
{
//...
// AwesomeStuff
//...
var GetErDone = new MyAwesomeClass(); // PROBLEM! No constructor with 0 arguements
}
}
Question is, do I
Proposed solution 1:
A) have to make an extra constuctor that resolves the dependency? For example:
public MyAwesomeClass() // new constructor
{
_dependCls = DependencyResolver.Current.GetService<IDependCls>();
}
public class SomewhereElse
{
public void AwesomeMethod()
{
var GetErDone = new MyAwesomeClass(); // IT WORKS!!
}
}
Proposed solution 2:
B) use the resolver inside AwesomeMethod
right before var GetErDone
public class SomewhereElse
{
public void AwesomeMethod()
{
var depCls = _dependCls = DependencyResolver.Current.GetService<IDependCls>();
var GetErDone = new MyAwesomeClass(depCls); // IT WORKS!!
}
}
Autofac solution?
C) Some other Autofac way?
Looking for best practices, as well as a good Autofac solution if possible. I think the first way is the worst as optional dependancies could lead to a lot of clutter.
Summary:
How do I get a new MyAwesomeClass()
when MyAwesomeClass
has dependencies?
Upvotes: 17
Views: 24859
Reputation: 19
You can simply use the ILifetimeScope.BeginLifetimeScope
method to create a new temporary scope which allows you to simply register the service and then resolve it.
public static class ContainerExtensions
{
public static T CreateInstance<T>(this ILifetimeScope lifetimeScope)
where T : notnull
{
return (T)lifetimeScope.CreateInstance(typeof(T));
}
public static object CreateInstance(this ILifetimeScope lifetimeScope, Type serviceType)
{
using var temporaryLifetimeScope = lifetimeScope.BeginLifetimeScope(builder =>
{
builder.RegisterType(serviceType)
.As(serviceType)
.ExternallyOwned();
});
return temporaryLifetimeScope.Resolve(serviceType);
}
}
Then you can simply call ILifetimeScope.CreateInstance<MyAwesomeClass>
to create an instance of the class.
In order for this to work you need to inject the ILifetimeScope
into your SomewhereElse
class (or provide that instance statically).
Unrelated: If you are registering a service with ILifetimeScope.Register(Func<IComponentContext, IEnumerable<Parameter>, T)
and only have an IComponentContext
and no ILifetimeScope
, you can simply resolve it from the component context.
ActivatorUtilities
as a workaroundThis was my original answer, but I no longer feel that this is a good solution because it has slightly different semantics from a regular resolve call (e.g. internal constructors are not discovered).
You can simply use the existing ActivatorUtilities
class to achieve this. To be able to use this class, you need to implement IServiceProvider
. Then you can simply provide an extension method.
internal class ContainerWrapper : IServiceProvider
{
private readonly IComponentContext _context;
public ContainerWrapper(IComponentContext context)
{
_context = context;
}
public object? GetService(Type serviceType)
{
// The method must return null if the service is not registered, otherwise, 'ActivatorUtilities.CreateInstance' will throw an exception
_context.TryResolve(serviceType, out var service);
return service;
}
}
public static class ContainerExtensions
{
public static T CreateInstance<T>(this IComponentContext context)
{
return (T)ActivatorUtilities.CreateInstance(new ContainerWrapper(context), typeof(T));
}
public static object CreateInstance(this IComponentContext context, Type type)
{
return ActivatorUtilities.CreateInstance(new ContainerWrapper(context), type);
}
}
Upvotes: 0
Reputation: 111
You can create a new instance of 'MyAwesomeClass' with reflection, resolving the constructor parameters with Autofac.
public static T Instance<T>() where T : class
{
Type instanceType = typeof(T);
ConstructorInfo constructorInfo = instanceType.GetConstructors()[0];
ParameterInfo[] constructorParamsInfo = constructorInfo.GetParameters();
object[] constructorParams = new object[constructorParamsInfo.Length];
for (int i = 0; i < constructorParamsInfo.Length; i++)
{
var parameterInfo = constructorParamsInfo[i];
var type = parameterInfo.ParameterType;
constructorParams[i] = Container.Resolve(type);
}
object instance = Activator.CreateInstance(instanceType, constructorParams);
return (T)instance;
}
Upvotes: 1
Reputation: 371
I know that this question is old, but I found a quite useful link on the autofac documentation describing dynamic instantiation of classes.
Perhaps it could be helpful for someone.
Upvotes: 0
Reputation: 1714
Have a look at the Composition Root pattern.
You are right, pulling up the dependency resolution only moves the problem to another place. If you continue to move it upwards in your object graph, though, you will reach the entry point of your application. There you will compose your object graph.
Compare that to the Service Locator anti-pattern (using DependencyResolver in client classes in your case) and you will see that Composition Root is a superior solution.
Upvotes: 11
Reputation: 3026
First of all apart from constructor injection you can also use property injection and method injection. However constructor injection is most common and the fastest method so I suggest to stick to it.
The second thing you need to do is to register your MyAwesomeClass
in the Autofac container along with its dependency, they have some nice examples right at their home page.
And the last thing - you should not create instances of MyAwesomeClass
directly - use Autofac instead. Here is an updated example:
public void AwesomeMethod()
{
//...
// AwesomeStuff
//...
var GetErDone = DependencyResolver.Current.GetService<MyAwesomeClass>();
}
Upvotes: 1
Reputation: 33940
In the class containing MyAwesomeMethod
take MyAwesomeClass
as a constructor dependency. Autofac will take care of the instantiation.
Upvotes: 0
Reputation: 331
If you want to resolve instance automatically via Autofac, you can only choose from this
Inject in property, by using
var builder = new ContainerBuilder();
builder.RegisterType<Foo>().PropertiesAutowired();
Use global access by DependencyResolver.Current.GetService<IFoo>();
Upvotes: 0