Original Source Code
I've got a simple business object in my BusinessObjects.dll file:
namespace BusinessObjects
{
public class MyClass
{
public MyClass()
{
DateTime = DateTime.Now;
}
public DateTime DateTime { get; set; }
}
}
In my SharedUI.dll I've got this "Context-provider" class, that I use to hold a referece to the currently selected MyClass - remember this is a simplyfied example :)...
namespace SharedUI
{
public class AppContext
{
[Export]
public MyClass SelectedMyClass { get; private set; }
public void SetupContext(MyClass myClass)
{
SelectedMyClass = myClass;
}
public static AppContext Context
{
get
{
if (context == null)
{
context = new AppContext();
}
return context;
}
}
private static AppContext context;
}
}
My MefTest.exe has this class:
namespace MefTest
{
public class Program
{
[Import]
public MyClass MyClass { get; set; }
private void Compose()
{
var ventSystem = new MyClass();
AppContext.Context.SetupContext(ventSystem);
var executingAssembly = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var contextAssembly = new AssemblyCatalog(Assembly.LoadFile(string.Format(#"{0}\SharedUI.dll", Environment.CurrentDirectory)));
var catalog = new AggregateCatalog(executingAssembly, contextAssembly);
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
private void Run()
{
Compose();
// MyClass is always null in the next line?
Console.WriteLine(MyClass.DateTime.ToString());
Console.ReadKey();
}
private static void Main(string[] args)
{
var p = new Program();
p.Run();
}
}
}
I'm a MEF rookie so please bear with me :)
UPDATED Source Code with suggestions from Daniel Plaisted
MyClass source is the same...
SharedUI.dll now looks like this:
namespace SharedUI
{
[Export]
public class AppContext
{
[Export(typeof(MyClass))]
public MyClass SelectedMyClass { get; private set; }
public void SetupContext(MyClass myClass)
{
SelectedMyClass = myClass;
}
}
}
MefTest.exe now looks like this:
namespace MefTest
{
public class Program
{
[Import]
public MyClass MyClass { get; set; }
[Import]
public AppContext AppContext { get; set; }
private void Compose()
{
var executingAssembly = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var contextAssembly = new AssemblyCatalog(Assembly.LoadFile(string.Format(#"{0}\SharedUI.dll", Environment.CurrentDirectory)));
var catalog = new AggregateCatalog(executingAssembly, contextAssembly);
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
var myClass = new MyClass();
AppContext.SetupContext(myClass);
}
private void Run()
{
Compose();
// AppContext.SelectedMyClass is NOT null in the next line... which is good I guess :)
Console.WriteLine(AppContext.SelectedMyClass.DateTime.ToString());
// MyClass is always null in the next line?
Console.WriteLine(MyClass.DateTime.ToString());
Console.ReadKey();
}
private static void Main(string[] args)
{
var p = new Program();
p.Run();
}
}
}
What am I doing wrong since I can't get it working?
When MEF needs to get an Export which is on a property of a class, it will create an instance of the class and call the property getter. So MEF is creating a new instance of your AppContext, different than the static AppContext.Context instance. The instance MEF creates doesn't have the SelectedMyClass property set on it, which is why your import ends up being null.
The problem is:
[Import] public MyClass MyClass { get; set; }
There are no [Export]s defined for MyClass. MEF will compose this appplication based on stuff it "knows", and since it does not know "MyClass"...
I noticed this one:
[Export] public MyClass SelectedMyClass { get; private set; }
This means you are trying to trick MEF into updating one of its parts from time to time? The solution to this would be to create a custom Catalog which contains "runtime" objects, in which you can updated the exported value for MyClass whenever you want. The current implementation will never resolve MyClass...
[edited:]
You can decorate a member as well, but you'll have to add the class type there. So this will work:
[Export(typeof(MyClass)] public MyClass SelectedMyClass { get; private set; }
You put your Export attribute in the wrong place.
You should put it on the definition of MyClass like so:
namespace BusinessObjects
{
[Export]
public class MyClass
{
public MyClass()
{
DateTime = DateTime.Now;
}
public DateTime DateTime { get; set; }
}
}
And then use the [Import] attribute wherever you want an instance of this class.
Remark: You cannot use MEF to move a specific instance of a class (not like this).
MEF is used to create instances of a requested type and inject them at indicated places.
To learn more about MEF check out the project's page at CodePlex.
Related
I cant make working the code below.. Do I need other class that impolement my IComponent with paratmeterless consturctor?
public class Program
{
public static void Main()
{
var lazy = new Lazy<IComponent>();
IComponent comp = lazy.Value;
var client = new ComponentClient(comp);
client.Run();
}
}
public interface IComponent
{
void Something();
}
public class LazyComponent : IComponent
{
public Lazy<IComponent> _LazyComponent { get; set ;}
public LazyComponent(Lazy<IComponent> lazyComponent)
{
_LazyComponent = lazyComponent;
}
public void Something()
{
_LazyComponent.Value.Something();
}
}
public class ComponentClient
{
public IComponent _Component { get; set; }
public ComponentClient(IComponent component)
{
_Component = component;
}
public void Run()
{
_Component.Something();
}
}
You need to tell the Lazy how to construct the component, by giving it a factory method.
https://learn.microsoft.com/en-us/dotnet/api/system.lazy-1?view=netframework-4.8
public class Program
{
public static void Main()
{
var lazy = new Lazy<IComponent>(() => new RealComponent());
var lazyComponent = new LazyComponent(lazy);
var client = new ComponentClient(lazyComponent);
client.Run();
}
}
I have class Important and some objects of this class created. I want allow user to choose main object of this class. Have a look at code below:
public class Program
{
public static void Main(string[] args)
{
Important imp1 = new Important("Important 1");
Important imp2 = new Important("Important 2");
Important imp3 = new Important("Important 3");
imp2.SetMostImportant();
Console.Write(Important.MostImportant.Name);
}
public class Important
{
public Important(string name)
{
Name = name;
if(MostImportant == null)
SetMostImportant();
}
public string Name { get; private set; }
public static Important MostImportant { get; private set; }
public void SetMostImportant()
{
MostImportant = this;
}
}
}
Is it good solution? If not, please tell me why not.
Before, to achieve this kind of things I just created boolean field named e.g. IsMainObject and, when I wanted to change main object, I iterated through all objects (or group of object) of specific class except element that I want to be main, and changed boolean to false, in my new candidate I simply set flag to true. Example below:
public class Program
{
public static void Main(string[] args)
{
Important imp1 = new Important("Important 1");
Important imp2 = new Important("Important 2");
Important imp3 = new Important("Important 3");
List<Important> list = new List<Important> { imp1, imp2, imp3 };
foreach(var item in list.Where(x => x.Name != "Important 2"))
{
item.SetMostImportant(false);
}
imp2.SetMostImportant(true);
Console.Write(list.FirstOrDefault(x => x.MostImportant == true).Name);
}
public class Important
{
public Important(string name)
{
Name = name;
}
public string Name { get; private set; }
public bool MostImportant { get; private set; }
public void SetMostImportant(bool val)
{
MostImportant = val;
}
}
}
I don't like this solution because:
I don't know if MostImportant is true for more than one objects without iterating.
I need to write extra-code to handle much more cases.
I don't have possibility to always iterate through all instances of specific class (groups not always are enough).
... and much more, but you got the idea.
public static Important MostImportant { get; private set; }
is a fine solution, and much better than
public bool MostImportant { get; private set; }
It's not uncommon to have a static property of the type that it's inside of when implementing "singleton" classes. I've written code that resembles this:
class MyClass
{
public static MyClass Instance { get; private set; }
public MyClass()
{
if (Instance == null)
{
Instance = this;
}
else
{
throw new Exception("MyClass already instantiated.");
}
}
}
I have two classes and I've been able to instantiate one class in the other, but when ever I try to use the instantiated class, I get an error. I can't use the instantiated class and VS even reports that instantiated class doesn't exist. Please why is that?
public class GradeBook
{
public GradeBook()
{
gradesList = new List<float>();
}
public void AddGrades(float grades)
{
gradesList.Add(grades);
}
List<float> gradesList;
public List<float> GradesList
{
get
{
return gradesList;
}
set
{
gradesList = value;
}
}
}
public class GradeStatistics
{
GradeBook aveg = new GradeBook();
//This is where the error occurs
aveg.GradesList;
}
A class cannot contain code, unless it is in a method, property, etc. Your code which creates the instance and accesses its property, is not. Create a new method in your class which contains your code. Furthermore: you should do something with the value from the property:
public class GradeStatistics
{
public void MyMethod()
{
GradeBook aveg = new GradeBook();
var result = aveg.GradesList;
}
}
First
You could use just the property for the List object and your method to
add items will be obsolete since List has an own method to add items.
public class GradeBook
{
// Property of type List (short version)
public List<float> GradesList {get; set;}
public GradeBook()
{
GradesList = new List<float>();
}
}
as already mentioned in the comments you need to use the GradeBook object inside the constructor or a method like
public class GradeStatistics
{
// constructor
public GradeStatistics()
{
GradeBook aveg = new GradeBook();
//This will stop the error to occur
//
aveg.GradesList.Add(0.65f);
}
}
or you create also a property in the new class
public class GradeStatistics
{
public GradeBook aveg {get; set;}
// constructor
public GradeStatistics()
{
// from now on you can use it also in methods
aveg = new GradeBook();
}
public void clearForeignList()
{
this.aveg.GradesList.Clear();
}
}
Check you code once again and look #martin-mulder answer. Here's a perfectly wokring code:
public class GradeBook
{
public GradeBook()
{
gradesList = new List<float>();
}
public void AddGrades(float grades)
{
gradesList.Add(grades);
}
List<float> gradesList;
public List<float> GradesList
{
get
{
return gradesList;
}
set
{
gradesList = value;
}
}
}
public class GradeStatistics
{
public void MyMethod()
{
GradeBook aveg = new GradeBook();
var x = aveg.GradesList;
}
}
First of all: You all are great, I very often search this site for MEF answers.
My problem is the following:
I have several assemblies with many [Imports] in them and one main application where the assembling takes place. Now the problem is, that those Imports do not get "filled" they are always staying null.
I've tried to reproduce this behaviour in a simple small project and came up with the following source code.
Am I missunderstanding some things about MEF?
Please help! Thank you all!
Assembly Interfaces:
namespace Interfaces
{
public interface IClass1
{
void Trigger();
}
public interface IClass2
{
void Trigger();
}
public interface IClass3
{
void Trigger();
}
}
Assembly Library1:
namespace Library1
{
[Export(typeof(IClass1))]
public class Class1:IClass1
{
#region IClass1 Members
public void Trigger()
{
}
#endregion
}
}
Assembly Library2:
namespace Library2
{
[Export(typeof(IClass2))]
public class Class2:IClass2
{
[Import]
public IClass1 Class1 { get; set; }
public void Trigger()
{
}
}
}
In the main programm I assemble the whole Mef stuff doing the following:
namespace MEFTest
{
public class mefStart
{
public CompositionContainer Container { get; private set; }
public void Start()
{
AggregateCatalog catalog = new AggregateCatalog();
AssemblyCatalog assemblyCatalog = new AssemblyCatalog(typeof(Program).Assembly);
DirectoryCatalog directoryCatalog = new DirectoryCatalog(".", "Library*.dll");
catalog.Catalogs.Add(directoryCatalog);
catalog.Catalogs.Add(assemblyCatalog);
this.Container = new CompositionContainer(catalog);
CompositionBatch batch = new CompositionBatch();
batch.AddExportedValue(this.Container);
this.Container.Compose(batch);
this.Container.ComposeParts(this);
}
}
}
But after that the following class does not have any of the imports filled:
namespace MEFTest
{
public class Class3:IClass3
{
[Import]
public IClass1 Class1 { get; set; }
[Import]
public IClass2 Class2 { get; set; }
public void Trigger()
{
Class1.Trigger();
Class2.Trigger();
}
}
}
When I am looking into the container, I see that the IClass1 and the ICLass2 were composed.
Why are the [Import]'s in Class3 not being satisfied? I guess I am missing something completely...
Thank you all in advance for your help!
Michael
as long as class3 is NOT instantiated by MEF you will not see any import.
btw if you do imports not via [ImportingConstructor] - be sure that the imports a satisfied (IPartImportsSatisfiedNotification) before you use them.
this would work, but i dont know where you need your class3
public class mefStart
{
[Import]
private IClass3 my3;
public CompositionContainer Container { get; private set; }
public void Start()
{
AggregateCatalog catalog = new AggregateCatalog();
AssemblyCatalog assemblyCatalog = new AssemblyCatalog(typeof(Program).Assembly);
DirectoryCatalog directoryCatalog = new DirectoryCatalog(".", "Library*.dll");
catalog.Catalogs.Add(directoryCatalog);
catalog.Catalogs.Add(assemblyCatalog);
this.Container = new CompositionContainer(catalog);
CompositionBatch batch = new CompositionBatch();
batch.AddExportedValue(this.Container);
this.Container.Compose(batch);
this.Container.ComposeParts(this);
//from here you can use Class3 with all imports
}
}
[Export(typeof(IClass3)]
public class Class3:IClass3
{
[Import]
public IClass1 Class1 { get; set; }
[Import]
public IClass2 Class2 { get; set; }
public void Trigger()
{
Class1.Trigger();
Class2.Trigger();
}
}
My question title sounds a little bit difficult - sorry. I'm new in MEF :-).
My scenario:
public class MainClass
{
[ImportMany(typeof(ITest))]
private List<ITest> Tests { get; set; }
public MainClass()
{
Init();
}
private void Init()
{
DirectoryCatalog catalog = new DirectoryCatalog(#"./");
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}
[Export("BirthdayJob")]
[Export(typeof(ITest))]
public partial class BirthdayTest : ITest
{
[ImportingConstructor]
public BirthdayUserControl(IParameter parameter)
{
InitializeComponent();
this.Parameter = jobParameter;
}
public IParameter Parameter { get; set; }
}
[Export(typeof(IParameter))]
[Export("BirthdayParameter")]
public class BirthdayJobParameter : IParameter
{
public override string ToString()
{
return "Birthday Remember";
}
}
public interface IParameter : IMefInterface
{
}
public interface IMefInterface
{
}
In the generic list of test, I should have all possible ITest objects with the associated IParameter object. Unfortunately, there aren't any items in the generic list.
Can you help? What did I do wrong?
Thanks in advance.
Regards, pro
//Edit
So I have a compilable Class for my problem :
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace ObjectContracts
{
public class Program
{
static void Main(string[] args)
{
var container = new CompositionContainer(new TypeCatalog(typeof (IFoo), typeof (Bar), typeof(Foo)));
var bar = container.GetExportedValue<Bar>();
Console.WriteLine(bar.Foo.Message);
Console.ReadLine();
}
}
[InheritedExport]
public interface IFoo
{
string Message { get; set; }
}
[Export]
public class Bar
{
[ImportingConstructor]
public Bar([Import("Foo")]IFoo foo)
{
this.Foo = foo;
}
public IFoo Foo { get; set;}
}
[Export("Foo")]
public class Foo : IFoo
{
public Foo()
{
Message = ":-)";
}
public string Message { get; set; }
}
}
What do I do wrong? Please help me :-)
Regards, patrick
The point is that if you use a contract (in your case "BirthdayJob") in your export, you need to specify that in your import as well. Like so:
[Export("BirthdayJob",typeof(ITest))]
public partial class BirthdayTest : ITest
{
// class definition
}
And your import:
public class MainClass
{
[ImportMany("BirthdayJob",typeof(ITest))]
private List<ITest> Tests { get; set; }
// class definition
}
The great thing about contracts is that they allow you to group instances of certain objects of a specific type and filter out any unwanted instances of objects of a specific type.
MEF is the coolest!
[Export("PeopleLivingInEdmontonAlbertaCanada",typeof(IPerson))]
public Person KevinParkinson { get; set; }
I've found the solution. In the export class of the foo class should be a reference of the derived interface. The constructor which have the importingconstructor flag should have also a reference to the interface.
[Export]
public class Bar
{
[ImportingConstructor]
public Bar([Import("Foo", typeof(IFoo))]IFoo foo)
//public Bar([Import(typeof(IFoo))]IFoo foo)
{
this.Foo = foo;
}
public IFoo Foo { get; set;}
}
[Export("Foo", typeof(IFoo))]
public class Foo : IFoo
{
public Foo()
{
Message = ":-)";
}
public string Message { get; set; }
}
Taking a wild guess, it is possibly the working directory of your DirectoryCatalog (depending on how you run the app.)
To verify that this is the problem, replace:
DirectoryCatalog catalog = new DirectoryCatalog(#"./");
With either:
DirectoryCatalog catalog = new DirectoryCatalog(#"<full path to directory>");
or:
AssemblyCatalog catalog = new AssemblyCatalog(typeof(BirthdayTest).Assembly);
Are you exporting IParameter anywhere? In the code you posted, you are not, and this is why the Birthday test isn't being picked up.