Reputation: 380
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
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]
Upvotes: 1