Versioned services/exports

Jul 31, 2010 at 10:41 PM
Edited Aug 1, 2010 at 8:46 AM

Hi Sacha,

first of all, sorry for my english and you doing great job here. I think Cinch, especially CinchV2, is the best MVVM framework for me.

Little bit about me. I'm pretty new in the world of WPF, MVVM and MEF, but I try my best. Eight months ago I started to implement some kind of RCP (Rich Client Platform like eclipse) for my actual project and future projects. My choice was WPF+Cinch (V1)+AvalonDock+MEF for extensibility. So I modefied your framework a little bit for MEF support and started my work. This platform running well and have many features like LayoutManager(AvalonDock), DocumentManager, Menu and Toolbar support and all this is MEF based. So it's very easy to extend menu, document types and ... of some application.

CinchV2 and MEF support was great news for me, but it's not so easy to upgrade for me (e.g. i did't use MEFedMVVM). But I had allways the feeling to not use all the power of your framework, so I take a decision to merge my ideas with CinchV2 in a new platform project and try to make it cleaner and more "Cinch based".

So first of all I need "versioned exports" for easy way to change some things, e.g. UIVisualiserService or LayoutManager, in the future by simply placing some dll with new version of some service and done. So I made my own custom ExportProvider to get the right exports. It's looks like this

 

[ExportService(ServiceType.Runtime, typeof(IDocumentManager))]
[ExportMetadata("Version", "1.0.0.0")]
public class DocumentManager : ViewModelBase, IDocumentManager
{
    ...
}

 

and ExportPrvider.GetExportsCore()

 

protected override System.Collections.Generic.IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
        {
            ...

            List<Export> exports = new List<Export>();
            IEnumerable<Export> exp = null;
            if (!_exportProvider.TryGetExports(definition, atomicComposition, out exp))
            {
                var newExp = from ex in exp
                             where !ex.Metadata.Contains(new KeyValuePair<string, object>("Default", true)) &&
                                    ex.Metadata.ContainsKey("Version")
                             select ex;
                if (newExp.Count() == 1)
                {
                    exports.Add(newExp.First());
                }
                else if (newExp.Count() > 1)
                {
                    var versions = newExp.Select(x => new Version(x.Metadata["Version"] as string)).ToList();
                    versions.Sort();
                    Export export = newExp.Where(x => (new Version(x.Metadata["Version"] as string)).Equals(versions.Last())).First();
                    exports.Add(export);
                    
                }
                else { return Enumerable.Empty<Export>(); }
            }
            else { 
                return Enumerable.Empty<Export>(); 
            }

            return exports.Select(export => new Export(export.Definition, () => GetValue(export)));
        }

The problem is, it's necessary to change some code in MEFedMVVM library for additional ExportProviders support.

 

I think it's general an useful feature and I'll be very very happy to have this in the MEFedMVVM or Cinch library. I can post all the chenges if you want.

Thank you,

Dima

Coordinator
Aug 2, 2010 at 9:49 AM

Dima

 

Sounds like a cool idea. Problem is MeffedMVVM is Marlons code, Cinch takes a dependency on that. I'll let Marlon know about this, and he may ask you for more details. But sounds like a great idea.

 

Aug 2, 2010 at 10:09 AM

This is quite a samll change to do for MEFedMVVM and I agree that it is a good idea.... MEFedMVVM uses a custom Export Provider itself... All I need to do is change the IComposer interface to include a GetExportProviders...

I think I will add this ... Will let you know when I do.

 

Thanks for the good suggestion

Aug 2, 2010 at 12:16 PM
Edited Aug 2, 2010 at 12:53 PM

Thank you both,

may be my solution helps you marlon.

I added an inteface to your library

 

public interface IMEFedMVVMExportProvider
{
void SetContextToInject(object context);
ExportProvider SourceProvider { get; set; }
ComposablePartCatalog Catalog { set; }
}

 

used this for my ExportProvider and set an Export on it, like this

 

[ExportService(ServiceType.Both, typeof(IMEFedMVVMExportProvider))]
public class VersionedExportProvider : ExportProvider, IDisposable , IMEFedMVVMExportProvider
{
...
}

then I modified your library on two places

 

1. on LocatorBootstrapper.EnsureLocatorBootstrapper() like this

 

IList<ExportProvider> exportProviders = 
(from p in mvvmCatalog.Parts
let eds = p.ExportDefinitions
from ed in eds
where ed.ContractName == "MEFedMVVM.ViewModelLocator.IMEFedMVVMExportProvider"
select (ExportProvider)p.CreatePart().GetExportedValue(ed)).ToList();

...

foreach (var item in exportProviders)
{
var exportProvider = item as IMEFedMVVMExportProvider;
if (exportProvider != null)
{
exportProvider.Catalog = mvvmCatalog;
}
}

...

CompositionContainer container = new CompositionContainer(exportProviders.ToArray());
foreach (var item in exportProviders)
{
var exportProvider = item as IMEFedMVVMExportProvider;
if (exportProvider != null)
{
exportProvider.SourceProvider = container;
}
}

and second place IMPORTEND! on MEFedMVVMResolver.GetViewModelByContract(string vmContractName, object contextToInject).
You use custom ImportDefinition with ImportCardinality.ZeroOrMore

var definition = new ContractBasedImportDefinition(vmContractName, viewModelTypeIdentity,
requiredMetadata, ImportCardinality.ZeroOrMore, false,
false, CreationPolicy.Any);


and then you getting ViewModel with


var vmExport = vmExports.FirstOrDefault(e => e.Metadata[ExportViewModel.NameProperty].Equals(vmContractName));


so I'm getting problems here if I have multiple ViewModels with differen versions.
My question is, why you use this definition with ImportCardinality.ZeroOrMore?

 

 

Aug 4, 2010 at 9:27 AM

Both these 2 things have been addressed in the latest check in I did in MEFedMVVM

http://mefedmvvm.codeplex.com/SourceControl/list/changesets

You can now specify multiple Custom ExportProviders via the IComposer

The second issue is addressed as well.. it was a bug! Thanks for all the help and very good suggestions! Keep it coming mate

 

Aug 4, 2010 at 2:43 PM
Best thanks Marlon, great job
Coordinator
Aug 4, 2010 at 4:41 PM

Cool, I will resync after I write next article, and also add a few enhancements to Cinch people have asked for.