Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Pierrick's Blog

.Net "full power"

Cab + MEF

Avec l’arrivée du Framework 4, une nouvelle brique fait son apparition : MEF (Managed Extensibility Framework).

Or par le passé il existait un framework fourni par Pattern & Practices, le composition UI application block (CAB).

Le CAB(Composite UI Application Block)  est un block applicatif pour concevoir des applications modulaires. les liaisons entre modules applicatifs sont faite par injection de dépendances

MEF : Managed Extensibility Framework est un nouveau framework pour concevoir des applications modulaires, fourni avec le framework .Net 4.0. comme pour le CAB, la liaison entre module est aussi par injection de dépendances.

Ces deux frameworks ont la même finalité, mais l’un datant de 2005, et l’ autre 2010 (date de sortie du framework 4 dans sa version définitive)

le but du post est de montrer que l’on peut intégrer dans une application “CAB”, des modules utilisant MEF et le framework 4.0 sans recompiler votre application initiale.

Rappel : une application CAB de base est une “coquille vide”, appelé  “shell”. C’est ce shell qui héberge les différents modules de l’application.

voici le shell sans module applicatif (une forme vide)

image

un fichier (profilecatalog.xml) contenant les différents modules à charger est associé à cette application

<SolutionProfile xmlns="http://schemas.microsoft.com/pag/cab-profile/2.0">
  <Section Name="Services">
    <Modules>
      <ModuleInfo AssemblyFile="Infrastructure.Module.dll" />
    </Modules>
  </Section>
  <Section Name="Layout">
    <Dependencies>
      <Dependency Name="Services" />
    </Dependencies>
    <Modules>
      <ModuleInfo AssemblyFile="Infrastructure.Layout.dll" />
    </Modules>
  </Section>
  <Section Name="Core">
    <Dependencies>
      <Dependency Name="Services" />
      <Dependency Name="Layout" />
    </Dependencies>
    <Modules>
      <ModuleInfo AssemblyFile="Core.dll" />
    </Modules>
  </Section>
  <Section Name="Libraries">
    <Dependencies>
      <Dependency Name="Services" />
      <Dependency Name="Layout" />
      <Dependency Name="Core" />
    </Dependencies>
    <Modules>
    </Modules>
  </Section>
  <Section Name="Editor">
    <Dependencies>
      <Dependency Name="Core" />
      <Dependency Name="Libraries" />
    </Dependencies>
    <Modules>
      <ModuleInfo AssemblyFile="Editor.dll" />
      <ModuleInfo AssemblyFile="ErlangProject.dll" />
    </Modules>
  </Section>
</SolutionProfile>

Les différents modules sont maintenant disponible au sein de l’application.

image 
en rouge le visuel des différents modules applicatifs

Nous allons maintenant intégrer MEF dans cette application. Il faut tout d’abord quelle s’execute avec le framework .Net 4.0, pour cela il suffit de modifier le fichier app.config comme suit :

<configuration>
  ...
 

  <startup useLegacyV2RuntimeActivationPolicy="true">
    <
requiredRuntime version="4.0.21006"/>
  </
startup>
</
configuration>

l’application maintenant s’execute avec le framework 4.0, et supporte le legacy… ;-), et donc de charger des assemblies .Net  2.0

pour que notre module MEF soit prise en compte par le shell, il faut completer le “profilecatalog.xml”, et y ajouter notre nouveau module “CAB”  compilé en 4.0

<SolutionProfile xmlns="http://schemas.microsoft.com/pag/cab-profile/2.0">
    <Modules>
      <ModuleInfo AssemblyFile="Editor.dll" />
      <ModuleInfo AssemblyFile="ErlangProject.dll" />
     
<ModuleInfo AssemblyFile="WorkflowIntegration.dll" />
    </Modules>
  </Section>
</SolutionProfile>

Cette assembly contient le code d’ initialisation du container MEF, mais aussi le code pour être reconnu comme en tant que module CAB.

Pour l’exemple j’ai intégré le nouveau designer de workflow du framework 4.0, il existe plusieurs tutoriel pour cela :

http://msdn.microsoft.com/en-us/library/dd489451(VS.100).aspx
http://msmvps.com/blogs/theproblemsolver/archive/2009/12/23/rehosting-the-workflow-designer-in-wf4.aspx

je me suis basé sur le deuxième exemple, en ajoutant un pincée de “MEF” ;-) cela donne :

image

regardons un peu sous capot, comment cela s’architecture.

l’intégration du conteneur MEF dans un module CAB est très simple :

    class WorkflowIntegrationModuleController : WorkItemController
    {
       
public override void Run()
        {
           
base.Run();
            AddServices();
        }

       
private void AddServices()
        {
           
//initialisation du container MEF
           
var catalog = new AggregateCatalog();
           
var thisAssembly =
               
new AssemblyCatalog(
                    System.Reflection.
Assembly.GetExecutingAssembly());

            catalog.Catalogs.Add(thisAssembly);
            catalog.Catalogs.Add(
new AssemblyCatalog(typeof(IDesignerController).Assembly));
           
//le sous répertoire nommé MefAddin, est utilisé pour les modules MEF
           
var extensionDir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
            extensionDir = System.IO.
Path.Combine(extensionDir, "MefAddin");
           
if (System.IO.Directory.Exists(extensionDir))
                catalog.Catalogs.Add(
new DirectoryCatalog(extensionDir));     
           
var container = new CompositionContainer(catalog);
           
//ajout du container en tant que service CAB, le usercontrol hostant le designer WF est une [SmartPart] (CAB)
           
this.WorkItem.RootWorkItem.Services.Add<CompositionContainer>(container);
        }
    }

Ce modulecontroller est instancié au chargement de l’assembly, lors de la phase d’initialisation du module.

 

Coté visuel, l’hébergement du Designer de wortkflow est faite dans un usercontrol windows forms, qui lui même sera hébergé dans le shell en tant que “smartpart” (terme CAB designant une zone visuel dans le shell)

image
vous noterez au passage que l’intégration de WPF dans du windows forms fonctionne très bien dans l’ide, et cela via la classe “ElementHost”.

Ce usercontrol réalise la liaison WindowsForms<->WPF, mais aussi la liaison entre CAB et MEF.

    public partial class WfDocumentDesigner : WfDocumentGenericDesigner
    {
       
public WfDocumentDesigner()
        {
            InitializeComponent();

        }

       
public WfDocumentDesigner(WfDocument doc)
            : base(doc)
        {
            InitializeComponent();
           
this.elementHost1.HostContainer.Loaded += new System.Windows.RoutedEventHandler(HostContainer_Loaded);
        }

       
void HostContainer_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
           
if (DesignerController != null)
            {
               
var documentContent = this.Document.Content;
               
if (string.IsNullOrEmpty(documentContent))
                {
                    DesignerController.New(
new FlowchartKind());
                }
               
else
                {
                   
using (System.IO.StringReader sr = new System.IO.StringReader(documentContent))
                    {
                        DesignerController.Load(sr);
                    }
                }
            }
        }
       
//injection via MEF
        [
Import(AllowDefault=true)]
       
public IDesignerController DesignerController { get; set; }

       
CompositionContainer _Container;
       
//injection via CAB
        [
ServiceDependency]
       
public CompositionContainer CompositionContainer
        {
           
get
            {
               
return _Container;
            }
           
set
            {
                _Container =
value;
               
if (value != null)
                {
                   
//injection du contenu WPF (designer WF)
                   
value.ComposeParts(this.elementHost1.Child);
                   
//injection de this
                   
value.ComposeParts(this);
                    RegisterDesigners();
                }
            }
        }

       
//injection via MEF
        [
ImportMany(typeof(IRegisterMetadata))]
       
public IEnumerable<IRegisterMetadata> RegisteredMetadata { get; set; }

       
/// <summary>
       
/// enregistrement des designers pour les activités du designer de workflow
       
/// </summary>
       
private void RegisterDesigners()
        {
           
//designers fournis par le framework
           
new System.Activities.Core.Presentation.DesignerMetadata().Register();
            //designers fournis par composition via MEF
            foreach (var item in RegisteredMetadata)
            {
               item.Register();
            }
        }
    }

on remarque bien ici que les injections CAB et MEF se font par des attributs.
- Import, ImportMany, Export,… pour MEF
- ServiceDepencency, SmartPart,… pour le CAB

En revanche si nous regardons l’implémentation du designer de workflow, il n’utilise que MEF et WPF 

    public partial class DesignerHost : UserControl, IDesignerController
    {
       
private WorkflowDesigner _workflowDesigner;
       
private string _fileName;

       
public DesignerHost()
        {
            InitializeComponent();

           
this.Loaded += new RoutedEventHandler(DesignerHost_Loaded);
        }

       
/// <summary>
       
/// interface de conrtôle du designer (chargement, enregistrement,...) 
       
/// </summary>
        [
Export]
       
public IDesignerController Controller
        {
           
get
            {
               
return this;
            }
        }

       
/// <summary>
       
/// liste des activités qui seront affichées dans la toolbox
       
/// </summary>
        [
ImportMany("Toolbox.Activities", AllowRecomposition=true)]
       
public IEnumerable<Lazy<IActivityToolboxProvider, IWorkflowCategory>> ToolboxItems { get; set; }

       
private void LoadToolbox(string categoryName) ...

   }

 

En conclusion, CAB et MEF cohabite très bien, ce qui peut permettre de faire des migrations/évolutions d’applications en douceur, tout en bénéficiant des dernières avancées technologiques.
Effectivement l’exercice ici est d’autant plus facile, que mon application est au départ modulaire. 

nota : une fois que l’application utilise le framework 4.0, il n’est plus possible de la debogger avec VS 2008, il vous faut utiliser VS 2010.

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Posted: samedi 2 janvier 2010 15:19 par pierrick

Commentaires

Pas de commentaires

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- [ #Yammer ] From Mailbox to Yammer and back / De votre messagerie vers Yammer et retour ! par Le blog de Patrick [MVP SharePoint] le 09-15-2014, 11:31

- [ #Office 365 ] New service settings panel / Nouveau panneau de paramétrage des services par Le blog de Patrick [MVP SharePoint] le 09-11-2014, 08:50

- Problème de déploiement pour une démo SharePoint/TFS? par Blog de Jérémy Jeanson le 09-10-2014, 21:52

- [ #Office365 ] Delve first impressions / Premières impressions sur Delve par Le blog de Patrick [MVP SharePoint] le 09-09-2014, 16:57

- [ #Office365 ] How to change Administration console language ? / Comment changer la langue de la console d’administration ? par Le blog de Patrick [MVP SharePoint] le 09-09-2014, 08:25

- [ #SharePoint 2013 ] Suppression de bases de données en état “Pas de Réponse” par Le blog de Patrick [MVP SharePoint] le 09-04-2014, 14:10

- Changer l’adresse d’une ferme Office Web Apps associée à SharePoint par Blog de Jérémy Jeanson le 09-01-2014, 22:21

- Une ferme #SharePoint 2013 dans @Azure en quelques clics (1ère partie) ! par Le blog de Patrick [MVP SharePoint] le 08-28-2014, 18:52

- SharePoint 2013: Préparation de la migration - Création des site Templates dans 2010 et 2013 par Blog Technique de Romelard Fabrice le 08-20-2014, 16:31

- [ #Yammer ] How to change interface language ? Comment changer la langue de l’interface ? par Le blog de Patrick [MVP SharePoint] le 08-20-2014, 14:21