giovedì 14 giugno 2012

How To : Dynamic Lambda C#

Non è la prima volta che mi affanno sull'argomento, ma ammetto che in precedenza mi sono arreso, abbandonando l'intento. Ammetto questa volta ho probabilmente "aggirato" il problema, ma credo che la soluzione definitiva sia sempre più vicina, non vicinissima ... ma più vicina di prima. Per provare questo sorgente vi serve sicuramente: Visual studio 2010 una soluzione due progetti Ho fatto un pò di prove per il momento il risultato mi piace. Partiamo dal primo progetto che di contro è il più semplice,ed è una class library.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DynamicLambdaTester.Entity
{

    public class entity
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    }

    public class entityList : List<entity>
    {

    }

}
bravi .. è proprio la mia solita entity.. giusto perchè non mi smentisco mai. Questo è il secondo progetto che fra le referenze annnovera per forza il primo, che naturalmente per pura praticità è una console application.
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Data;
using System.Linq;
using Microsoft.CSharp;
using DynamicLambdaTester.Entity;

namespace DynamicLambdaTester
{
   class Program
    {
        
        static void Main(string[] args)
        {

            entity e1 = new entity() 
                    {ID=1,Name="Fabio",Description="Team Leader"};
            entity e2 = new entity() 
                    {ID=3,Name="Claudio",Description="CoLeader"};
            entity e3 = new entity() 
                    {ID=3,Name="Giorgio",Description="Senior Developer"};
            entity e4 = new entity()
                    {ID=2,Name="Donato",Description="Senior Developer"};
            entity e5 = new entity() 
                    {ID=3,Name="Carlo",Description="Senior DBA"};


            entityList el = new entityList() ;
            el.Add(e1);
            el.Add(e2);
            el.Add(e3);
            el.Add(e4);
            el.Add(e5);
                      

            string prms = "entityList el";
            string body = "return el.Where " + 
                    "(c => c.Description.Contains(\"Senior\")).ToList<entity>();";
          
            
            var ed = DynamicSelect("List<entity>", prms, body, el);

            for (int i = 0; i < ed.Count(); i++)
            {
                Console.WriteLine(ed[i].Name);
            }
                       
            Console.ReadKey();
        }

        public static List<entity> DynamicSelect(
                string typeRet, 
                string parameters, 
                string body, 
                entityList tDataParams)
        {

            CSharpCodeProvider c = new CSharpCodeProvider();
            CompilerParameters cp = new CompilerParameters();

            cp.ReferencedAssemblies.Add("system.dll");
            cp.ReferencedAssemblies.Add("system.core.dll");
            cp.ReferencedAssemblies.Add("system.xml.dll");
            cp.ReferencedAssemblies.Add("system.data.dll");
            cp.ReferencedAssemblies.Add("system.data.linq.dll");
            cp.ReferencedAssemblies.Add("[PERCORSO DEL COMPILATO DEL PRIMO PROGETTO " +
                                        @"Es. C:\USER\..\DynamicLambdaTester.Entity.dll");
           

            cp.CompilerOptions = "/t:library";
            cp.GenerateInMemory = true;

            StringBuilder sb = new StringBuilder("");
            
            sb.Append("using System;\n");
            sb.Append("using System.Collections.Generic;\n");
            sb.Append("using System.Data;\n");            
            sb.Append("using System.Linq;\n");

            sb.Append("using DynamicLambdaTester.Entity;\n");
            sb.Append("namespace DynamicLambdaTester{ \n");
            sb.Append("public class LambdaExtension{ \n");
            sb.Append("public "+ typeRet +" Select("+parameters+"){\n");            
            sb.Append("try {\n");
            sb.Append(body);
            sb.Append("}\n");
            sb.Append("catch (Exception ex ) { ");
            sb.Append("  return new "+ typeRet +"();");
            sb.Append("} \n");
            sb.Append("} \n");
            sb.Append("} \n");
            sb.Append("}\n");

            CompilerResults cr = c.CompileAssemblyFromSource(cp, sb.ToString());
            if (cr.Errors.Count > 0)
            {
                for (int i = 0; i < cr.Errors.Count; i++)
                {
                    Console.WriteLine("ERROR: " + cr.Errors[i].ErrorText );
                }
                return null;
            }

            System.Reflection.Assembly a = cr.CompiledAssembly;
            object o = a.CreateInstance("DynamicLambdaTester.LambdaExtension");

            Type t = o.GetType();
            MethodInfo mi = t.GetMethod("Select");

            List<entity> r = (List<entity>)mi.Invoke(o, new object[] { tDataParams });

            return r;
        }        
    }
}
Non è complesso, ma abbastanza intricato. Nel main creo le entità per cui provare ad eseguire la selezione, e di contro le aggiungo all'entityList in modo poi da effettuare l'estrazione. in body ho la bella possibilità di aggiungere la lambda ( non so perchè mi viene lambada.. ma fa nulla ). DynamicSelect che cosa fa??? Bhe crea a runtime una class library che conterrà il metodo da eseguire, guarda caso, SELECT ( che per il momento lascio così ) anche se secondo me deve cambiare in WHERE ... ma per il momento va bene. L'ultima parte è noia ( ops ) è banalmente reflection con cui andiamo a chiamare il metodo attendendoci un risultato. Che dire.. chi la dura la vince.

Nessun commento: