问题
This may be simple but as I am new to MEF arena that is why I am having some difficulty to figure out the solution to my problem.
I am working on an application using WPF + Prism with MEF as DI container. I want to tie my object (i.e. RuleFile
) with each application instance by associating it with the file say RuleFile1.ruleapp. Therefore I have decorated it with attribute [PartCreationPolicy(CreationPolicy.Shared)]
to treat it as singleton so that it remains same throughout the application with each application instance.
[Serializable()]
[Export]
[PartCreationPolicy(CreationPolicy.Shared)]
public class RuleFile : NotifyPropertyChanged, IRuleFile { }
Next, at the time of ViewModel [ImportingContructor]
as shown below, the object is same as desired.
[ImportingConstructor]
public RuleViewModel(RuleFile ruleFile)
[ImportingConstructor]
public SchemaViewModel(RuleFile ruleFile)
Until now everything is smooth.
Using below piece of code, i am trying to get the same exported object that is passing to view model as mentioned above but container.GetExportedValue<IRuleFile>()
is giving a new object reference that is not the same one:
var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
var exportObj = container.GetExportedValue<IRuleFile>();
Question 1: Why i am getting a different reference although object should be the same as it is a singleton object with CreationPolicy.Shared
?
Question 2: Ultimately this all effort is to exchange/replace RuleFile
exported object with the deserialized one in the MEF DI container?
回答1:
You don't replace instances in a MEF container, that is not how it works, and it's dangerous and completely unmanageable. (Also keep in mind C# is not C where you can simply change pointed-to objects). Suppose a class X gets an instance from the container and has a factory method in which it passes this instance another class Y. Now suddenly you want to 'replace' that instance. First of all, what would have to happen to the old instance? Disposed? Kept alive? Mailed to your grandmother? Second, if X got the instance using GetExportedValue
, how are you going to notify it the instance it has is now gone and is replaced by something else? You can't.. Third, suppose X used Import
instead and by some magic it gets notified that it's instance has been replaced. How is it now in turn going to notify Y that the instance is replaced? You can't, unless you keep a list of Y. Etc, etc. I hope this made it clear replacing objects in a container isn't a good idea.
There are a couple of things you can do instead though:
Just make sure your RuleFile is created and injected in the container before it is imported anywhere. This also makes most sense: I have the impression RuleFile is some kind of application-wide configuration, so it is desirable this configuration is setup completely before the application even starts. Override
MefBootstrapper.ConfigureContainer
, deserialize you RuleFile instance and useComposeExportedValue
to set it as the sole instance in the container. If deserializition fails, you aither show an error dialog and abort your app, or supply a default configuration and inject that instead.Provide a wrapper around RuleFile wich reads from the deserialized RuleFile if available, or else supplies default values. So the observed behaviour is the same as RuleFile being replaced in the container. However this has the major drawback that if there is code using the IRuleFile instance before the file is loaded, it gets different values than after the file is loaded. Which is why the first approach is better. Example:
private class DefaultRuleFile: IRulefile { string SomeProperty { get{ return "DefaultValue"; } } } [Export( typeof( IRulefile ) )] [Export( typeof( RuleFileImplementation ) )] [PartCreationPolicy(CreationPolicy.Shared)] public class RuleFileImplementation : IRulefile { private IRuleFile impl; RuleFileImplementation() { impl = new DefaultRuleFile(); } string SomeProperty { get{ return impl.SomeProperty; } } void LoadFromFile( string file ) { impl = SerializationHelper.Deserialize<IRuleFile>( file ); } } //at some point in the application: container.GetExportedValue<RuleFileImplementation>().LoadFromFile( "file" )
来源:https://stackoverflow.com/questions/25595772/how-to-exchange-replace-the-shared-singleton-object-in-mef-container