Mihalis Bagos
Mihalis Bagos

Reputation: 2510

Create instance of a class with dependencies using Autofac

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

Answers (7)

user272507
user272507

Reputation: 19

Solution 1: Create a new lifetime scope

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.

Solution 2: Use ActivatorUtilities as a workaround

This 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

Jose
Jose

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

canvee
canvee

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.

Autofac Dynamic Instantiation

Perhaps it could be helpful for someone.

Upvotes: 0

Filippo Pensalfini
Filippo Pensalfini

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

Sergey Rybalkin
Sergey Rybalkin

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 MyAwesomeClassin 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

Peter Lillevold
Peter Lillevold

Reputation: 33940

In the class containing MyAwesomeMethod take MyAwesomeClass as a constructor dependency. Autofac will take care of the instantiation.

Upvotes: 0

Vitaly Zemlyansky
Vitaly Zemlyansky

Reputation: 331

If you want to resolve instance automatically via Autofac, you can only choose from this

  • Inject in constructor of your class
  • Inject in property, by using

    var builder = new ContainerBuilder();

    builder.RegisterType<Foo>().PropertiesAutowired();

  • Use global access by DependencyResolver.Current.GetService<IFoo>();

Upvotes: 0

Related Questions