I am trying to understand MEF and did a few samples on that. But, when I try to execute this particular code below, I am with an exception.
ConsoleApplication2.program
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
namespace ConsoleApplication2
{
class Program
{
[Import(typeof(Contracts.IInput))]
public Contracts.IInput myinterface { get; set; }
public void Method()
{
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
container.ComposeParts(container );
Console.WriteLine(myinterface.IsValid());
}
public static void Main(string[] args)
{
var obj = new Program();
obj.Method();
Console.ReadLine();
}
}
}
I have a separate project for defining an interface.
namespace Contracts
{
public interface IInput
{
//All classes that inherit IInput must implement the IsValid Property.
string IsValid();
}
}
And in another separate project, I use a export class.
using System.ComponentModel.Composition;
using Contracts;
namespace Plugin
{
[Export(typeof(Contracts.IInput))]
public class Plugin : IInput
{
public string IsValid()
{
return "1";
}
}
}
Now, all these produce (two library files and an executable) at the same "bin\Debug" folder.But on executing when reaching the code, "composeParts(this)" an exception occurs like below,
Object reference not set to an instance of an object.
When I try to change "Composeparts(container)" there is another exception like below,"
**> The composition produced a single composition error. The root cause is
provided below. Review the CompositionException.Errors property for more detailed information.
1) Cannot populate the collection 'ConsoleApplication2.Program.myinterface' because it does not implement ICollection or is read-only. If the collection is not IEnumerable or T[] it must implement ICollection and be either pre-initialized or be writable with a default constructor.
Resulting in: Cannot activate part 'ConsoleApplication2.Program' Element: ConsoleApplication2.Program --> ConsoleApplication2.Program **
I used importmany like
class Program
{
[ImportMany(typeof(Contracts.IInput))]
public IEnumerable<Contracts.IInput> myinterface { get; set; }
public void Method()
{
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
try
{
container.ComposeParts(container);
foreach (var i in myinterface)
Console.WriteLine(i.IsValid());
}
catch (CompositionException compositionException)
{
Console.WriteLine(compositionException.ToString());
}
}
But in this case, still myinterface is null and this throws a null reference exception.
There are 2 problems:
First:
Use Import
not ImportMany
.
OR
Since ImportMany
expects a collection so you should use something like this:
public IEnumerable<Contracts.IInput> myinterface { get; set; }
Second:
Just as you cannot use properties that have the Import
attribute on them in the Constructor (Thats what an importing constructor is for), You can't access them in the program's Main function.
This, for instance, will work:
var catalog = new AggregateCatalog();
//Add all the parts found in all assemblies in
//the same directory as the executing program
catalog.Catalogs.Add(
new DirectoryCatalog(
Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location
)
)
);
var container = new CompositionContainer(catalog);
container.ComposeParts(container);
// Use the container to get a value for myinterface
myinterface = container.GetExportedValue<Contracts.IInput>();
Console.WriteLine(myinterface.IsValid());