Managed Extensibility Framework (MEF)

dategiovedì 25 novembre 2010 alle 18.12  - posted by Manuel Scapolan in C# | Framework .NET

Come posso sviluppare un'applicazione che tenga conto di codice non ancora scritto?
Come posso estendere la mia applicazione senza doverla ridistribuire ogni volta?
Con l'aiuto di MEF posso fare questo e molto altro!

Managed Extensibility Framework (o MEF) è una libreria tutta nuova distribuita con il .NET Framework 4.0 sviluppata per risolvere problemi di estendibilità delle applicazioni, ovvero per poter strutturare in modo rapido e trasparente un'architettura a plug-in.


mef

Come funziona MEF

MEF riesce a comporre le parti di un sistema con architettura a plug-in grazie a due specifiche:

  • Una coppia di attributi [Import]/[Export]. [Import] viene utilizzato per decorare le parti dell'applicazione che espongono un servizio, mentre [Export] definisce le implementazioni di quel servizio (i plug-in appunto)
  • Un catalogo che definisce dove MEF deve cercare per trovare le implementazioni da associare al servizio definito nell'applicazione

MEF diagram

MEF in Action: un esempio

Vogliamo sviluppare un'applicazione che visualizzi all'utente una serie di notizie provenienti da fonti diverse, le fonti devo poterle caricare a runtime.
Prima di tutto devo indicare a MEF il "punto di estensione" dell'applicazione, ovvero una proprietà IEnumerable generica con tipo l'interfaccia che i vari plug-in dovranno poi implementare e lo faccio semplicemente decorando la proprietà con l'attributo [ImportMany].

namespace MEF.Console
{
    class Program
    {
        [ImportMany]
        public IList<INewsService> NewsService { get; set; }

        public Program()
        {
            NewsService = new List<INewsService>();
        }

        static void Main(string[] args)
        {
...

Ed ecco l'interfaccia definita in un assembly esterno.

namespace MEF.PluginInterface
{
  using System;

  public interface INewsService
  {
    string Source { get; }
    string[] NewsOf(DateTime day);
  }
}

Creiamo poi un plug-in che implementi questa interfaccia. Per indicare a MEF che si tratta di un plug-in dell'applicazione devo decorare la classe che implementa il servizio con l'attributo [Export]

namespace Ansa
{
    using System;
    // namespace di MEF
    using System.ComponentModel.Composition;
    using MEF.PluginInterface;

    [Export(typeof(INewsService))]
    public class AnsaNewsService : INewsService
    {
        public string Source {
            get { return "Ansa"; }
        }

        public string[] NewsOf(DateTime day)
        {
            return new string[] 
            { 
                "Internet: Amazon debutta in Italia",
                "Evento 1nn0va sugli Anti-Pattern!"
            };
        }
    }
}

Come ultimo passaggio devo indicare a MEF come trovare i plug-in. Devo definire un catalogo, che nel nostro esempio sarà di tipo Directory, devo creare un container su quel catalogo e utilizzarlo per comporre le parti del sistema (ovvero associare le implementazioni definite con [Export] al contratto definito con [ImportMany].

private void _Compose()
{
  var c = new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory);
  CompositionContainer container = new CompositionContainer(c);
  container.ComposeParts(this);
}

Nel main possiamo quindi utilizzare il servizio:

static void Main(string[] args)
{
  Program p = new Program();
  try
  {
    p._Compose();
  }
  catch (Exception ex)
  {
    System.Console.WriteLine(ex.Message);
  }

  if (p.NewsService == null)
  {
    System.Console.WriteLine("MEF non configurato correttamente.");
    System.Console.Read();
    return;
  }

  foreach (INewsService service in p.NewsService)
  {
    System.Console.WriteLine("Notizie {0}", service.Source);
    DateTime day = DateTime.Today;
    Array.ForEach<string>(service.NewsOf(day),
            news => System.Console.WriteLine("{0} - {1}", 
                      day.ToShortDateString(), news));
  }
  System.Console.Read();
}

A questo punto possiamo definire altri plug-in e sarà sufficiente caricare gli assembly nella directory dell'applicazione per poterli eseguire.

namespace Gazzetta
{
    using System;
    // namespace di MEF
    using System.ComponentModel.Composition;
    using MEF.PluginInterface;

    [Export(typeof(INewsService))]
    public class GazzettaNewsService : INewsService
    {
        public string Source {
            get { return "Gazzetta"; }
        }

        public string[] NewsOf(DateTime day)
        {
            return new string[] 
            { 
                "Inter e Milan qualificate agli ottavi."
            };
        }
    }
}

Riferimenti:

tagsTags:

Refactoring: sostituire lo switch con il polimorfismo

datedomenica 31 ottobre 2010 alle 10.36  - posted by Manuel Scapolan in C#

Lo switch viene spesso utilizzato assieme ad un enumerativo per specificare porzioni di codice da eseguire in base al valore di una determinata variabile. L'utilizzo di tale costrutto rende però il nostro codice rigido e poco mantenibile perché all'aggiunta di un nuovo valore siamo costretti ad aggiornare sia l'enumerativo che lo switch.
Vediamo allora come con un refactoring object-oriented possiamo sostituire lo switch per rendere il nostro codice più flessibile, mantenibile e pulito.

Nell'esempio seguente abbiamo creato una classe MSOfficeLicence con un metodo Price che tramite uno switch ritorna il prezzo di una licenza Office in base al tipo (Home o Business):

public class MSOfficeLicence
{
  public enum LicenceType
  { 
   Home = 0,
   Business = 1
  }
 
  private MSOfficeLicence.LicenceType _type;
  public MSOfficeLicence.LicenceType Type { get { return _type; } }
 
  public MSOfficeLicence(MSOfficeLicence.LicenceType type)
  {
   this._type = type;
  }
 
  public decimal Price()
  {
   switch (_type)
   { 
     case LicenceType.Home:
       return (decimal)99.90;
     case LicenceType.Business:
       return (decimal)180.90;
     default:
       throw new ArgumentNullException("Type not valid");
   }
  }
}

Proviamo ad applicare il polimorfismo e quindi rendiamo la classe MSOfficeLicence e il metodo Price astratti e creiamo una classe derivata per ogni tipo di licenza:

public abstract class MSOfficeLicence
{
  public abstract decimal Price();
}
  
public class MSOfficeHome : MSOfficeLicence
{
  public override decimal Price()
  {
    return (decimal)99.90;
  }
}
  
public class MSOfficeBusiness : MSOfficeLicence
{
  public override decimal Price()
  {
    return (decimal)180.90;
  }
}

Ecco che tolto lo switch possiamo aggiungere nuovi tipi di licenza semplicemente creando una nuova derivata che esegue l'override del metodo Price. La logica del codice client deciderà quindi quale licenza istanziare magari tramite una factory o semplicemente tramite dependency injection:

MSOfficeLicence Licence = new MSOfficeHome();
Console.Write(Licence.Price());

Nota: I prezzi sono solo a titolo di esempio :-)

Trim di un array di stringhe con una sola riga di codice

datevenerdì 4 giugno 2010 alle 22.32  - posted by Manuel Scapolan in C# | Tips and Tricks

L'altro giorno stavo cercando un modo veloce per eseguire il "Trim" degli elementi di un array di stringhe. Analizzando la classe Array mi sono imbattuto nel metodo Array.ForEach. Questo metodo sembrava proprio fare al caso mio, in quanto permette di eseguire una Action per ogni elemento dell'array, purtroppo però come il classico foreach non permette di modificare gli elementi dell'insieme che stiamo scorrendo.
Sicuro di trovare all'interno della classe Array la soluzione al mio problema ho continuato a cercare fino alla scoperta del metodo Array.ConvertAll. Questo metodo esegue la conversione di tipo fra array permettendo di specificare come parametro la funzione di conversione. Forse non l'ho utilizzato per il motivo per cui è stato sviluppato, ma in definitiva mi ha permesso di ottenere lo scopo con una sola riga di codice (04-05):
string[] myArray = new string[]{ " 23", " 45", " 67 "};
Array.ForEach<string>(myArray, s => Console.Write(s));
// risultato: 23 45 67
myArray = Array.ConvertAll<string, string>(myArray, 
             new Converter<string, string>(s => s.Trim()));
Array.ForEach<string>(myArray, s => Console.Write(s));
// risultato: 234567

tagsTags: ,

Distinct con LINQ

datesabato 3 aprile 2010 alle 11.06  - posted by Manuel Scapolan in C# | LINQ

Se vogliamo ottenere con LINQ una lista di elementi senza duplicati possiamo utilizzare il metodo Distinct<T>() dell'interfaccia IEnumerable<T> nel seguente modo:
// Prendo da un array di nomi solo quelli che cominciano con M
string[] names = new string[]{ "Andrea", "Marco", "Manuel", "Marco", 
                               "Sara", "Elisa", "Marta" };
var distinctNames = (from name in names
                    where name.StartsWith("M")
                    select name).Distinct<string>();

tagsTags: ,

About me

manuel scapolanSono un consulente informatico. Nel 2004 terminati gli studi in Ingegneria Informatica (1° livello), ho iniziato come freelance collaborando con una ditta di consulenza informatica ed una agenzia di marketing e comunicazione nello sviluppo di applicazioni web. Attualmente divido il lavoro di sviluppatore e progettista web con attività di formazione nel settore della programmazione.
View Manuel Scapolan's profile on LinkedIn

Follow me on Follow manuelscapolan on Twitter
Member of:
innova

Calendario


<<  febbraio 2012  >>
lumamegivesado
303112345
6789101112
13141516171819
20212223242526
2728291234
567891011

Disclaimer

Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati mediante:
creative commons
Attribuzione: Non commerciale
Condividi allo stesso modo. R.2.5

Books (a bit more about my library)

Domain Driven Design - Eric Evans Applying Domain-Driven Design and Patterns - Jimmy Nilsson Refactoring to Patterns - Joshua Kerievsky Design Patterns -  Erich Gamma, Richard Helm, Ralph Johnson, John M. Vlissides Code Complete Second Edition - Steve McConnell Patterns of Enterprise Application Architecture - Martin Fowler Agile Principles, Patterns, and Practices in C# - Robert C. Martin xUnit Test Patterns - Gerard Meszaros Refactoring - Martin Fowler CLR via C# Second Edition - Jeffrey Richter Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries - Krzysztof Cwalina, Brad Abrams Don't make me think! - Steve Krug Bulletproof Ajax - Jeremy Keith

Manuel Scapolan Copyright © 2007 - 2010 - Tutti i diritti riservati - Powered by BlogEngine.NET 1.5.0.7 - silk icons by famfamfam - Time CET