Reputation: 461
I'm new to Autofac and having some trouble with resolving a nested open generic service type.
I would like my ContactService
to implement two interfaces. Both interfaces make use of the same generic type parameter, but the second is nested:
class ContactService<TParent>
: IContactService<TParent>,
IFeatureProvider<ContactFeature<TParent>>
where TParent : class, IDomainModel
{ }
If I do this:
builder
.RegisterGeneric(typeof(ContactService<>))
.As(typeof(IContactService<>))
.As(typeof(IFeatureProvider<>);
Then trying to resolve the service IFeatureProvider<ContactFeature<Household>>
(for example) causes Autofac to attempt to instantiate
ContactService<ContactFeature<Household>>
instead of
ContactService<Household>
I understand why the above doesn't work. So then I tried this instead:
builder
.RegisterGeneric(typeof(ContactService<>))
.As(typeof(IContactService<>))
.As(typeof(IFeatureProvider<>)
.MakeGenericType(typeof(ContactFeature<>)));
However, I still get a similar error:
TypeLoadException: GenericArguments[0], 'ContactFeature`1[Household]',
on 'Contact`1[TParent]' violates the constraint of type parameter 'TParent'.
System.RuntimeTypeHandle.Instantiate(RuntimeTypeHandle handle, IntPtr* pInst,
Int32 numGenericArgs, ObjectHandleOnStack type)
ArgumentException: GenericArguments[0], 'ContactFeature`1[Household]',
on 'IContactService`1[TParent]' violates the constraint of type 'TParent'.
System.RuntimeType.ValidateGenericArguments(MemberInfo definition,
RuntimeType[] genericArguments, Exception e)
It looks like Autofac is trying to pass ContactFeature<Household>
as TParent
instead of just Household
.
Sorry for the long explanation. I hope someone can help me get this to work!
Thank you.
Upvotes: 1
Views: 533
Reputation: 3329
It seems all you have to do is to split you open generic registration to 2 separate registrations, like that:
var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(ContactService<>))
.As(typeof(IContactService<>));
builder.RegisterGeneric(typeof(ContactService<>))
.As(typeof(IFeatureProvider<>));
var container = builder.Build();
var service = container.Resolve(typeof(IFeatureProvider<ContactFeature<Household>>));
var service2 = container.Resolve(typeof(IContactService<Household>));
Console.WriteLine(service.GetType());
Console.WriteLine(service2.GetType());
Console.ReadKey();
Both resolves would give you instances of ContactService<Household>
. But to be honest, I do not know why it works separately, and does not work, when you put it together in single registration.
I used the following classes and interfaces to test the solution:
class ContactService<TParent> : IContactService<TParent>, IFeatureProvider<ContactFeature<TParent>>
where TParent : class, IDomainModel
{
}
interface IContactService<T> where T : class, IDomainModel
{
}
interface IFeatureProvider<TProvider>
{
}
interface IDomainModel
{
}
class Household : IDomainModel
{
}
class ContactFeature<TDomainModel> where TDomainModel: class, IDomainModel
{
}
Upvotes: 2