A question came up on the MEF discussion board recently (today?) about how to handle a complex graph of dependencies. Although it was "solved" - and I suspect it was a case of missing the required assembly, going by what info was at hand - it still prompted me to dig into how one can go beyond the basics.
Imagine you have an application that calls off different services to execute tasks. Rather than hard-coding the services into the application, these can be defined as parts which are composed at runtime using MEF.
But the relationship between the application and the services is not straightforward, which gives a dependency graph similar to the below image:

What should we do now?
As MEF uses the concept of a "contract" to resolve the [Import] and [Export]; statements sprinkled within an application, this ultimately comes down to two similar approaches.
If the Proxy and Service implementations are equivalent - so we can avoid using distinct interfaces for behaviour which is identical - then we can use the same interface and specify a different contract for each extensibiity point defined in the application.
public class ConsumingApplication
{
[ImportMany("Contoso.Application", typeof(IServiceProxy))]
public IEnumerable<IServiceProxy> Services { get; set; }
// implementation here
}
And our simple client can use the corresponding [Export] statement.
[Export("Contoso.Application", typeof(IServiceProxy))]
public class StandaloneProxy : IServiceProxy
{
// implementation here
}
Our complex dependency has a bit more code, but it can be broken down into two main features:
The contract which it satisfies - Contoso.Application and IServiceProxy
The contract which it requires - Contoso.External and IServiceProxy
[Export("Contoso.Application", typeof(IServiceProxy))]
public class ActualProxy : IServiceProxy
{
[ImportMany("Contoso.External")]
public IEnumerable<IServiceProxy> Services { get; set; }
// implementation here
}
So while reusing the same interface, we can specify how the parts relate.
If the behaviour of the proxy and the actual service are different, then we can just use the types to represent the contract.
public class ConsumingApplication
{
[ImportMany(typeof(IServiceProxy))]
public IEnumerable<IServiceProxy> Services { get; set; }
// implementation here
}
The exported contract becomes:
[Export(typeof(IServiceProxy))]
public class StandaloneProxy : IServiceProxy
{
// implementation here
}
And our complex part still has two contracts:
The contract which it satisfies - the IServiceProxy contract.
The contract which it requires - the IService contract (implicit due to the awesomeness of ImportMany)
[Export(typeof(IServiceProxy))]
public class ActualProxy : IServiceProxy
{
[ImportMany]
public IEnumerable<IService> Services { get; set; }
// implementation here
}
No more magic strings, while still being able to declare
To really simplify the contracts, you can use [InheritedExport] on the interface. This declares to MEF that all types which implement the interface should be used as exported parts, using the interface type as the contract.
So I annotate both interfaces:
[InheritedExport]
public interface IServiceProxy
{
// code here
}
[InheritedExport\
public interface IService
{
// code here
}
and can eliminate all other [Export] attributes from the codebase:
public class StandaloneProxy : IServiceProxy
{
// implementation here
}
public class ActualProxy : IServiceProxy
{
[ImportMany]
public IEnumerable<IService> Services { get; set; }
// implementation here
}
Thoughts?