Unable to Import parts with Metadata - c#

I'm trying to import parts and include a custom MetadataAttribute, following the imperative model, using .NET 4.5
Below, I've included the simplest of example I can, which illustrates the problem.
When this code is executed, the Engine class constructor is called, and passed an empty Enumerator, rather than the two plugins which are clearly part of the project.
At the moment I'm suspecting the PluginMetadata attribute, but I don't see how to get Metadata into the catalog without it.
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Registration;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var builder = new RegistrationBuilder();
builder.ForTypesDerivedFrom<IPlugIn>().Export<Lazy<IPlugIn, IPlugInMetadata>>();
builder.ForType<Engine>().Export();
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly(), builder);
var container = new CompositionContainer(catalog);
var engine = container.GetExport<Engine>();
engine.Value.Run();
}
}
internal class Engine
{
private IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> PlugIns { get; set; }
public Engine(IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> plugins)
{
PlugIns = plugins;
}
public void Run()
{
foreach (var plugIn in PlugIns)
{
Console.WriteLine("Starting {0}", plugIn.Metadata.Name);
plugIn.Value.Work();
}
}
}
interface IPlugIn
{
void Work();
}
interface IPlugInMetadata
{
string Name { get; }
}
[MetadataAttribute]
class PlugInMetadataAttribute : ExportAttribute, IPlugInMetadata
{
public PlugInMetadataAttribute(string name)
{
this.name = name;
}
private readonly string name;
public string Name { get { return name; } }
}
[PlugInMetadata("PlugIn1")]
class PlugIn1 : IPlugIn
{
public void Work()
{
Console.WriteLine("PlugIn 1 working");
}
}
[PlugInMetadata("PlugIn2")]
class PlugIn2 : IPlugIn
{
public void Work()
{
Console.WriteLine("PlugIn 2 working");
}
}
}

Metadata interfaces must not have any properties with setters. You should modify the IPlugInMetadata interface so its properties won't have any setters, otherwise the composition will fail:
interface IPlugInMetadata
{
string Name { get; }
}
Also, you should consider making your PlugInMetadataAttribute class inherit from ExportAttribute rather than Attribute. That will allow using this attribute as an export attribute and you won't have to use a RegistrationBuilder.
EDIT: I think I found your problem
When trying to use ImportMany in the constructor, you must specify so explicitly, so your constructor should look like this:
[ImportingConstructor]
public Engine([ImportMany] IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> plugins)
{
PlugIns = plugins;
}
Alternatively, you can choose to import it as a property:
[ImportMany]
private IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> PlugIns { get; set; }
As a side note, when deriving from ExportAttribute, you'd like to include constructors that automatically export your part as IPlugIn:
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
class PlugInMetadataAttribute : ExportAttribute, IPlugInMetadata
{
public PlugInMetadataAttribute()
: base(typeof(IPlugIn))
{
}
public PlugInMetadataAttribute(string contractName)
: base(contractName, typeof(IPlugIn))
{
}
public string Name { get; set; }
}

Related

Inject a string property in a class using Ninject

One of my interfaces has a string property that will depend on where the interface is being used. I want to avoid hardcoding the property every time the object is created. I can set the property in constructor, but the object is injected using a factory.
The interface as follows:
public interface IObjectStore
{
string StorageTableName { get; set;}
void UpdateObjectStore(string key, string value);
string ReadObjectStore(string key);
}
Which is used in a service
public class CategoryService<T> : ICategoryService<T> where T : Company
{
private readonly IObjectStore objectStore;
public CategoryService(IObjectStore objStore)
{
this.objectStore = objStore;
objectStore.StorageTableName = "CategoryTable"; // I want to avoid this hard coding
}
...
}
The service is created using service factory (Ninject.Extensions.Factory)
public interface IServiceFactory
{
ICategoryService<T> CreateCategoryService<T>() where T : class;
}
Which is then injected using Ninject at the controller level. Here are my bindings
bool storeInNoSql = true;
kernel.Bind<IServiceFactory>().ToFactory().InSingletonScope();
kernel.Bind<ICategoryService<Article>>().To<CategoryService<Article>>();
kernel.Bind<IObjectStore>().ToMethod(ctx => storeInNoSql ? ctx.Kernel.Get<ObjectStore>() : null);
So the question is: how do i tell Ninject to set the property StorageTableName to "CategoryTable" everytime the object is injected into CategoryService and to "ArticleTable" everytime it is inserted into ArticleService?
I think this is what you are looking for.
It's just a very small sample project I just did, but this should solve your problem.
public class Ninject_34091099
{
public static void Run()
{
using (IKernel kernel = new StandardKernel())
{
kernel.Bind<IInterface<Generic1>>()
.To<Class<Generic1>>()
.WithConstructorArgument("name", "STRING ONE");
kernel.Bind<IInterface<Generic2>>()
.To<Class<Generic2>>()
.WithConstructorArgument("name", "The other string");
kernel.Bind<IServiceFactory>().ToFactory().InSingletonScope();
var factory = kernel.Get<IServiceFactory>();
var c1 = factory.CreateInterface<Generic1>();
var c2 = factory.CreateInterface<Generic2>();
Console.WriteLine(c1.Name);
Console.WriteLine(c2.Name);
}
Console.WriteLine("Done");
Console.ReadLine();
}
}
public interface IInterface<T> where T : class
{
string Name { get; set; }
}
public class Generic1
{
}
public class Generic2
{
}
public class Class<T> : IInterface<T> where T : class
{
public string Name { get; set; }
public Class(string name)
{
Name = name;
}
}
public interface IServiceFactory
{
IInterface<T> CreateInterface<T>() where T : class;
}
Sorry that the names mean nothing :D
Hope it helps

PostSharp LocationInterceptionAspect not being applied to inherited properties

I have created an attribute that inherits LocationInterceptionAspect.
For demonstration purposes the code is as follows:
[Serializable]
public class RepeaterAttribute : LocationInterceptionAspect
{
public override bool CompileTimeValidate(PostSharp.Reflection.LocationInfo locationInfo)
{
var propertyInfo = locationInfo.PropertyInfo;
if (propertyInfo == null) return false;
if (propertyInfo.PropertyType != typeof(String))
return false;
return base.CompileTimeValidate(locationInfo);
}
public override void OnSetValue(LocationInterceptionArgs args)
{
args.Value = ((String)args.Value) + ((String)args.Value);
args.ProceedSetValue();
}
}
I have a library called External and in it is a class called Parent.
namespace External
{
public class Parent
{
public String ParentProperty { get; set; }
}
}
In a console application I have a class called Child that inherits from Parent.
The console application references the External library.
public class Child : External.Parent
{
public String ChildProperty { get; set; }
}
In my console application my code is.
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var child = new Child();
child.ParentProperty = "A";
Console.WriteLine("This should be 'AA' : '{0}'", child.ParentProperty);
child.ChildProperty = "B";
Console.WriteLine("This should be 'BB' : '{0}'", child.ChildProperty);
Console.ReadKey();
}
}
}
and in the console application's AssemblyInfo.cs I have:
[assembly: ConsoleApplication.Repeater(AttributeTargetTypes = "ConsoleApplication.Child")]
But when I run the Repeater attribute is not being applied to the inherited "ParentProperty" from the Parent class.
PostSharp is not able to change classes in different assemblies that in the one it is transforming. The base property is declared in a different assembly. This is a limitation of LocationInterceptionAspect.
You can use MethodInterception, which supports intercepting methods in different assemblies:
[Serializable]
public class SetterRepeaterAttribute : MethodInterceptionAspect
{
public override void OnInvoke( MethodInterceptionArgs args )
{
args.Arguments[0] = ((String)args.Arguments[0]) + ((String)args.Arguments[0]);
args.Proceed();
}
}
And multicast it on assembly level to the setter of the base class:
[assembly: ConsoleApplication2.SetterRepeater(
AttributeTargetMembers = "set_ParentProperty",
AttributeTargetTypes = "External.Parent",
AttributeTargetAssemblies = "regex:.*")]
Note that in this case the interception is done at the call site level, the ParentProperty setter is not changed itself. Calls from the original assembly won't be intercepted.

Reference an interface name without the class name

This is probalby really simple and I have just looked at it too long. In my project I have a Contracts.cs with the following code:
namespace RC.Common.Core.ProcessPlugin
{
public class Contracts
{
public interface IProcessPlugin
{
void RunProcess(int jobID);
}
public interface IProcessMetaData
{
string Process { get; }
}
}
}
And then I have PluginProcessFactory.cs with this as some of its code:
namespace RC.Common.Core.ProcessPlugin
{
public class PluginProcessFactory
{
[ImportMany]
IEnumerable<Lazy<Contracts.IProcessPlugin, Contracts.IProcessMetaData>> processes;
// more code
}
}
How can I get it so that the references to the interfaces in the IEnumerable don't contain the class reference in the name? So it looks like this:
IEnumerable<Lazy<IProcessPlugin, IProcessMetaData>> processes;
Don't put the interfaces inside of a class:
namespace RC.Common.Core.ProcessPlugin
{
// Place directly in namespace
// public class Contracts
// {
public interface IProcessPlugin
{
void RunProcess(int jobID);
}
public interface IProcessMetaData
{
string Process { get; }
}
// }
}

Using MEF with C#, how do I call methods on the host, from the plugin?

I am trying to add plugin extensibility to my C# application using the Managed Extensibility Framework (MEF) framework, and so far it is going ok; I have my main/host application loading plugins from a defined folder, and can call their methods etc. from the main application. Both the host application and the plugins reference a seperate dll assembly which contains the interfaces common to all projects.
This is working fine and I can call/interact with the plugins from the main application. However, I also would like to be able to interact with the host application from the plugins, but can't seem to find out how this is done.
I would like to be able to get/set/execute exported properties and methods in the main app from my plugins. Currently I am only able to 'speak' to the plugins from the main app, not the other way around as well.
My code thus far:
Interface DLL
namespace MefContracts
{
[InheritedExport]
public interface IPlugin
{
String DoWork();
}
public class Class1
{
public IPlugin plugin { get; set; }
}
}
Main/Host Application
namespace MyMEF
{
class clsMEF
{
private CompositionContainer _container;
[Import(typeof(MefContracts.IPlugin))]
public MefContracts.IPlugin plugin;
public clsMEF()
{
Compose();
}
void Compose()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog("..\\..\\Extensions"));
_container = new CompositionContainer(catalog);
try
{
this._container.ComposeParts(this);
}
catch (CompositionException compositionException)
{
Console.WriteLine(compositionException.ToString());
}
}
}
void Main()
{
clsMEF myMef = new clsMEF();
MessageBox.Show(myMef.plugin.DoWork());
}
}
Plugin
namespace MefPlugin
{
[Export]
public class Class1 : MefContracts.IPlugin
{
public String DoWork()
{
return "Plugin called";
}
}
}
You could add a host interface in the contracts assembly. For example:
[InheritedExport]
public interface IHost
{
string Version { get; }
}
Then add a property of type IHost to the IPlugin interface:
[InheritedExport]
public interface IPlugin
{
IHost Host { get; }
String DoWork();
}
Finally each plug-in will need to decorate the Host property with MEF's ImportAttribute:
[Import(typeof(IHost))]
public IHost Host { get; }
After much playing and trial and error, I found the issue I was having was I had not added the current executing assembly (System.Reflection.Assembly.GetExecutingAssembly()) to the host's assembly catalogue along with the plugin's assemblies.
Many thanks to #PanosRontogiannis who got me on the right lines - that answer worked brilliantly once the assembly was properly added.
Here is the working code for others in need:
Interface DLL
using System.ComponentModel.Composition;
namespace MefContracts
{
[InheritedExport]
public interface IPlugin
{
String Work(String input);
}
[InheritedExport]
public interface IHost
{
string Version { get; }
}
}
Host Application
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace MyMEF
{
[Export]
public class Class1 : MefContracts.IHost
{
public String Version
{
get { return "v1.00"; }
}
}
class clsMEF
{
private CompositionContainer _container;
[Import(typeof(MefContracts.IPlugin))]
public MefContracts.IPlugin plugin;
public clsMEF()
{
Compose();
}
void Compose()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog("..\\..\\Extensions"));
catalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly())); // <-- THIS WAS THE MISSING PIECE
_container = new CompositionContainer(catalog);
try
{
this._container.ComposeParts(this);
}
catch (CompositionException compositionException)
{
Console.WriteLine(compositionException.ToString());
}
}
}
static class Program
{
static void Main()
{
clsMEF myMef = new clsMEF();
MessageBox.Show(myMef.plugin.Work("My Input"));
}
}
}
Plugin
using System.ComponentModel.Composition;
namespace MefPlugin
{
[Export]
public class Class2 : MefContracts.IPlugin
{
[Import(typeof(MefContracts.IHost))]
public MefContracts.IHost Host;
public String Work(String input)
{
return "Plugin Called (Input: " + input + "). Host Application Version: " + input + Host.Version;
}
}
}

How to export parts from an object not instantiated by the MEF container

Introduction
Class SessionModel is a service locator providing several services (I am going to elaborate my system architecture in the future, but for now I need to do it that way).
Code
I edited the following code part to be a Short, Self Contained, Correct (Compilable), Example (SSCCE):
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var sessionModel = new SessionModel(3);
// first case (see text down below):
var compositionContainer = new CompositionContainer();
// second case (see text down below):
//var typeCatalog = new TypeCatalog(typeof (SessionModel));
//var compositionContainer = new CompositionContainer(typeCatalog);
compositionContainer.ComposeExportedValue(sessionModel);
var someService = compositionContainer.GetExportedValue<ISomeService>();
someService.DoSomething();
}
}
public class SessionModel
{
private int AValue { get; set; }
[Export]
public ISomeService SomeService { get; private set; }
public SessionModel(int aValue)
{
AValue = aValue;
// of course, there is much more to do here in reality:
SomeService = new SomeService();
}
}
public interface ISomeService
{
void DoSomething();
}
public class SomeService : ISomeService
{
public void DoSomething()
{
Console.WriteLine("DoSomething called");
}
}
}
Problem
I would like MEF to consider the parts (i.e. SomeService) exported by the service locator when composing other parts, but unfortunately this does not work.
First Case
When I try to get the exported value for ISomeService there is a System.ComponentModel.Composition.ImportCardinalityMismatchException telling me there are no exports with this contract name and required type identity (ConsoleApplication1.ISomeService).
Second Case
If I create the CompositionContainer using the TypeCatalog the exception is slightly different. It is a System.ComponentModel.Composition.CompositionException telling me MEF doesn't find a way to create a ConsoleApplication1.SessionModel (which is right and the reason why I am doing it myself).
Additional Information
mefx says for both cases:
[Part] ConsoleApplication1.SessionModel from: DirectoryCatalog (Path=".")
[Export] ConsoleApplication1.SessionModel.SomeService (ContractName="ConsoleApplication1.ISomeService")
[Part] ConsoleApplication1.SessionModel from: AssemblyCatalog (Assembly="ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
[Export] ConsoleApplication1.SessionModel.SomeService (ContractName="ConsoleApplication1.ISomeService")
What do I have to do? Is this possible with MEF or do I have to use Unity or StructureMap, or something else? Can this be done implementing an ExportProvider?
OK, that's how I did it:
I implemented my own SessionModelExportProvider finding exports in my SessionModel (see code below). Class SessionModelExport is just for holding the export data and – instead of creating an instance of a service – returning the value of the property of the SessionModel.
public class SessionModelExportProvider : ExportProvider
{
private List<Export> Exports { get; set; }
public SessionModelExportProvider(SessionModel sessionModel)
{
// get all the properties of the session model having an Export attribute
var typeOfSessionModel = typeof (SessionModel);
PropertyInfo[] properties = typeOfSessionModel.GetProperties();
var propertiesHavingAnExportAttribute =
from p in properties
let exportAttributes = p.GetCustomAttributes(typeof (ExportAttribute), false)
where exportAttributes.Length > 0
select new
{
PropertyInfo = p,
ExportAttributes = exportAttributes
};
// creating Export objects for each export
var exports = new List<Export>();
foreach (var propertyHavingAnExportAttribute in propertiesHavingAnExportAttribute)
{
var propertyInfo = propertyHavingAnExportAttribute.PropertyInfo;
foreach (ExportAttribute exportAttribute in propertyHavingAnExportAttribute.ExportAttributes)
{
string contractName = exportAttribute.ContractName;
if (string.IsNullOrEmpty(contractName))
{
Type contractType = exportAttribute.ContractType ?? propertyInfo.PropertyType;
contractName = contractType.FullName;
}
var metadata = new Dictionary<string, object>
{
{CompositionConstants.ExportTypeIdentityMetadataName, contractName},
{CompositionConstants.PartCreationPolicyMetadataName, CreationPolicy.Shared}
};
var exportDefinition = new ExportDefinition(contractName, metadata);
var export = new SessionModelExport(sessionModel, propertyInfo, exportDefinition);
exports.Add(export);
}
}
Exports = exports;
}
protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition,
AtomicComposition atomicComposition)
{
return Exports.Where(e => definition.IsConstraintSatisfiedBy(e.Definition));
}
}
public class SessionModelExport : Export
{
private readonly SessionModel sessionModel;
private readonly PropertyInfo propertyInfo;
private readonly ExportDefinition definition;
public SessionModelExport(SessionModel sessionModel, PropertyInfo propertyInfo, ExportDefinition definition)
{
this.sessionModel = sessionModel;
this.propertyInfo = propertyInfo;
this.definition = definition;
}
public override ExportDefinition Definition
{
get { return definition; }
}
protected override object GetExportedValueCore()
{
var value = propertyInfo.GetValue(sessionModel, null);
return value;
}
}
The problem is that the SomeService is an instance property. You could have several SessionModel objects in your system, and MEF would have no way of knowing which SessionModel is returning the ISomeService instance that is supposed to be matched to an import.
Instead, just make SessionModel a static class and SomeService a static property. Alternatively, make SessionModel a singleton. The SomeService property would still be static, but would export the service from the one-and-only instance of SessionModel.
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.ReflectionModel;
using System.Reflection;
using System.Linq;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var catalogs = new AggregateCatalog();
var catalog = new System.ComponentModel.Composition.Hosting.AssemblyCatalog(Assembly.GetExecutingAssembly());
catalogs.Catalogs.Add(catalog);
var sessionModel = new SessionModel(3);
var container = new CompositionContainer(catalog);
ISomeService someService = container.GetExportedValueOrDefault<ISomeService>(sessionModel.cname);
if (someService != null)
{
someService.DoSomething();
}
}
}
public class SessionModel
{
private int AValue { get; set; }
//[Import("One",typeof(ISomeService))]
//public ISomeService SomeService { get; private set; }
public SessionModel(int aValue)
{
AValue = aValue;
// of course, there is much more to do here in reality:
}
public string cname { get { return "One"; } }
}
public class SessionModel1
{
private int AValue { get; set; }
//[Import("Two",typeof(ISomeService))]
//public ISomeService SomeService { get; private set; }
public SessionModel1(int aValue)
{
AValue = aValue;
}
public string cname { get { return "Two"; } }
}
public interface ISomeService
{
void DoSomething();
}
[Export("One",typeof(ISomeService))]
public class SomeService : ISomeService
{
public SomeService()
{
Console.WriteLine("Some Service Called");
}
public void DoSomething()
{
Console.WriteLine("DoSomething called");
Console.ReadKey();
}
}
[Export("Two",typeof(ISomeService))]
public class SomeService1 : ISomeService
{
public SomeService1()
{
Console.WriteLine("Some Service1 Called");
}
public void DoSomething()
{
Console.WriteLine("DoSomething called 1");
Console.ReadKey();
}
}
}
First case: By passing sessionModel to ComposeExportedValue you add a part of type SessionModel and not of ISomeService. To make this case work you nee to pass the service to ComposeExportedValue.
compositionContainer.ComposeExportedValue(sessionModel.SomeService);
Second case: In this case you leave the creation of parts to the container. The container can create new parts if there is either a parameter-less constructor or a constructor with parameters decorated with the ImportingConstructorAttribute. This most probably means that you will need to change your design a bit.
Personally I would go with the first case, but try to keep this to a minimum. After all the normal (and suggested) usage of MEF is letting the container create and handle parts.

Categories

Resources