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.

venerdì 1 giugno 2012

MS: 70-516 Samples of chapter 1 Lesson 2

La seconda lezione inizia con un argomento già ampiamente discusso in questo blog "la serializzazione". Personalmente ritengo che questo processo sia deceisamente utile, tuttavia può essere discutibile passare da un dataset per effettarlo.
static string desktopFileName(string fileName)
        {
            return Path.Combine(
                    Environment.GetFolderPath(
                        Environment.SpecialFolder.Desktop),
                    fileName);
        }

        static void Sample_01()
        {
            DataSet ds = new DataSet();
            DataTable dColleghi = ds.Tables.Add("Colleghi");

            DataColumn dc0 = new DataColumn("ID");
            dc0.DataType = typeof(int);
            dc0.AllowDBNull = false;
            dc0.AutoIncrement = true;
            dc0.AutoIncrementStep = 1;
            dc0.AutoIncrementSeed = 1;
            dColleghi.Columns.Add(dc0);

            DataColumn dc1 = new DataColumn("Name");
            dc1.DataType = typeof(string);
            dc1.MaxLength = 50;
            dc1.AllowDBNull = false;
            dColleghi.Columns.Add(dc1);

            DataColumn dc2 = new DataColumn("Description");
            dc2.DataType = typeof(string);
            dc2.MaxLength = 150;
            dc2.AllowDBNull = true;
            dColleghi.Columns.Add(dc2);

            DataColumn dc3 = new DataColumn("Year");
            dc3.DataType = typeof(int);
            dColleghi.Columns.Add(dc3);

            DataColumn dc4 = new DataColumn("Subclass");
            dc4.DataType = typeof(string);
            dColleghi.Columns.Add(dc4);


            dColleghi.Rows.Add("1", "Arosio", "Sen", "2012", "Dev");
            dColleghi.Rows.Add("2", "Eridani", "Sen", "2013", "Dev");

            /* file normale */
            dColleghi.WriteXml(desktopFileName("dev.xml"));
            
            dColleghi.Columns["Description"].ColumnMapping = MappingType.Attribute;
            dColleghi.Columns["Name"].ColumnMapping = MappingType.Attribute;
            dColleghi.Columns["Year"].ColumnMapping = MappingType.Attribute;
            dColleghi.Columns["Subclass"].ColumnMapping = MappingType.Attribute;

            /* file con attributi */
            dColleghi.WriteXml(desktopFileName("dev.xml"));

            
            dColleghi.Columns["Description"].ColumnMapping = MappingType.Element;
            dColleghi.Columns["Name"].ColumnMapping = MappingType.Element;
            dColleghi.Columns["Year"].ColumnMapping = MappingType.Element;
            dColleghi.Columns["Subclass"].ColumnMapping = MappingType.Hidden;

            /* file con schema annesso */
            dColleghi.WriteXml(desktopFileName("dev.xml"), XmlWriteMode.WriteSchema);

        }
Chiaramente è valido anche il processo contrario ossia.. passando dalla lettura di un file xml ( con schema ) ad un data table. Le opzioni di salvataggio di un XML sono possibili anche a livello di dataset così come per il seguente esempio.
static void Sample_02()
        {
            DataSet ds = new DataSet("Colleghi");

            DataTable dTipoCollega = ds.Tables.Add("TipoCollega");
            dTipoCollega.Columns.Add("IDTipoCollega", typeof(int));
            dTipoCollega.Columns.Add("TipoCollega", typeof(string));
            dTipoCollega.Columns.Add("Descrizione", typeof(string));

            dTipoCollega.PrimaryKey = new DataColumn[] { dTipoCollega.Columns["IDTipoCollega"] };

            DataTable dCollega = ds.Tables.Add("Collega");
            dCollega.Columns.Add("IDCollega", typeof(int));
            dCollega.Columns.Add("IDTipoCollega", typeof(int));
            dCollega.Columns.Add("Cognome", typeof(string));
            dCollega.Columns.Add("Nome", typeof(string));
            dCollega.Columns.Add("Eta", typeof(string));

            dCollega.PrimaryKey = new DataColumn[] { dCollega.Columns["IDCollega"] };

            ds.Relations.Add("rCollega_Tipo", dTipoCollega.Columns["IDTipoCollega"],
                                                dCollega.Columns["IDCollega"]);


            dTipoCollega.Rows.Add("1", "interno", "");
            dTipoCollega.Rows.Add("2", "esterno", "");

            dCollega.Rows.Add("1", "1", "Arosio", "Fabio", "38");
            dCollega.Rows.Add("2", "2", "Eridani", "Claudio", "38");

            /* dataset */
            ds.WriteXml(desktopFileName("dev.xml"));

            /* dataset con schema*/
            ds.WriteXml(desktopFileName("dev.xml"),XmlWriteMode.WriteSchema);


            /* 
             * modificando un dato è possibile tracciare la modifica
             * sfruttando la modalità di scruttira 
             * DiffGram 
             */
            dCollega.Rows[0][3] = "aRoSiO";

            ds.WriteXml(desktopFileName("dev.xml"), XmlWriteMode.DiffGram);
        }
Può sempre avere una logica ( anche se per me è discutibile ) avere una serializzazione binaria.
static void Sample_03()
        {

            DataSet ds = new DataSet("Colleghi");

            DataTable dTipoCollega = ds.Tables.Add("TipoCollega");
            dTipoCollega.Columns.Add("IDTipoCollega", typeof(int));
            dTipoCollega.Columns.Add("TipoCollega", typeof(string));
            dTipoCollega.Columns.Add("Descrizione", typeof(string));

            dTipoCollega.PrimaryKey = new DataColumn[] { dTipoCollega.Columns["IDTipoCollega"] };

            DataTable dCollega = ds.Tables.Add("Collega");
            dCollega.Columns.Add("IDCollega", typeof(int));
            dCollega.Columns.Add("IDTipoCollega", typeof(int));
            dCollega.Columns.Add("Cognome", typeof(string));
            dCollega.Columns.Add("Nome", typeof(string));
            dCollega.Columns.Add("Eta", typeof(string));

            dCollega.PrimaryKey = new DataColumn[] { dCollega.Columns["IDCollega"] };

            ds.Relations.Add("rCollega_Tipo", dTipoCollega.Columns["IDTipoCollega"],
                                                dCollega.Columns["IDCollega"]);


            dTipoCollega.Rows.Add("1", "interno", "");
            dTipoCollega.Rows.Add("2", "esterno", "");

            dCollega.Rows.Add("1", "1", "Arosio", "Fabio", "38");
            dCollega.Rows.Add("2", "2", "Eridani", "Claudio", "38");

            /* con xml in chiaro ! */
            BinaryFormatter fmt = new BinaryFormatter();
            FileStream fs = new FileStream(
                        desktopFileName("dev.bin"),FileMode.Create);
            
            
            fmt.Serialize(fs, ds);
            fs.Close( );

            /* con xml in binario ! */
            ds.RemotingFormat = SerializationFormat.Binary;

            fs = new FileStream(
                        desktopFileName("dev.bin"), FileMode.Create);            
            fmt.Serialize(fs, ds);
            fs.Close();        
        }
Premetto che questo esempio, mi sembra interessante, tuttavia mi preme precisare che alcune attività che il libro presenta sembrano appartenere ad un mondo "retrò"
[Serializable]
    public class Car
    {
        public string Make { get; set; }
        public string Model { get; set; }
        public int Year { get; set; }
    }
Questa classe rappresenta la nostra "futura data table" e potrebbe essere interpretata come lo schema "XML" rappresentato tuttavia tramite una tipizzazione forte. I due successivi metodi sono consento la creazione di un dataTale partendo dalla classe.
static void Sample_05()
        {
            DataTable cars = CreateTableWithUDT();

            DataRow newAuto = cars.NewRow();

            newAuto["Vin"] = "123456789ABCD";
            
            Car c = new Car { Make = "Chevy", Model = "Impala", Year = 2003 };
            newAuto["CarObject"] = c;
            
            cars.Rows.Add(newAuto);
            
            Car theCar = (Car)cars.Rows[0]["CarObject"];
        }

        static  DataTable CreateTableWithUDT()
        {
            
            DataTable cars = new DataTable("Car");
            
            DataColumn vin = new DataColumn("Vin");
            
            vin.DataType = typeof(string);
            vin.MaxLength = 23;
            vin.Unique = true;
            vin.AllowDBNull = false;
            vin.Caption = "VIN";
            cars.Columns.Add(vin);
            
            //UDT column
            DataColumn carColumn = new DataColumn("CarObject", typeof(Car));
            cars.Columns.Add(carColumn);
            //Set the Primary Key
            cars.PrimaryKey = new DataColumn[] { vin };

            return cars;
        }
Nello specifico è creata per prima la colonna indice e sono quindi aggiunte le colonne. Alla fine del capitolo sono presenti alcune domande che riporto: If you want to assign a Car object to a column called CarObject, which attribute must be on the Car class to enable saving the data table to a file? A. Bindable B. DataObject C. Serializable D. FileIOPermission L'esempio in precednza indica che la classe è Serializable quindi la risposta corretta è la C. You are storing custom Car objects in a data table whose DataView property displays a filtered list of Car objects, filtered on Make Of The Cars. If you change the make of one of the Car objects, you want to ensure that the filter will dynamically update. What must you do to be assured that the filter will detect the change made to your custom Car object? A. Create a new Car object and assign it to the column value. B. Nothing, because this scenario works by default. Non bisogna fare nulla perchè la dataview sente la modifica, quindi la risposta giusta è la B. You are storing custom Car objects in a data table that will be serialized to a file. After serializing to a binary file called cars.bin, you open the file with a binary editor and notice that XML is in the file. Which setting can you use to ensure that you will create a binary file without embedded XML? A. Set the BatchUpdate setting to SerializationFormat.Binary. B. Set the RemotingFormat property to SerializationFormat.Binary. C. Set the DefaultView property to SerializationFormat.Binary. D. Set the Constraints property to SerializationFormat.Binary. Per l'esempio eseguito in precedenza abbiamo utilizzato RemotingFormat quindi la risposta giusta è la B. When working with a data set that has data in it, you decide you want to store the schema, but not the data, of the data set to a file so you can share the schema with other users. Which method must you execute to store the schema? A. InferXmlSchema B. ReadXmlSchema C. WriteXmlSchema D. WriteXml Non abbiamo eseguito esempio ma basta esclusivamente WriteXmlSchema(string fileName) indicando il nome del file da salvare.