I think I am really confused about what I can do with AutoFac, can someone please put me on track.
I have a base type
class PersonBase{
public string SaySomething(){
return "I am base";
}
}
I derive two concrete classes
class FakePerson : PersonBase{
public override string SaySomething(){
return "I'm so Fake";
}
}
class RealPerson : PersonBase{
public override string SaySomething(){
return "I am For Real";
}
}
Create a generic class, PersonHandler, to deal with different types of people and would like the PersonHandler to instantiate the person at the appropriate time, so I do not want an instance of Person injected, just need to derived type
class PersonHandler<T>
where T : PersonBase, new() {
T _Person;
public DoWork(){
_Person = new T();
_Person.SaySomething();
}
}
Now I try to use the handler, after registering the types as detailed next, with varying results.
var ph = contrainer.Resolve<PersonHandler<PersonBase>>();
ph.DoWork();
I attempted to register the types as follows
1. vBuilder.RegisterType<PersonHandler<FakePerson>>().As<PersonHandler<PersonBase>>();
This gives me an error stating the PersonHandler<FakePerson>
is not assignable to PersonHandler<PersonBase>
(or the other way around I don't recal which)
2. vBuilder.RegisterGeneric<typeof(PersonHandler<>)>
vBuilder.RegisterType<FakePerson>().As<PersonBase>();
This does not resolve PersonBase
to FakePerson
, but just gives PersonHandler<PersonBase>
, So it results in "I am Base"
3. vBuilder.RegisterGeneric(typeof(PersonHandler<FakePerson>)).As(typeof(PersonHandler<PersonBase>));
This given an error saying that PersonHandler<FakePerson>
is not an open type
So now I have been chasing my tale all day and, frankly, it is getting quit tedious,
PLEASE HELP
The (almost) correct solution is this one that you already tried:
The reason Autofac gave you an error is that generic class types in C# don't work this way.
That is, you can't write:
(Give it a try in your IDE - the compiler will reject it.)
The reason for this is that contravariance, the required feature added in C#4, only supports interface types.
To cut a long story short, if you create
IPersonHandler<T>
and use that as the service then the above code will work as expected:Note the
in
parameter on the interface declaration.Make
PersonHandler<T>
implementIPersonHandler<T>
:Then register like so:
You can then get handlers using:
The returned
handler
object will be of typePersonHandler<FakePerson>
.When you resolve a service that is registered with an open generic type (such as
PersonHandler<>
) you need to specify the exact closed type that you want to handle, and Autofac will generate one for you.In this case, you called
container.Resolve<PersonHandler<PersonBase>>()
so you were returned aPersonHandler<PersonBase>
. Now, if you look at yourPersonHandler
class, and substitutePersonBase
forT
, the resultingDoWork
method looks like this:So the
SaySomething()
call uses the method on the base class.I'm not sure exactly what you are trying to do, but it looks like you want to decide which concrete
PersonBase
implementation to use during registration. If this is the case, you might try using the Delegate Factory feature of Autofac instead:Then you can register your classes as follows:
By varying the second line of this code, you can register whichever type of
PersonBase
you actually want to use in the program.