New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open generics cannot be resolved #4

Closed
kayanme opened this Issue Feb 24, 2016 · 8 comments

Comments

Projects
None yet
2 participants
@kayanme

kayanme commented Feb 24, 2016

Consider the next example:

[Export(typeof(A<>))
public class A<T>
{
      [ImportingConstructor]
      public A(T y)
      {
      }
}

[Export]
public class B
{  
}

[Export]
public class C
{
   [ImportingConstructor]
   public C(A<B> a)
   {
   }
}

All registered in MEF catalog, of course.
While trying to resolve C by AutoFac from that catalog a error "Service not registered" occures about class B, but in MEF it is resolved ok.

@tillig

This comment has been minimized.

Show comment
Hide comment
@tillig

tillig Feb 25, 2016

Contributor

Acknowledged / reproduced. I'm adding a failing unit test that runs this scenario and we'll see what we can figure out. Thanks for the report!

Contributor

tillig commented Feb 25, 2016

Acknowledged / reproduced. I'm adding a failing unit test that runs this scenario and we'll see what we can figure out. Thanks for the report!

@tillig

This comment has been minimized.

Show comment
Hide comment
@tillig

tillig Feb 25, 2016

Contributor

Looking at it, this isn't going to be a simple one to resolve. The problem is that MEF uses string-based contract names to resolve things. For example, if you do this:

[Export]
public class B
{  
}

It's sort of like registering a named service, like:

builder.RegisterType<B>().Named<B>("MyNamespace.B");

MEF uses the [ImportingConstructor] thing to line up contracts by name, so when it sees this:

[Export]
public class C
{
   [ImportingConstructor]
   public C(B b)
   {
   }
}

It wires that constructor up to the appropriately named service. That named vs. typed thing is important, because it means for open generics there's something inside MEF that dynamically allows an export like [Export(typeof(A<>))] to generate contract name registrations like MyNamespace.A(MyNamespace.B) and MyNamespace.A(MyNamespace.C). Note the names of the exposed services are changing there based on the generic type parameter.

In fact, in the failing unit test, the ComponentNotRegisteredException gives a message illustrating that: The requested service 'ContractName=Autofac.Integration.Mef.Test.GenericExportRegistrationTests+OpenGenericExport(Autofac.Integration.Mef.Test.GenericExportRegistrationTests+SimpleType)' has not been registered. MEF imports want a named service associated with a closed generic that just isn't registered.

When Autofac registers an open generic, it has to add a registration source to provide that dynamic functionality so when someone asks for A<B> the source can say, "yes, I know about that and can fulfill the requirement."

We don't have an open generic registration source that also dynamically adds MEF-compatible contract names to open generic registrations, so when a MEF-loaded export tries to import something based on an open generic, it fails (as you noticed).

Autofac has a lot of code built up to support open generics and doesn't inherently support dynamically named services associated with an open generic. Also, most of that code is internal to core Autofac since it's not really in a state where we want folks depending on it directly or extending it.

Most likely fixing this issue will require a MEF-specific registration source along with some code to try and marry up the use of the standard ContainerBuilder.RegisterGeneric call. Figuring that out is probably not going to be a quick fix.

Contributor

tillig commented Feb 25, 2016

Looking at it, this isn't going to be a simple one to resolve. The problem is that MEF uses string-based contract names to resolve things. For example, if you do this:

[Export]
public class B
{  
}

It's sort of like registering a named service, like:

builder.RegisterType<B>().Named<B>("MyNamespace.B");

MEF uses the [ImportingConstructor] thing to line up contracts by name, so when it sees this:

[Export]
public class C
{
   [ImportingConstructor]
   public C(B b)
   {
   }
}

It wires that constructor up to the appropriately named service. That named vs. typed thing is important, because it means for open generics there's something inside MEF that dynamically allows an export like [Export(typeof(A<>))] to generate contract name registrations like MyNamespace.A(MyNamespace.B) and MyNamespace.A(MyNamespace.C). Note the names of the exposed services are changing there based on the generic type parameter.

In fact, in the failing unit test, the ComponentNotRegisteredException gives a message illustrating that: The requested service 'ContractName=Autofac.Integration.Mef.Test.GenericExportRegistrationTests+OpenGenericExport(Autofac.Integration.Mef.Test.GenericExportRegistrationTests+SimpleType)' has not been registered. MEF imports want a named service associated with a closed generic that just isn't registered.

When Autofac registers an open generic, it has to add a registration source to provide that dynamic functionality so when someone asks for A<B> the source can say, "yes, I know about that and can fulfill the requirement."

We don't have an open generic registration source that also dynamically adds MEF-compatible contract names to open generic registrations, so when a MEF-loaded export tries to import something based on an open generic, it fails (as you noticed).

Autofac has a lot of code built up to support open generics and doesn't inherently support dynamically named services associated with an open generic. Also, most of that code is internal to core Autofac since it's not really in a state where we want folks depending on it directly or extending it.

Most likely fixing this issue will require a MEF-specific registration source along with some code to try and marry up the use of the standard ContainerBuilder.RegisterGeneric call. Figuring that out is probably not going to be a quick fix.

@kayanme

This comment has been minimized.

Show comment
Hide comment
@kayanme

kayanme Feb 27, 2016

It seems right.
By the way, can't we use name-style mef registration to extract autofac pure-type-style one? Parsing strings to types or something, not just translating the string metadata. Sorry, never looked through your code...

And another thing - the main reason for all of this combining stuff from mef ot autofac is to make a fully compatible integration from the third-side product (with mef-based composition) to ours, which should resolve everything from that third-party the similar way it were there into us.
I opened one more issue, which broke this kind of an integration.

kayanme commented Feb 27, 2016

It seems right.
By the way, can't we use name-style mef registration to extract autofac pure-type-style one? Parsing strings to types or something, not just translating the string metadata. Sorry, never looked through your code...

And another thing - the main reason for all of this combining stuff from mef ot autofac is to make a fully compatible integration from the third-side product (with mef-based composition) to ours, which should resolve everything from that third-party the similar way it were there into us.
I opened one more issue, which broke this kind of an integration.

@tillig

This comment has been minimized.

Show comment
Hide comment
@tillig

tillig Feb 27, 2016

Contributor

I'm not sure I understand what you're asking for in the "name-style MEF registration" question.

Also, have you tried this package from MefContrib? It may do what you want.

Contributor

tillig commented Feb 27, 2016

I'm not sure I understand what you're asking for in the "name-style MEF registration" question.

Also, have you tried this package from MefContrib? It may do what you want.

@kayanme

This comment has been minimized.

Show comment
Hide comment
@kayanme

kayanme Feb 27, 2016

I'll try to explain.
If I got things right, the main problem with open generics through MEF is that MEF uses strings as a keys to its' services, even for types, instead of real type references. So resolving of an open generic uses a string combination rather than a type composition, like Autofac does.
And Autofac can not produce new implicit registrations by combining strings - types only.
If this is so, the question is - can we make service registrations from MEF not just by simply taking their strings and putting them into Autofac, but by converting them (strings) to types and use that types?
Sorry if I didn't get the point.

I'll check the package, thank you.

kayanme commented Feb 27, 2016

I'll try to explain.
If I got things right, the main problem with open generics through MEF is that MEF uses strings as a keys to its' services, even for types, instead of real type references. So resolving of an open generic uses a string combination rather than a type composition, like Autofac does.
And Autofac can not produce new implicit registrations by combining strings - types only.
If this is so, the question is - can we make service registrations from MEF not just by simply taking their strings and putting them into Autofac, but by converting them (strings) to types and use that types?
Sorry if I didn't get the point.

I'll check the package, thank you.

@tillig

This comment has been minimized.

Show comment
Hide comment
@tillig

tillig Feb 27, 2016

Contributor

I see what you're asking. Autofac does have named service registration support and we use that in the MEF integration. Given the way part catalogs work, how contracts on services in the catalog are string-based, we sort of have to do it the way we're doing it.

Contributor

tillig commented Feb 27, 2016

I see what you're asking. Autofac does have named service registration support and we use that in the MEF integration. Given the way part catalogs work, how contracts on services in the catalog are string-based, we sort of have to do it the way we're doing it.

@tillig

This comment has been minimized.

Show comment
Hide comment
@tillig

tillig Oct 27, 2016

Contributor

I'll leave this open in case someone wants to submit a PR for it, but in the meantime I updated the Autofac + MEF docs to include this as a known issue.

Contributor

tillig commented Oct 27, 2016

I'll leave this open in case someone wants to submit a PR for it, but in the meantime I updated the Autofac + MEF docs to include this as a known issue.

@tillig

This comment has been minimized.

Show comment
Hide comment
@tillig

tillig Oct 27, 2017

Contributor

This has been open for a year with no PRs coming in; I'm going to close it since it's documented as a known issue. Should a PR come in later, we would totally consider accepting it.

Contributor

tillig commented Oct 27, 2017

This has been open for a year with no PRs coming in; I'm going to close it since it's documented as a known issue. Should a PR come in later, we would totally consider accepting it.

@tillig tillig closed this Oct 27, 2017

@tillig tillig added the wontfix label Oct 27, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment