Export generics using MEF

Mar 18, 2009 at 8:51 AM
In my project I have defined generic interface as contract:

    [ContractType]
    public interface IDomainController<T>
    {
        IList<T> GetAll();
        void Add(T entity);
        void Update(T entity);
        void Delete(T entity);
        void SaveChanges();
        void Refresh(RefreshMode refreshMode, T item);
    }

Then I have created generic implementation for this interface and exported it using MEF:

    [Export(typeof(IDomainController<>)), CompositionOptions(CreationPolicy = CreationPolicy.Shared)]
    public class DomainController<T> : IDomainController<T>
    {
/* some code here */
[ImportingConstructor]
public DomainController(IRepository repository)
{
 _Repository = repository
}
    }

I have used the following code to create export in catalog:

            // Prepare catalog
            var catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
            catalog.Catalogs.Add(new AssemblyCatalog(typeof(DomainContext).Assembly));
            catalog.Catalogs.Add(new AssemblyCatalog(typeof(IRepository).Assembly));

            // Issue: Doesn't work on generic
            Type type = typeof (IDomainController<>);
            Assembly assembly = type.Assembly;
            AssemblyCatalog assemblyCatalog = new AssemblyCatalog(assembly);
            catalog.Catalogs.Add(assemblyCatalog);

But it hasn't created any exported parts for the last AssemblyCatalog, the same result is for TypeCatalog. For previous catalogs exported parts are created successfully according to attributed definitions.I don't know what problem is. Does MEF support this way for export at all? Can I map contract to generic implementation?

P.S. Most DI tools incuding Unity work properly in same case.

Mar 18, 2009 at 9:36 AM
Hi Andrew

We do not currently support open generics, though we do support closed generics.

The reasoning is this, MEF parts relate on contracts which are strings, not types. To illustrate, see the code below.

namespace Orders {

  public interface IOrderRepository {}

  [Export(typeof(IOrderRespository))]
  public class OrderRepository : IOrderRepository {
  }
}

Although I have passed typeof(IOrderRepsitory) to the export, that is just a convenience that gets converted to a contract name of "Orders.IOrderRepository". The same applies to the importer...

[Export]
public class OrderService(
  [Import]
  public IOrderRepository Repository {gets;set;} 
)

The import on IOrderRepository also gets converted to a contract name of "Orders.IOrderRepository". This way the container is able to satisfy the import as the 2 contracts match. In the same way we support closed generics, so....

public interface IRepository<T> {}

namespace Orders {

  [Export(typeof(IRepository<Order>))]
  public class OrderRepository : IRepository<Order> {
  }
}

[Export]
public class OrderService(
  [Import]
  public IRepository<Order> Repository {gets;set;} 
)

Will work because the OrderRepository is exporting the contractname "Orders.IRepository<Order>" and the OrderService is importing the same contract.

However, this is what it looks like if we try the same with open generics.

public interface IRepository<T> {}

namespace Utils {

  [Export(typeof(IRepository<>))]
  public class Repository : IRepository<T> {
  }
}

[Export]
public class OrderService(
  [Import]
  public IRepository<Order> Repository {gets;set;} 
)

Now the contract names will be different. The exporter will have a contract of  "Utils.IRepository<>" and the importer will have a contract of "Utils.IRepository<Order>".

It is a simple match up, that breaks down in the open-generics case. This is because fundamentally, MEF is not matching on type.

Thanks for the feedback though. We may be able to proivde a solution to this at some point in the future.

Glenn
Mar 18, 2009 at 12:27 PM
Thanks, Glenn

Will work around this limitation

Andrew

2009/3/18 gblock <notifications@codeplex.com>

From: gblock

Hi Andrew

We do not currently support open generics, though we do support closed generics.

The reasoning is this, MEF parts relate on contracts which are strings, not types. To illustrate, see the code below.

namespace Orders {

  public interface IOrderRepository {}

  [Export(typeof(IOrderRespository))]
  public class OrderRepository : IOrderRepository {
  }
}

Although I have passed typeof(IOrderRepsitory) to the export, that is just a convenience that gets converted to a contract name of "Orders.IOrderRepository". The same applies to the importer...

[Export]
public class OrderService(
  [Import]
  public IOrderRepository Repository {gets;set;} 
)

The import on IOrderRepository also gets converted to a contract name of "Orders.IOrderRepository". This way the container is able to satisfy the import as the 2 contracts match. In the same way we support closed generics, so....

public interface IRepository<T> {}

namespace Orders {

  [Export(typeof(IRepository<Order>))]
  public class OrderRepository : IRepository<Order> {
  }
}

[Export]
public class OrderService(
  [Import]
  public IRepository<Order> Repository {gets;set;} 
)

Will work because the OrderRepository is exporting the contractname "Orders.IRepository<Order>" and the OrderService is importing the same contract.

However, this is what it looks like if we try the same with open generics.

public interface IRepository<T> {}

namespace Utils {

  [Export(typeof(IRepository<>))]
  public class Repository : IRepository<T> {
  }
}

[Export]
public class OrderService(
  [Import]
  public IRepository<Order> Repository {gets;set;} 
)

Now the contract names will be different. The exporter will have a contract of  "Utils.IRepository<>" and the importer will have a contract of "Utils.IRepository<Order>".

It is a simple match up, that breaks down in the open-generics case. This is because fundamentally, MEF is not matching on type.

Thanks for the feedback though. We may be able to proivde a solution to this at some point in the future.

Glenn

Read the full discussion online.

To add a post to this discussion, reply to this email (MEF@discussions.codeplex.com)

To start a new discussion for this project, email MEF@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com


Mar 18, 2009 at 4:07 PM
Hi Andrew

There is one option you could pursue, but I warn you, it's a bit of hack. Oour container accepts ExportProviders in the constructor when you create it. EPs allow you to tap in to the resolving process of the container. You can conceptually write an export provider that parses contracts as they come in, and if you see a request whose contract name is generic, then you can create the closed generic version and return it. The devil is in the details on this, because generics can be nested, and you would have to parse the contracts yourself, and then create the appropriate closed generics.

I have a blog post on ExportProviders that you can look to if you are interested in exploring this path...http://codebetter.com/blogs/glenn.block/archive/2008/12/25/using-exportprovider-to-customize-container-behavior-part-i.aspx

Regards
Glenn



Aug 20, 2009 at 8:05 PM

Hi Andrew

I just posted on an implementation of open generics for MEF which was added to MEF Contrib. Check it out here.

http://codebetter.com/blogs/glenn.block/archive/2009/08/20/open-generic-support-in-mef.aspx

Regards

Glenn

Aug 21, 2009 at 6:53 AM
Thanks, Glenn

I will take a look.

Andrew

2009/8/20 gblock <notifications@codeplex.com>

From: gblock

Hi Andrew

I just posted on an implementation of open generics for MEF which was added to MEF Contrib. Check it out here.

http://codebetter.com/blogs/glenn.block/archive/2009/08/20/open-generic-support-in-mef.aspx

Regards

Glenn

Read the full discussion online.

To add a post to this discussion, reply to this email (MEF@discussions.codeplex.com)

To start a new discussion for this project, email MEF@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com


Jun 16, 2010 at 4:00 PM

Hello,

I am not being able to get this work, even from the latest SVN of MEFContrib.

The contract name of the Export is always different from the Import one which results in composition error (No valid exports found...)

 

Export's contract name is "MEFGeneric.IRepository()".

Import's one is "MEFGeneric.IRepository(MEFGeneric.Order)"

 

Can someone please tell me why this is so?

Note: I am using MEF from .NET 4 RTM.

Thanks.