Arash EM
Arash EM

Reputation: 380

How can I resolve a class with 3rd party generic interface by string using autofac

There are different classes registered in autofac with same generic interface but using different class models. What I need is to resolve the class with string without knowing the class model. I am not sure if it can easily be achieved. Any suggestions please.

I am using a console app and I don't know the class model as we receive a string. I have registered all class in the start class but I want to resolve the class without knowing the class model. I don't want to use if else to say if string == "something" then resolve this else if resolve this. I am looking for the best practice.

public TestModel1 { }
public TestModel2 { }

public interface ICalculator<string, T> {}
public Impl1: ICalculator<string, TestModel1> {}
public Impl2: ICalculator<string, TestModel2> {}

in Autofac I register like:

builder.Register<Impl1>.As<ICalculator<string, TestModel1>>();
builder.Register<Impl2>.As<ICalculator<string, TestModel2>>();

in Console app I want to do something like: container.Resolve by string rather than doing like

if (...) {
class = container.resolve<ICalculator<string, TestModel1>>()
} else {
class = container.resolve<ICalculator<string, TestModel2>>()
}

I want to be able to do something like in console app:

var class = container.resolve<ICalculator>("pass string");

Upvotes: 1

Views: 166

Answers (1)

Erik Philips
Erik Philips

Reputation: 54618

AFAIK You can only resolve what you register. SO you can't get an ICalculator from a ICalculator<string, TestModel2> because those are two very different interfaces.

Use Named Services. You can either do:

Assuming:

public class Impl1 : ICalculator<string, TestModel1> {}
public class Impl2 : ICalculator<string, TestModel1> {}
// Notice these are the same ---------------------^
// If these need to be different use #2 (Below)

Register:

builder.Register<Impl1>.Named<ICalculator<string, TestModel1>>("pass string1");
builder.Register<Impl2>.Named<ICalculator<string, TestModel1>>("pass string2");
// Notice these are the same ------------------------------^

Resolved it via:

var r = container.resolve<ICalculator<string, TestModel1>>("pass string2) 
// = Impl2

OR 2.

First create a base interface:

public interface ICalculator {}
public interface ICalculator<T1, T2> : ICalcuator;

public class Impl1 : ICalculator<string, TestModel1> {}
public class Impl2 : ICalculator<string, TestModel2> {}

Register:

builder.Register<Impl1>.Named<ICalculator>("pass string1");
builder.Register<Impl2>.Named<ICalculator>("pass string2");

Resolved it via"

var r = container.resolve<ICalculator>("pass string2) 
// = Impl2

You should really consider using scopes to help control the lifetime of your objects if your console app (or real app) is very complex:

builder
  .Register<Impl1>
  .Named<ICalculator<string, TestModel1>>("pass string1")
  .InstancePerLifetimeScope();
builder
  .Register<Impl2>
  .Named<ICalculator<string, TestModel1>>("pass string2")
  .InstancePerLifetimeScope();

then

using (scope1 = container.BeginLifetimeScope())
{
  var r = scope1.Resolve<ICalculator>("pass string1");
  // r would be disposed if it implemented IDisposable
  // before scope1 is `Disposed()`
}

However, the problem I face is that the interface is coming from a library developed by another team.

In that case you could try registering generic, and let the DI know the Generic types at resolution instead of during implementation:

// @nuget: Autofac

using System;
using Autofac;

public class Program
{
    private static IContainer _Container;
    public static void Main()
    {
        var builder = new ContainerBuilder();

        builder
            .RegisterGeneric(typeof (Impl1<, >))
            .Named("pass string1", typeof (Calculator3rdParty<, >))
            .InstancePerLifetimeScope();

        builder
            .RegisterGeneric(typeof (Impl2<, >))
            .Named("pass string2", typeof (Calculator3rdParty<, >))
            .InstancePerLifetimeScope();

        _Container = builder.Build();

        using (var scope = _Container.BeginLifetimeScope())
        {
            var c3p = scope
                .ResolveNamed<Calculator3rdParty<string, Model1>>("pass string2");
            // specify type at resolve ----------^

            Console.WriteLine(c3p.GetType());
        }
    }
}

public interface Calculator3rdParty<T1, T2>{}

public class Model1{}

public class Model2{}

public class Impl1<T1, T2> : Calculator3rdParty<T1, T2>{}

public class Impl2<T1, T2> : Calculator3rdParty<T1, T2>{}

Output:

Impl2`2[System.String,Model1]

DotNetFiddle Example

Upvotes: 1

Related Questions