in

dotNet Umbria

Il primo User Group in Umbria sul mondo .Net

Paolo Possanzini

October 2008 - Posts

  • 7° Workshop "Microsoft Office SharePoint Server - Tecnologia e strumenti per lo sviluppo e amministrazione"

    Facciamo un grande in bocca al lupo per i nostri amici di dotnetmarche, che domani in collaborazione con Microsoft e l'università di Camerino, organizzano il 7 workshop su sharepoint. Ancora un grandissimo in bocca al lupo.
    Ecco il link alla pagina dell'evento : http://dotnetmarche.org/eventi/Default.aspx?IDevento=23

  • Windows Azure ... un passo verso il Cloud computing.

    Riporto una notizia che trovo molto importante per lo sviluppo futuro di applicazioni basate sui servizi.
    Microsoft in occasione del PDC ha presentato una nuovissima versione di windows : Windows Azure.

    Il nuovissimo sistema operativo è studiato per supportare a livello di kernel il Cloud Computing.
    Sono già disponibili  SDK ed esempi su come l'infrastruttura funzionerà.

    La cosa mi intriga parecchio perchè il cloud computing è una cosa che mi affascina da parecchio tempo. Avere a disposizione una infrastruttura in grado di supportarlo permette la creazione di applicazioni sicuramente più interessanti.
    Vediamo come evolverà il progetto. Vi riporto il link al sito ufficiale dove è già possibile scaricare dei preview SDK ed esempi.

    http://www.microsoft.com/azure/default.mspx

  • Custom Linq Provider (parte 2)

    Continuiamo a vedere come implementare un provider custom che supporta la sintassi Linq.

    Per prima cosa creiamo un oggetto che implementa le interfacce IQueryable e IQueryProvider. Ecco una prima implementazione :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace CustomLinqProvider
    {
      public class myQueryableObject<T> : IQueryable, IQueryable<T>, IQueryProvider
      {
        private System.Linq.Expressions.Expression _expression = null;

        #region IQueryable Members

        public Type ElementType
        {
          get { return typeof(T); }
        }

        public System.Linq.Expressions.Expression Expression
        {
          get { return _expression; }
          internal set { _expression = value; }
        }

        public IQueryProvider Provider
        {
          get { return this; }
        }

        #endregion

        #region IEnumerable Members

        public System.Collections.IEnumerator GetEnumerator()
        {
          throw new NotImplementedException();
        }

        #endregion

        #region IEnumerable<T> Members

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
          throw new NotImplementedException();
        }

        #endregion

        #region IQueryProvider Members

        public IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression)
        {
          myQueryableObject<TElement> mq = new myQueryableObject<TElement>();
          mq.Expression = expression;
          return mq;
        }

        public IQueryable CreateQuery(System.Linq.Expressions.Expression expression)
        {
          return CreateQuery<T>(expression);
        }

        public TResult Execute<TResult>(System.Linq.Expressions.Expression expression)
        {
          throw new NotImplementedException();
        }

        public object Execute(System.Linq.Expressions.Expression expression)
        {
          throw new NotImplementedException();
        }

        #endregion
      }
    }

     

    I metodi che per adesso ci interessano sono i due metodi CreateQuery. questi metodi vengono chiamati al termine della creazione della Expression Tree da parte del compilatore. La Expression Tree viene preparata e viene passata come oggetto Expression che può essere interpretato ed utilizzato per eseguire le nostre query.

    Come è possibile vedere non viene fatta alcun tipo di elaborazione, semplicemente la expression viene memorizzata all'interno del membro _expression e vine e restituito un oggetto dello stesso tipo che stiamo creando. Questo perchè è molto spesso conveniente implementare IQueryProvider e IQueryable all'interno della stessa classe. Capiremo tra poco il perchè.

    Abbiamo detto che i metodi CreateQuery vengono invocati appena la creazione della expression tree è completata, tuttavia non viene eseguita nessuna elaborazione. Il motivo di questa tardata esecuzione è che ancora non sappiamo come applicare l'Expression Tree; in modo particolare non sappiamo se dobbiamo attenderci un risultato singolo oppure una lista di elementi e il metodo che viene invocato per l'esecuzione della query potrebbe essere differente.

    Facciamo un esempio. Nella query

    var result = from rr in myQueryObject<tabella1>()
                       select rr;

    abbiamo definito che intendiamo ottenere tutti i campi della tabella 1 senza nessun filtro. Tuttavia l'esecuzione potrebbe essere eseguita in uno dei seguenti metodi:

    rr.ToArray();
    rr.Count();
    rr.FirstOrDefault();

    Ognuno di questi metodi applica la query eseguendo operazioni differenti:
    La prima restituisce un risultato multiplo, la seconda esegue un raggruppamento su tutti i record per restituirci il numero di elementi all'interno della tabella, il terzo restituisce il primo elemento completo.

     

    Vediamo quindi come viene invocata l'esecuzione della query.
    La prima grande differenza tra i metodi che invocano la query è il tipo di risultato che andiamo ad ottenere.

    Se il risultato è un elenco di elementi, allora verranno invocati i metodi GetEnumerator() della nostra classe.
    Se invece il risultato è un elemento singolo, allora la query verrà arricchita con l'informazione della chiamata all'ultimo metodo e verrà invocato il metodo Execute().

    Tornando al nostro esempio, nel caso di ToArray() verà invocato il GetEnumerator, nel caso di Count() o di FirstOrDefault(), verrà aggiunto all'Expression Tree l'informazione della chiamata all'ultimo metodo e verrà effettivamente invocato il metodo Execute.

    Quindi per implementare il nostro parser, dovremo implementare sia i metodi Execute che i metodi GetEnumerator di cui abbiamo già gli stub pronti perchè abbiamo implementato le interfaccie.

    E comodo avere implementate entrambe le interfaccie all'interno della stessa classe, poichè il metodo GetEnumerator viene fornito dalla interfaccia IQueryable, mentre il metodo Execute viene fornito dalla interfaccia IQueryProvider.

    E' venuto il momento di capire l'Expression Tree e piegarla alla nostra volontà. L'Expression Tree è la rappresentazione a livello linguaggio delle operazioni che devono essere eseguite per ottenere un certo set di dati.
    Quello che vogliamo fare per prima cosa è capire come "leggere" l'expression tree, per poi interpretarla ed eseguire operazioni Custom.

    Scriviamo questa Query:

    myQueryableObject<int> test = new myQueryableObject<int>();
    var result = from i in test
                 select i;

     

    e vediamo come viene interpretata eseguendo result.Expression.ToString();

     

    value( CustomLinqProvider.myQueryableObject`1[System.Int32] ).Select( i => i )

     

    quello che vediamo è la visualizzazione testuale dell'expression tree. come possiamo vedere ogni operazione viene esplicitata in modo del tutto simile alla chiamata a metodi.
    Quello che accade in realtà, è che ogni sezione della nostra espressione viene rappresentata attraverso un nodo della expression tree.

    Ogni nodo è composto da un oggetto che eredita dalla classe expression. L'oggetto specializza la classe base in modo da rappresentare in modo più fedele possibile il significato del nodo stesso.
    Vediamo quali sono gli oggetti che ereditano da Expression.

    • BinaryExpression
    • ConditionalExpression
    • ConstantExpression
    • InvocationExpression
    • LambdaExpression
    • ListInitExpression
    • MemberExpression
    • MemberInitExpression
    • MethodCallExpression
    • NewArrayExpression
    • NewExpression
    • ParameterExpression
    • TypeBinaryExpression
    • UnaryExpression

     

    Ognuno di questi oggetti rappresenta un particolare tipo di nodo all'interno della nostra espressione. Ogni classe può tuttavia rappresentare più di un tipo di nodo.
    I tipi possibili di nodo sono rappresentati da un Enum.

    public enum ExpressionType
    {
        Add,
        AddChecked,
        And,
        AndAlso,
        ArrayLength,
        ArrayIndex,
        Call,
        Coalesce,
        Conditional,
        Constant,
        Convert,
        ConvertChecked,
        Divide,
        Equal,
        ExclusiveOr,
        GreaterThan,
        GreaterThanOrEqual,
        Invoke,
        Lambda,
        LeftShift,
        LessThan,
        LessThanOrEqual,
        ListInit,
        MemberAccess,
        MemberInit,
        Modulo,
        Multiply,
        MultiplyChecked,
        Negate,
        UnaryPlus,
        NegateChecked,
        New,
        NewArrayInit,
        NewArrayBounds,
        Not,
        NotEqual,
        Or,
        OrElse,
        Parameter,
        Power,
        Quote,
        RightShift,
        Subtract,
        SubtractChecked,
        TypeAs,
        TypeIs
    }
    
    
    

     

    Per interpretare in modo corretto quindi un Expression Tree è sufficiente navigare l'albero e interpretare ogni tipo di nodo. Nel prossimo post vedremo con scrivere in modo corretto un parser per interpretare l'expression tree.
    Vi consiglio di dare un'occhiata ai link che vi riporto.

    Link utili :

    http://msdn.microsoft.com/en-us/library/bb882521.aspx
    http://msdn.microsoft.com/en-us/library/bb882536.aspx
    http://msdn.microsoft.com/en-us/library/bb397951.aspx

  • 070-432 : MS SQL Server 2008, Implementation and Maintenance -> Passed.

    Passato l'esame per l'implementazione e la manutenzione dei DB in SQL Server 2008 .. :D

  • Custom Linq Provider (parte 1)

    Diciamo che il rientro dalle ferie è stato più duro del previsto. In questo tempo però ho avuto modo di approfondire alcuni argomenti piuttosto interessanti legati alla programmazione con il framework 3.5 in modo particolare con Linq.

    Facendo una veloce ricerca su google possiamo accorgerci che le implementazioni custom di linq stanno crescendo in modo piuttosto veloce. Da quelle per fare le query su Amazon a quelle per cercare documenti in Sharepoint a tutto quello che vi può venire in mente. Questo è possibile perchè la sintassi introdotta con il framework 3.5 e che abbiamo visto utilizzare in LinqtoSql o in LinqtoXml non è legata alla implementazione di alcune librerie di accesso ai dati di Microsoft, ma è un nuovo tipo di sintassi introdotta a livello di linguaggio e supportata da 2 interfaccie che sono presenti nelle librerie base del framework 3.5. Quindi è sufficiente reimplementare le interfaccie di base per rendere i nostri oggetti pienamente compatibili con la sintassi Linq.

    L'obiettivo di questo articolo è di comprendere come Linq viene interpretato a livello di compilatore e come viene utilizzato per effettuare ricerche sui dati. Arriveremo anche customizzare il comportamento di Linq per effettuare query su fonti dati non tradizionali. In TeamDev abbiamo seguito questo approccio per estendere le funzionalità del nostro layer di accesso ai dati al fine di sfruttare le interessantissime caratteristiche della sintassi Linq.

    Le interfaccie di base

    Abbiamo detto che linq si appoggia a due interfaccie :

    1. IEnumerable (presente da tempo nella libreria mscorlib)
    2. IQueryable (introdotta con il framework 3.5 nella libreria System.Core)

    queste interfaccie vengono riconosciute dal compilatore e rendono possibile l'utilizzo della sintassi Linq. Comprendere come il compilatore utilizza le interfaccie, aiuta a comprendere come Linq funziona e come possiamo customizzarne il comportamento.

    public interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }
    
     

    IEnumerable è presente dalla prima implementazione del framework a supporto della sintassi foreach. Possiamo infatti utilizzare il foreach solo sugli oggetti che implementano IEnumerable.
    Siccome Linq utilizza IEnumerable per effettuare le query che coinvolgono collection e liste, questo ci lascia intuire che tutti i foreach possono essere trasformati in query Linq.

    public interface IQueryable : IEnumerable
    {
        Type ElementType { get; }
        Expression Expression { get; }
        IQueryProvider Provider { get; }
    }
     

    IQueryable è stata introdotta con la versione 3.5 del framework e si trova in System.Core. L'implementazione di IQueryable permette di customizzare il comportamento della query linq per adattarla ad una sorgente dati particolare.
    Mentre le query  su oggetti IEnumerable vengono eseguite in memoria , le query su IQueryable possono essere trasformate in query custom su oggetti di ogni tipo.

    Tutto questo è possibile grazie alla classe Expression e alla interfaccia IQueryProvider esposte attraverso IQueryable

    Come vengono utilizzate le interfaccie

    Quando utilizzata con IQueryable, la query linq viene trasformata dal compilatore in un oggetto di tipo Expression (detto anche ExpressionTree , perchè ha una struttra ad albero).
    L'oggetto Expression viene passato ad un provider che provvede ad interpretarlo e restituirà un risultato coerente con la query effettuata.

    Quando utilizzata con IEnumerable, la query linq viene immediatamente eseguita attraverso degli extension methods che estendono le funzionalità della interfaccia IEnumerable.

    E facile intuire quindi che per customizzare il comportamento delle query Linq dobbiamo implementare una delle suddette interfaccie.
    Agendo con Reflector ci accorgiamo anche che il modo più corretto per intervenire è utilizzare IQueryable piuttosto che IEnumerable, questo perchè IEnumerable ha lo scopo di effettuare operazioni in memoria, cosa che probabilmente vorremo continuare a fare.

    IQueryable invece è pensata per agire fuori dal contesto dell'applicazione e quindi è l'interfaccia che meglio si presta alla realizzazione di provider custom per l'accesso ai dati.

    Nel prossimo post vedremo come è strutturata la classe Expression e come creare un parser per tradurre le Expression Tree in tutto ciò che vogliamo.

dotNet Umbria 2007-2008
Powered by Community Server (Commercial Edition), by Telligent Systems