How to use MEF Inherited Export & MetaData? - c#

I have an interface:
[InheritedExport(typeof(IMetric))]
public interface IMetric { ... }
I have a Meta attribute interface:
public interface IMetricAttribute { ... }
and an attribute that implements it:
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MetricAttribute : ExportAttribute, IMetricAttribute {
public string MetricName { get; set; }
public string MetricDescription { get; set; }
public MetricAttribute(string name, string description)
: base(typeof(MetricAttribute)) {
this.MetricName = name;
this.MetricDescription = description;
}
}
I then have two classes:
[Metric("MetricA","MetricA")]
public class MetricA: IMetric { ... }
[Export(typeof(IMetric))] <<<< THIS IS IMPORTANT
[Metric("MetricB", "MetricB")]
public class MetricB: IMetric { ... }
I then try to import the metrics ( i can see both in the cataloge)
The following returns be MetricA AND MetricB
var metrics = compositionContainer.GetExports<IMetric>();
However the following returns ONLY MetricB and NOT MetricA
var metrics = compositionContainer.GetExports<IMetric, IMetricAttribute>();
any idea why?
(note the duplicate export on MetricB (it already has it from implementing IMetric))
thanks
David

First time I've seen this behaviour, but from what I can understand, metadata is generated per-export at the type level. So, given:
[Metric("MetricA", "MetricA")]
public class MetricA : IMetric
{
}
You have two exports for this type. You have the export of MetricA which is implictly provided by your MetricAttribute, and you have the inherited export for IMetric provided by the InheritedExport(typeof(IMetric)) attribute on your interface.
If you look at the container, you'll notice two exports defined for MetricA. Here is the first, with its metadata:
And here is the second:
You'll notice that the metadata is done on the export of MetricA, not the inherited export. If I added a further export, lets say [Export("test")] to MetricA, you get another export definition, with the same metadata items for MetricName and MetricDescription for the contract named "test". This shows you that as the type is analysed, the export attribute is identified, and the export definition that is created includes the metadata specified at the same level in the abstraction tree.
The easiest way to do what you want, is to drop out the InheritedExport, and modify your definition of your MetricAttribute to:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false), MetadataAttribute]
public class MetricAttribute : ExportAttribute, IMetricAttribute
{
public MetricAttribute(string name, string description)
: base(typeof(IMetric))
{
this.MetricName = name;
this.MetricDescription = description;
}
public string MetricName { get; private set; }
public string MetricDescription { get; private set; }
}
Where you then pass in typeof(IMetric) to the base ExportAttribute constructor. You then correctly get the two exports for GetExports<IMetric>() and GetExports<IMetric, IMetricAttribute>().

I came across the same problem and found a differen solution which worked fine for me:
I just added metadata to the interface!
[InheritedExport(typeof(IMetric))]
[Metric("name","description")]
public interface IMetric { ... }
You can leave the fields blank or use null as default, but it is important to specify the Metadata here.
Then you specify your classes without export attribute:
[Metric("MetricA")]
public class MetricA: IMetric { ... }
Be aware that you can can specify just one metadata, but the second one won't be description in this case, it will be null! So the metadata in the interface are NOT default values.
All in all this worked for me and I can use InheritedExport with my metadata :-)

To clarify on Matthew answer:
When you are defining custom metadata attribute class MetricAttribute and inheriting from ExportAttribute, you are essentially adding [Export] attribute to all classes that you decorate with your [Metric] attribute. This means that you don't need [InheritedExport] attribute on the interface anymore as it just creates separate export definition without any metadata.
If you want to create a more reusable metadata attribute, you can expose ExportAttribute constructor parameters in your MetricAttribute as follows:
public MetricAttribute(Type contractType, string name, string description)
: base(contractType)
{
this.MetricName = name;
this.MetricDescription = description;
}
By introducing contractType variable you can now supplement your definition of
[Export(typeof(IMetric))]
[Metric("MetricB", "MetricB")]
public class MetricB: IMetric { ... }
with:
[Metric(typeof(IMetric), "MetricB", "MetricB")]
public class MetricB: IMetric { ... }

Related

MEF GetExports<T>() is not importing correctly

I have base interface for my Metadata interfaces and Attributes.
public interface IBase
{
string Name { get; }
}
public interface IAAAMetaData : IBase
{
string[] Names { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Method)]
public class AAAMetaData : ExportAttribute, IAAAMetaData
{
public AAAMetaData(string contract)
{
Name = contract;
}
public AAAMetaData(string[] contracts)
{
Names = contracts;
}
public string Name { get; set; }
public string[] Names { get; set; }
}
public interface IBBBMetaData : IBase
{
string[] Names { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Method)]
public class BBBMetaData : ExportAttribute, IBBBMetaData
{
public BBBMetaData(string contract)
{
Name = contract;
}
public BBBMetaData(string[] contracts)
{
Names = contracts;
}
public string Name { get; set; }
public string[] Names { get; set; }
}
Here is my plugins:
[AAAMetaData("Test1")]
public void Plugin1(object sender, EventArgs e)
{
sender = "Plugin1";
}
[BBBMetaData("Test2")]
public void Plugin2(object sender, EventArgs e)
{
sender = "Plugin2";
}
Now when I try get exports I am getting wrong result. Here is the code that I am using to get exports:
var exports = _container.GetExports<Action<object, EventArgs>, IAAAMetaData>();
In the result of GetExprts<T>() I am getting two items in the list. If I open the list and see the items inside it imported Plugin2 also. What is wrong in here? IAAAMetaData and IBMetaData are totally different things. You can't even cast IAAAMetaData to IBBBMetaData. Can anyone explain what is going on in here?
Thanks for the help!
The reason behind this is that both metadata interfaces have exactly the same properties. Same type and name for each property. If for example you change your interfaces to:
public interface IAAAMetaData : IBase
{
string[] AAA_Names { get; }
}
public interface IBBBMetaData : IBase
{
string[] BBB_Names { get; }
}
you will get the single export that you expect.
This is kind of explained in the Metadata filtering and DefaultValueAttribute section of Exports and Metadata (CodePlex MEF site):
When you specifiy a metadata view, an implicit filtering will occur to
match only those exports which contain the metadata properties defined
in the view.
Of course this wouldn't happen if the signature of the exported methods were different. Try adding an extra variable and you will get the single export. Also another approach would be to use contract names with ExportAttribute and ImportAttribute.
I wasn't aware that there was a method with multiple generic parameters, but since you're not getting a compiler error I assume there is indeed one. However, the second argument is probably not a contract type. Contracts in MEF are specified by name. Thus, try this:
var exports = _container.GetExports<Action<object, EventArgs>>( "Test1" );
IMO IAAAMetaData and IBBBMetaData are not that different. They are actually identical.
The proof - GetExports retrieves two items of the same INNNMetaData type, depending on what the input was IAAAMetaData or IBBBMetaData. Because of the same base interface, you could actually write:
var exports = container.GetExports<Action<object, EventArgs>, IBase>();
and you will get two items of type IBase => Test1 and Test2.
So using your code, the only solution that i found was using the contract name:
var exports = container.GetExports<Action<object, EventArgs>, IAAAMetaData>().FirstOrDefault(iaaaa => iaaaa.Metadata.Name == "Test1");
In this case, the interface parameter for GetExports is irrelevant.

MEF, why are identical duplicates of one and the same exported plugin created?

(1) Using the code below I get exactly 2 items in my containers of one and the same exported plugin and I wonder why:
(2) Additional question which I really cannot implement: How can I extend the framework to handle different plugin types (such as having several imports of different types, or one import that stores all plugins in a dynamic IEnumerable or so). I want to provide in my static wrapper class one generic method that returns the discovered plugin as a function of type and matching meta data.
The exported plugin (which resides in a separate dll and whose location is pointed to when the DirectoryCatalog is built.
[Export(typeof(IPlugin))] //<---- If this line is commented out then only one item is imported (why?)
[PluginAttribute(typeof(StrategyPlugin_Test1), "StrategyPlugin", "Plugin1")]
public class StrategyPlugin_Test1 : IPlugin
{
public void DoSomething()
{
Console.WriteLine("I do something");
}
}
The following code defines the strongly typed meta data and imports as well as a static class that performs the MEF functions and is supposed to hold the discovered plugins:
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginAttribute : ExportAttribute
{
public Type PluginType { get; set; }
public string PluginGroupName { get; set; }
public string PluginName { get; set; }
public PluginAttribute(Type pluginType, string pluginGroupName, string pluginName) : base(typeof(IPlugin))
{
PluginType = pluginType;
PluginGroupName = pluginGroupName;
PluginName = pluginName;
}
}
public interface IPluginAttribute
{
Type PluginType { get; }
string PluginGroupName { get; }
string PluginName { get; }
}
public interface IPlugin
{
void DoSomething();
}
public class PluginDefinition
{
[ImportMany(typeof(IPlugin))]
public IEnumerable<Lazy<IPlugin, IPluginAttribute>> Plugins { get; set; }
public PluginDefinition()
{
}
}
Here the static class that wraps some of the core MEF stuff:
public static class PluginManager
{
private static PluginDefinition PluginDefinitions { get; set; }
static PluginManager()
{}
public static void Configure(PluginDefinition pluginDefinitions, IEnumerable<string> pluginDirectories)
{
AggregateCatalog aggregateCatalog = new AggregateCatalog(new DirectoryCatalog(pluginDirectories.FirstOrDefault()));
CompositionContainer container = new CompositionContainer(aggregateCatalog);
container.ComposeParts(pluginDefinitions);
//store plugin definition
PluginDefinitions = pluginDefinitions;
}
public static T GetPlugin<T>(string pluginName, string pluginGroupName) where T : class
{
//how to implement this given type of T could be any of the plugin types ...
//...provided for in an extended PluginDefinition class?
return null;
}
}
The reason behind the duplicate exports is the fact that you are deriving your custom export metadata attribute from ExportAttribute. This means that when you decorate a class member with PluginAttribute, you do not need to add the ExportAttribute. MEF will look for attributes assignable to ExportAttribute and it will find one in your PluginAttribute.
For the other question regarding plug-in types, MEF allows for multiple exports on the same type. You can have one export of type IPlugin and another more specialized like you were doing in your code:
[Export(typeof(IPlugin))] //<---- If this line is commented out then only one item is imported (why?)
[PluginAttribute(typeof(StrategyPlugin_Test1), "StrategyPlugin", "Plugin1")]
public class StrategyPlugin_Test1 : IPlugin
And then have different imports for each exported type. You can have an IEnumerable<IPlugin> import and then on another property a StrategyPlugin_Test1 import.

Protobuf-net not serializing base class members

We have the following classes and WCF service (using protobuf-net for serialization):
[DataContract]
[KnownType(typeof(NamedViewModel))]
public class NamedViewModel<TKey> : IViewModel
{
[DataMember]
public virtual TKey Id { get; set; }
[DataMember]
public virtual string Name { get; set; }
}
[DataContract]
[KnownType(typeof(ScheduleTemplateViewModel))]
public class NamedViewModel : NamedViewModel<int>
{
}
[DataContract]
public class ScheduleTemplateViewModel : NamedViewModel
{
[DataMember]
public string Comment { get; set; }
}
[DataContract]
public class Container
{
[DataMember]
public IEnumerable<ScheduleTemplateViewModel> Templates { get; set; }
}
[ServiceContract]
public interface IService
{
[OperationContract]
Container Get();
}
public class Service : IService
{
public IEnumerable<Container> Get()
{
return new Container { Templates = Enumerable.Range(1, 10)
.Select(i => CreateTemplate()).ToArray() };
}
private void ScheduleTemplateViewModel CreateTemplate()
{
var instance = WindsorContainer.Resolve<ScheduleTemplateViewModel>();
// populate instance
return instance;
}
}
We have two problems:
We get an exception during serialization that the Castle DynamicProxy type for ScheduleTemplateViewModel is unexpected. We noticed that there is custom code in protobuf-net to handle NHibernate and EntityFramework proxies...but not Castle DynamicProxies. We worked around this by adding an additional case statement in the protobuf-net source code to check for Castle's IProxyTargetAccessor type...but it would be nice if there were a way of handling this without modifying the protobuf-net source code...
Members on ScheduleTemplateViewModel (namely Comment) are serialized correctly...but base class Members are not. We already have the InferTagFromNameDefault set to true on RuntimeTypeModel.Default.
I can add that; can you tell me the full name (including namespace) of that interface?
From the example you give, none of those values should serialize, as none of them include the necessary numeric field-number information. Since you say some do serialize, I'm going to assume that this is an omission in the copy/paste. Protobuf-net will try to use the Order=n information from [DataMember(...)] if nothing better is available. However, if must be emphasized that protobuf-net cannot use [KnownType(...)], and inheritance again needs some explicit numeric field-number information. This is most easily added via [ProtoInclude(...)], but can also be provided at runtime

Can you get merged attributes for a class in C#?

Say I have the following set of classes, is it possible for the attributes of DerivedClass to be merged? Currently if I use the GetType().GetCustomAttributes()method passing in true for inheritance it takes the highest attributes in the inheritance structure.
i.e. [Another("Bob")] and [My(16)]
Is it possible for the attributes to be merged? So I would end up with two attributes of [My(16, "Male")] and [Another("Bob")]
I don't mean to say that I would specify an attribute of [My(16, "Male")] but rather I would be returned an attribute with an Age of 16 and a Gender of Male.
public class MyAttribute : Attribute
{
public MyAttribute(int age)
{
Age = age;
}
public MyAttribute(int age, string gender)
{
Age = age;
Gender = gender;
}
public int Age { get; set; }
public string Gender { get; set; }
}
public class AnotherAttribute : Attribute
{
public AnotherAttribute(string name)
{
Name = name;
}
public string Name { get; set; }
}
[My(12, "Male")]
[Another("Bob")]
public class BaseClass
{
}
[My(16)]
public class DerivedClass : BaseClass
{
}
You can have multiple instances of the same attribute on an entity (this is an option on the AttributeUsageAttribute applied to your attribute). When you get attributes on a type, you can get all the attributes applied to the inheritance chain.
However there is nothing in the standard tools that will take two attribute instances and make one.
(In theory a post processor that re-wrote the MSIL could do this.)
First, I don't believe any of the behavior you're inquiring about would depend on the AttributeUsage attribute which should be decorating your attribute classes (it looks like I could be wrong). Nor do I see where any attribute would be getting merged. Each attribute is a representation of a class in it's own right. Each attribute class should return it's own data. I would expect DerivedClass.GetCustomAttributes() to return three separate references for each attribute - 2 from the base class, and 1 for itself.
Try setting the AttributeUsage on the 'MyAttribute' to allow multiple. It may not be merging them, but taking the 'latest' version of the attribute.
I just thought of something else that could be causing the mysterious disappearing act. You may have to go after the base class to get the original attribute? You could try that:
base.GetType().GetCustomAttributes();
I did some digging and here is what the AttibuteUsage will do and could be causing you to only see one of the MyAttribute classes:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
I was close in my statement earlier about using AttributeUsage; however, it is the Inherited named parameter that will limit visibility to the base class' MyAttribute decoration. Try specifying Inherited = true and see what you get.

Honouring of AttributeUsage on derived attribute types

Given the following, I would not expect the compiler to allow multiple attributes that are derived from the base attribute, given that is set to AllowMultiple=false. In fact it compiles without a problem - what am I missing here?
using System;
[AttributeUsage(AttributeTargets.Property,AllowMultiple=false,Inherited=true)]
abstract class BaseAttribute : Attribute { }
sealed class DerivedAttributeA : BaseAttribute { }
sealed class DerivedAttributeB : BaseAttribute { }
class Sample1
{
[DerivedAttributeA()]
[DerivedAttributeB()]
public string PropertyA{ get; set; } // allowed, concrete classes differ
[DerivedAttributeA()]
[DerivedAttributeA()]
public string PropertyB { get; set; } // not allowed, concrete classes the same, honours AllowMultiple=false on BaseAttribute
}
The problem is simply that the AllowMultiple check only compares attributes of the same actual type (i.e. the concrete type instantiated) - and is perhaps best used with sealed attributes for this reason.
It will, for example, enforce the following (as an illegal duplicate), inheriting this from BaseAttribute:
[DerivedAttributeB()]
[DerivedAttributeB()]
public string Name { get; set; }
In short, I don't think you can do what you want here... (enforce no more than one instance including subclasses of BaseAttribute per property).
A similar example of this problem would be:
[Description("abc")]
[I18NDescriptionAttribute("abc")]
public string Name { get; set; }
class I18NDescriptionAttribute : DescriptionAttribute {
public I18NDescriptionAttribute(string resxKey) : base(resxKey) { }
}
The intent above is to provide a [Description] from resx at runtime (fully supported by ComponentModel etc) - but it can't stop you also adding a [Description].

Categories

Resources