I don't understand MEF very well, so hopefully this is a simple fix of how I think it works.
I'm trying to use MEF to get some information about a class and how it should be used. I'm using the Metadata options to try to achieve this. My interfaces and attribute looks like this:
public interface IMyInterface
{
}
public interface IMyInterfaceInfo
{
Type SomeProperty1 { get; }
double SomeProperty2 { get; }
string SomeProperty3 { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ExportMyInterfaceAttribute : ExportAttribute, IMyInterfaceInfo
{
public ExportMyInterfaceAttribute(Type someProperty1, double someProperty2, string someProperty3)
: base(typeof(IMyInterface))
{
SomeProperty1 = someProperty1;
SomeProperty2 = someProperty2;
SomeProperty3 = someProperty3;
}
public Type SomeProperty1 { get; set; }
public double SomeProperty2 { get; set; }
public string SomeProperty3 { get; set; }
}
The class that is decorated with the attribute looks like this:
[ExportMyInterface(typeof(string), 0.1, "whoo data!")]
[ExportMyInterface(typeof(int), 0.4, "asdfasdf!!")]
public class DecoratedClass : IMyInterface
{
}
The method that is trying to use the import looks like this:
private void SomeFunction()
{
// CompositionContainer is an instance of CompositionContainer
var myExports = CompositionContainer.GetExports<IMyInterface, IMyInterfaceInfo>();
}
In my case myExports
is always empty. In my CompositionContainer, I have a Part in my catalog that has two ExportDefinitions
, both with the following ContractName
: "MyNamespace.IMyInterface". The Metadata
is also loaded correctly per my exports.
If I remove the AllowMultiple
setter and only include one exported attribute, the myExports
variable now has the single export with its loaded metadata.
What am I doing wrong?
EDIT: If I use weakly typed Metadata, my export is suddenly satisfied:
var myExports = CompositionContainer.GetExports<IMyInterface, IDictionary<string, object>>();
Any ideas why?
It is known that MEF has some problems when dealing with AllowMultiple = true
. For a complete explanation you could for instance look here, anyway it derives from the fact that the metadata are saved in a Dictionary where the values are arrays when AllowMultiple is true, and such a thing can't be mapped on your IMyInterfaceInfo.
This is the workaround I use. First of all your attribute should derive from Attribute, not from ExportAttribute:
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ExportMyInterfaceAttribute : Attribute, IMyInterfaceInfo
{
public ExportMyInterfaceAttribute(Type someProperty1, double someProperty2, string someProperty3)
{
SomeProperty1 = someProperty1;
SomeProperty2 = someProperty2;
SomeProperty3 = someProperty3;
}
public Type SomeProperty1 { get; set; }
public double SomeProperty2 { get; set; }
public string SomeProperty3 { get; set; }
}
This means that the exported class should have 3 attributes, a standard export and your custom attributes:
[Export(typeof(IMyInterface))]
[ExportMyInterface(typeof(string), 0.1, "whoo data!")]
[ExportMyInterface(typeof(int), 0.4, "asdfasdf!!")]
public class DecoratedClass : IMyInterface
Then you have to define a view for the metadata which will be imported. This has to have a constructor that takes the IDictionary as parameter. Something like this:
public class MyInterfaceInfoView
{
public IMyInterfaceInfo[] Infos { get; set; }
public MyInterfaceInfoView(IDictionary<string, object> aDict)
{
Type[] p1 = aDict["SomeProperty1"] as Type[];
double[] p2 = aDict["SomeProperty2"] as double[];
string[] p3 = aDict["SomeProperty3"] as string[];
Infos = new ExportMyInterfaceAttribute[p1.Length];
for (int i = 0; i < Infos.Length; i++)
Infos[i] = new ExportMyInterfaceAttribute(p1[i], p2[i], p3[i]);
}
}
Now you should be able to successfully call
var myExports = CompositionContainer.GetExports<IMyInterface, MyInterfaceInfoView>();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With