Visualizzazione post con etichetta Linq. Mostra tutti i post
Visualizzazione post con etichetta Linq. Mostra tutti i post

lunedì 14 ottobre 2013

How To: Lamba on String

E' bello fare certe scoperte... ( SOLO DOPO ) !

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

namespace LambdaString
{
    class Program
    {
        static void Main(string[] args)
        {
            string Test1 = "questo è uno splendido testo";
            var cTest1 = Test1.ToCharArray().Where(c => c == 'n');
            Console.WriteLine(cTest1.Count());

           
        }
    }
}
E ammetto che ci ho pure provato gusto !
Quello che mi fa sorridere e il non averci mai pensato eppure è li bello ovvio come il sole !

Le stringhe sono array e come tali sono riconducibili ad un Querable Object.. e via .. 

mercoledì 9 ottobre 2013

How To: Linq IN clause syntax

Ed eccomi qui con la scoperta della settimana.
Mi sono arrovellato, senza troppa congizione di causa su come si potesse implementare una IN con Linq, ammetto di aver avuto l'aiuto dal pubblico, ma la soluzione era "facile, Facile" del resto Microsoft ci ha ben abituato a lavorare al contrario no ?

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

namespace Linq.InWorkaround
{
    class Program
    {
        static void Main(string[] args)
        {
            // Esempio di IN
            List<string> ents = new List<string>();
            ents.Add("Fabio Arosio");
            ents.Add("Lorenzo Arosio");
            ents.Add("Federico Arosio");
            ents.Add("Claudio Eridani");

            // creo quanto ricercare .. 
            List<string> pattern = new List<string>();
            pattern.Add("Fabio Arosio");
            pattern.Add("Claudio Eridani");


            Console.WriteLine("Esempio di IN");
            // basta utilizzare la contains al contrario
            var r = ents.Where(e => pattern.Contains(e));

            foreach (string s in r)
            {
                Console.WriteLine(s);
            }
            Console.ReadKey();

            Console.WriteLine("\n\rEsempio di Like");
            // Questa è la Like
            var r1 = ents.Where(e => e.Contains("Arosio"));

            foreach (string s in r1)
            {
                Console.WriteLine(s);
            }

            Console.ReadKey();
        }
    }
}

Bene non è difficile il tutto si basa sul "chi contiene cosa" generalmente quando in SQL utilizziamo la IN è per identificare una serie di valori per il cui il nostro campo può variare, con LINQ la situazione è inversa dato che la sintassi vera e propria non esiste. Quindi creiamo un set, e chiediamo se il campo è contenuto in quel range.

mercoledì 25 settembre 2013

How To: Sum of nullable int

Ma come si comporta linq quando somma un campo che può contenere null ?

Già mi sono posto questa domanda perchè non mi è mai capitato di beccare un errore simile, posto che il compilatore blocchi int i = null mi rimane solo qualche piccola pillola per ovviare a eventuali problemi provenienti da codice scritto male.

Ma linq mi ha data prova di essere abbastanza solido... quanto meno in questo semplice esempio.


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

namespace TestSumLinq
{
    class Program
    {
        public class entity
        {
            public int fstValue { get; set; }
            public int? sndValue { get; set; } 
        }

        static void Main(string[] args)
        {
            List<entity> l = new List<entity>();

            l.Add(new entity() { fstValue = 0, sndValue= null});
            l.Add(new entity() { fstValue = 1, sndValue = 2 });
            l.Add(new entity() { fstValue = 2, sndValue = 3 });
            l.Add(new entity() { fstValue = 3, sndValue = 4 });

            // facendo le somme ottengo ?? 

            var r1 = l.Sum(s => s.fstValue);

            var r2 = l.Sum(s => s.sndValue);

            Console.WriteLine("somma campo int : " + r1.ToString());

            Console.WriteLine("somma campo int?: " + r2.ToString());

            Console.ReadKey();
        }
    }
} 


E in bona sostanza rimango piacevolmente colpito .. perchè funziona senza rompersi.

lunedì 10 giugno 2013

How To: Linq Sort / OrderBy

Facendo qualche esperimento mi sono reso conto che l'order by di Linq è meno scomodo di quanto si possa pensare.

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

namespace TestSort
{
    class Program
    {
        static void Main(string[] args)
        {
            entities es = new entities();
            es.Add(new entity() { id = 1, 
                                    name = "fabio", 
                                    description = "nice" });

            es.Add(new entity() { id = 2, 
                                    name = "lorenzo", 
                                    description = "very nice" });

            es.Add(new entity() { id = 2, 
                                    name = "federico", 
                                    description = "it will be" });


            var esx = es.OrderBy(c=> c.name);
            
        }
    }


    public class entity
    {
        public int id { get; set; }
        public string name { get; set; }
        public string description { get; set; }
    }

    public class entities : List<entity>
    { 
    
    }
}

Facile.. no ?

venerdì 31 maggio 2013

How to: C# Lazy ( Side effect )

Recentemente mi sono confrontato con un cordiale discorso di performance.

La maggior parte delle nostre applicazione vive l'ansia di dover caricare dei dati, e a dispetto delle mille possibilità  (leggasi "entity framework" ) non sempre possiamo rivolgerci ad estrattori specifici, o di contro questi sono rappresentati da elementi che wrappano i vare ef.


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

namespace LazyTester
{
    public class entity
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }    
    }

    public class entites : List<entity>
    {
        public entites()
        {
            this.Add( new entity(){
                    ID=1,
                    Name = "Fabio",
                    Surname ="Arosio"});

            this.Add( new entity(){
                    ID=2,
                    Name = "Claudio",
                    Surname ="Arosio"});
            
            this.Add( new entity(){
                    ID=3,
                    Name = "Lorenzo",
                    Surname ="Arosio"});
            
            this.Add( new entity(){
                    ID=4,
                    Name = "Federico",
                    Surname ="Arosio"});
            
            Console.WriteLine("done");
        }    
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Lazy...");
            Lazy<entites> ldsE = new Lazy<entites>();
                       

            Console.WriteLine("Normal...");
            entites dsE = new entites();
          
            Console.WriteLine("Normal:"+ dsE.Count.ToString());

            Console.WriteLine("Lazy:" + ldsE.Value.Count.ToString());

            Console.ReadLine();
        }
    }
}


Questo esempietto anche se molto molto scarno presenta il vantaggio di un costruttore Lazy.

già nel nome ben si comprende che cosa farà ma in breve possiamo ipotizzare che quanto costruito come lazy non sarà disponibile fin da subito.
Tanto che la "done" sulla console appare appena prima di della WriteLine Lazy.

Supponiamo giusto per dare visibiltà della situazione che il caricamento differito dei dati avrà luogo solo quando questi saranno necessari e di contro questo "sposta il tempo d'accesso" solo e soltanto a quando questi servono davvero.


var ldsEE = new Lazy<entites>().Value.Where (c=> c.Name.StartsWith("F"));


Ci è lecito pensare e implementare anche in questo modo .. ma perdendone qualche beneficio..

Sicuramente credo che sia sempre cosa buona e giusta sperimentare. !

giovedì 21 febbraio 2013

Quando un distinct non basta. Linq workaround


Ci troviamo spesso nella situazione di dover effettuare dei distinct di oggetti di una collection di cui ci interessa che il distinct effettuato riguardi un sottogruppo di campi. Capita anche, lavorando con iQuerable provenienti da un orm interfacciato con un database quali EntityFramework o Linq-To-Sql classes, che le entità in oggetto in realtà comprendano dei campi scritti da trigger database che non ci interessano particolarmente. Oltre all'iterazione dell'intera collection o dell'intero iQuerable (che tra l'altro causa la materializzazione in ram di ogni singola entità iterata con conseguente aumento del costo computazionale) una soluzione relativamente standard consiste nel creare un'implementazione di IEqualityComparer per la classe specifica delle entità in considerazione.


How To: Lambda Distinct.

Ammetto che la soluzione non è assolutamente mia, ma di un nuovo editor che a breve potrà partecipare al Blog.. ben venuto Vittorio.

Il dilemma del giorno è come ottenere l'effetto del distinct SQL tramite lambda...


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

namespace IQuerableDistinct
{
    public class friend
    {
        public string Surname { get; set; }
        public string Name { get; set; }
        public string Nick { get; set; }
        public string Socialpage { get; set; }
    }

    public class friends : List<friend> { };

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

            friend f = new friend() 
                { Surname = "Arosio", 
                  Name = "Fabio", 
                  Nick = "thema!", 
                  Socialpage="" };
            
            friend f1 = new friend() 
                { Surname = "Arosio", 
                  Name = "Fabio", 
                  Nick = "nudrubufu", 
                  Socialpage = "nudrubufu.blogspot.com" };

            friend f2 = new friend() 
                { Surname = "Arosio", 
                  Name = "Fabio", 
                  Nick = "xa", 
                  Socialpage = "deviantArt.com/Thema" };

            friend f3 = new friend() 
                { Surname = "Arosio", 
                  Name = "Fabio", 
                  Nick = "TheMa!", 
                  Socialpage = "flikr.com/213445" };

            friend f4 = new friend() 
                { Surname = "Arosio", 
                  Name = "Fabio", 
                  Nick = "TheMa!", 
                  Socialpage = "500px/FabioArosio" };


            friends fx = new friends();
            fx.Add(f);
            fx.Add(f1);
            fx.Add(f2);
            fx.Add(f3);
            fx.Add(f4);

            foreach (friend x in fx) 
            { 
                Console.WriteLine(x.Surname + 
                        "\t" + x.Name + 
                        "\t" + x.Nick); 
            }

            Console.WriteLine("\r\nDistinct by Surname+Name...");

            var d = fx.GroupBy(c => c.Surname + c.Name).Select(g => g.First());


            foreach (friend x in d)
            {
                Console.WriteLine(x.Surname +
                        "\t" + x.Name );
            }

            Console.ReadLine();
        }
    }
}

Sembra facile ma non è difficile come al solito.

Creaiamo 4 entità che per comodo e per gestire il nostro esempio presentano valori simili... ma differenti per attributi..

La distinct ci permette di comprendere quanti amici abbiamo con lo stesso nome..

sabato 25 agosto 2012

How To Skip Linq Exception

 

Implement static IEnumerable<T> function to skip Exception when run a query.

This function return valid items only.

  1. public static class LINQException {
  2.     public static void TryExample() {
  3.         var LsWithEx = from p in Enumerable.Range(0, 10) select int.Parse("dsfksdj");
  4.         var LsSkipEx = (from p in Enumerable.Range(0, 10) select int.Parse("dsfksdj")).SkipExceptions();
  5.     }
  6.  
  7.     public static IEnumerable<T> SkipExceptions<T>(this IEnumerable<T> values)
  8.     {
  9.         using (var enumerator = values.GetEnumerator())
  10.         {
  11.             bool next = true;
  12.             while (next)
  13.             {
  14.                 try
  15.                 {
  16.                     next = enumerator.MoveNext();
  17.                 }
  18.                 catch
  19.                 {
  20.                     continue;
  21.                 }
  22.  
  23.                 if (next) yield return enumerator.Current;
  24.             }
  25.         }
  26.     }
  27.     
  28.     }

 

I hope this help Smile

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ì 13 aprile 2012

How To Get Date & Time from NTP SERVER

 

Se mai vi dovesse servire Sorriso

La chiamata

public class Program
 {
      public static void Main(string[] args)
     {
         var t =NTP.GetNTPTime();
     }
 }

 

La Funzione

public class NTP
   {
       public static DateTime GetNTPTime()
       {
           try
           {
               // 0x1B == 0b11011 == NTP version 3, client - vedi RFC 2030
               byte[] ntpPacket = new byte[] { 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };


               var ntp = (from p in (Dns.GetHostEntry("pool.ntp.org").AddressList).Take(1)
                          select new { Client = new UdpClient(), EP = new IPEndPoint(p, 123) }
                        ).ToArray().FirstOrDefault();

               ntp.Client.Connect(ntp.EP);
               ntp.Client.Send(ntpPacket, ntpPacket.Length);

               var TmpEP = ntp.EP;
               byte[] data = ntp.Client.Receive(ref TmpEP);

               // dal 32 byte inizia la data
               byte[] ConvertedSeconds = new byte[4];
               ConvertedSeconds[0] = (byte)(data[32 + 3] & (byte)0x7F); // disabilita l'MSB (sui server che lo usano)
               ConvertedSeconds[1] = data[32 + 2];
               ConvertedSeconds[2] = data[32 + 1];
               ConvertedSeconds[3] = data[32 + 0];

               uint seconds = BitConverter.ToUInt32(ConvertedSeconds, 0);

               return (new DateTime(1900, 1, 1)).AddSeconds(seconds);
           }
           catch (Exception ex )
           {
               Console.WriteLine(ex.Message);
               return DateTime.MinValue;
           }
   
       }
   }

sabato 24 marzo 2012

IEnumerable AsDataReader & SqlBulkCopy

 

Volete trasferire il contenuto della vostra collezione Enumerabile su una tabella SQL o caricare un DataTable a partire da essa?

Dopo alcune ricerche mi sono soffermato sul seguente articolo :

//===============================================================================
// Copyright  2008 Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
//
//This code lives here: http://code.msdn.microsoft.com/LinqEntityDataReader
//Please visit for comments, issues and updates
//
//Version 1.0.0.1 Added GetSchemaTable support for Loading DataTables from EntityDataReader
//Version 1.0.0.2 Added support for Entity Framework types, including Foreign Key columns
//Version 1.0.0.3 In DataReader.GetValue, now using dynamic methods for common scalar types instead of reflection with PropertyInfo.GetValue()
//Version 1.0.0.4 Added support for simple IEnumerable<T> where T is a scalar to support, eg, passing List<int> to a TVP
//Version 1.0.0.5 Simplified the Attribute code, added dynamic method support for all scalar types
//Version 1.0.0.6 Replaced the switch block for ValueAccessor with a Lambda Expression
//Version 1.0.0.6 Fixed a bug with nullable foreign keys on EF entities
//Version 1.0.0.6 Extensive refactoring, introduced EntityDataReaderOptions, changed constructors
//Version 1.0.0.6 Introduced option to flatten related entities.  Now you can have the EntityDataReader flatten an object graph.
//                This is especially useful for enabling you to project all the scalars of a related enty by just projecting the entity.
//                eg. a projection like new { salesOrder.ID, salesOrder.Customer, salesOrder.Product }                 
//                will create a DbDataReader with Id, Customer.*, Product.*
//Version 1.0.0.7 For anonymous types the order of columns is now controlled by the anonymous type's constructor arguments, for which
//                Reflection tracks the ordinal position.  This ordering is applied to any type where the constructor args match the properties
//                1-1 on name and type.  Reflection over properties does not preserve the declaration order of the properties, and for Table Valued Parameters
//                SqlClient maps the DbDataReader columns to the TVP columns by ordinal position, not by name.  This relies on the behavior of
//                the C# compiler that Anonymous types have a constructor with constructor arguments that match the object initializer on both name
//                and position.



namespace Microsoft.Samples.EntityDataReader
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Data;
  using System.Reflection;
  using System.Data.Common;
  using System.Data.Objects.DataClasses;
  using System.Data.Metadata.Edm;
  using System.Data.Objects;
  using System.Linq.Expressions;


  /// <summary>
  /// The EntityDataReader wraps a collection of CLR objects in a DbDataReader.  
  /// Only "scalar" properties are projected, with the exception that Entity Framework
  /// EntityObjects that have references to other EntityObjects will have key values for
  /// the related entity projected.
  ///
  /// This is useful for doing high-speed data loads with SqlBulkCopy, and copying collections
  /// of entities ot a DataTable for use with SQL Server Table-Valued parameters, or for interop
  /// with older ADO.NET applciations.
  ///
  /// For explicit control over the fields projected by the DataReader, just wrap your collection
  /// of entities in a anonymous type projection before wrapping it in an EntityDataReader.
  ///
  /// Instead of
  /// IEnumerable<Order> orders;
  /// ...
  /// IDataReader dr = orders.AsDataReader();
  ///
  /// do
  /// IEnumerable<Order> orders;
  /// ...
  /// var q = from o in orders
  ///         select new
  ///         {
  ///            ID=o.ID,
  ///            ShipDate=o.ShipDate,
  ///            ProductName=o.Product.Name,
  ///            ...
  ///         }
  /// IDataReader dr = q.AsDataReader();
  ///
  /// </summary>
  /// <typeparam name="T"></typeparam>
  public sealed class EntityDataReader<T> : DbDataReader, IDataReader
  {

    readonly IEnumerator<T> enumerator;
    readonly EntityDataReaderOptions options;

    T current;
    bool closed = false;

    static List<Attribute> scalarAttributes;
    static List<Attribute> scalarAttributesPlusRelatedObjectScalarAttributes;
    static List<Attribute> scalarAttributesPlusRelatedObjectKeyAttributes;

    readonly List<Attribute> attributes;

    #region Attribute inner type
    private class Attribute
    {
      //PropertyInfo propertyInfo;
      public readonly Type Type;
      public readonly string FullName;
      public readonly string Name;
      public readonly bool IsRelatedAttribute;

      readonly Func<T, object> ValueAccessor;

      /// <summary>
      /// Uses Lamda expressions to create a Func<T,object> that invokes the given property getter.
      /// The property value will be extracted and cast to type TProperty
      /// </summary>
      /// <typeparam name="TObject">The type of the object declaring the property.</typeparam>
      /// <typeparam name="TProperty">The type to cast the property value to</typeparam>
      /// <param name="pi">PropertyInfo pointing to the property to wrap</param>
      /// <returns></returns>
      public static Func<TObject, TProperty> MakePropertyAccessor<TObject,TProperty>(PropertyInfo pi)
      {
        ParameterExpression objParam = Expression.Parameter(typeof(TObject), "obj");
        MemberExpression typedAccessor = Expression.PropertyOrField(objParam, pi.Name);
        UnaryExpression castToObject = Expression.Convert(typedAccessor, typeof(object));
        LambdaExpression lambdaExpr = Expression.Lambda<Func<TObject, TProperty>>(castToObject, objParam);

        return (Func<TObject, TProperty>)lambdaExpr.Compile();
      }


      public static Func<TObject, TProperty> MakeRelatedPropertyAccessor<TObject, TProperty>(PropertyInfo pi, PropertyInfo pi2)
      {

        Func<TObject, object> getRelatedObject;
        {
          // expression like:
          //    return (object)t.SomeProp;
          ParameterExpression typedParam = Expression.Parameter(typeof(T), "t");
          MemberExpression typedAccessor = Expression.PropertyOrField(typedParam, pi.Name);
          UnaryExpression castToObject = Expression.Convert(typedAccessor, typeof(object));
          LambdaExpression lambdaExpr = Expression.Lambda<Func<TObject, object>>(castToObject, typedParam);
          getRelatedObject = (Func<TObject, object>)lambdaExpr.Compile();
        }


        Func<object, TProperty> getRelatedObjectProperty;
        {
                 
          // expression like:
          //    return (object)((PropType)o).RelatedProperty;
          ParameterExpression objParam = Expression.Parameter(typeof(object), "o");
          UnaryExpression typedParam = Expression.Convert(objParam,pi.PropertyType);
          MemberExpression typedAccessor = Expression.PropertyOrField(typedParam, pi2.Name);
          UnaryExpression castToObject = Expression.Convert(typedAccessor, typeof(TProperty));
          LambdaExpression lambdaExpr = Expression.Lambda<Func<object, TProperty>>(castToObject, objParam);
          getRelatedObjectProperty = (Func<object, TProperty>)lambdaExpr.Compile();
        }

        Func<TObject, TProperty> f = (TObject t) =>
        {
          object o = getRelatedObject(t);
          if (o == null) return default(TProperty);
          return getRelatedObjectProperty(o);
        };

        return f;
      }

      public Attribute(PropertyInfo pi)
      {
        this.FullName = pi.DeclaringType.Name + "_" + pi.Name;
        this.Name = pi.Name;
        Type = pi.PropertyType;
        IsRelatedAttribute = false;

        ValueAccessor = MakePropertyAccessor<T,object>(pi);
      }

      public Attribute(string fullName, string name, Type type, Func<T, object> getValue, bool isRelatedAttribute)
      {
        this.FullName = fullName;
        this.Name = name;
        this.Type = type;
        this.ValueAccessor = getValue;
        this.IsRelatedAttribute = isRelatedAttribute;
      }
      
      public object GetValue(T target)
      {
        return ValueAccessor(target);
      }
    }
    #endregion

    #region "Scalar Types"

    static bool IsScalarType(Type t)
    {
      return scalarTypes.Contains(t);
    }
    static readonly HashSet<Type> scalarTypes = LoadScalarTypes();
    static HashSet<Type> LoadScalarTypes()
    {
      HashSet<Type> set = new HashSet<Type>()
                              {
                                //reference types
                                typeof(String),
                                typeof(Byte[]),
                                //value types
                                typeof(Byte),
                                typeof(Int16),
                                typeof(Int32),
                                typeof(Int64),
                                typeof(Single),
                                typeof(Double),
                                typeof(Decimal),
                                typeof(DateTime),
                                typeof(Guid),
                                typeof(Boolean),
                                typeof(TimeSpan),
                                //nullable value types
                                typeof(Byte?),
                                typeof(Int16?),
                                typeof(Int32?),
                                typeof(Int64?),
                                typeof(Single?),
                                typeof(Double?),
                                typeof(Decimal?),
                                typeof(DateTime?),
                                typeof(Guid?),
                                typeof(Boolean?),
                                typeof(TimeSpan?)
                              };
      

      return set;

    }
    #endregion

    #region Constructors

    public EntityDataReader(IEnumerable<T> col)
      : this(col,EntityDataReaderOptions.Default,null) { }

    public EntityDataReader(IEnumerable<T> col, EntityDataReaderOptions options)
      : this(col, options, null) { }

    public EntityDataReader(IEnumerable<T> col, EntityDataReaderOptions options, ObjectContext objectContext)
    {
      this.enumerator = col.GetEnumerator();
      this.options = options;

      if (options.RecreateForeignKeysForEntityFrameworkEntities && objectContext == null)
      {
        throw new ArgumentException("If RecreateForeignKeysForEntityFrameworkEntities=true then objectContext is required");
      }

      //done without a lock, so we risk running twice
      if (scalarAttributes == null)
      {
         scalarAttributes = DiscoverScalarAttributes(typeof(T));
      }
      if (options.FlattenRelatedObjects && scalarAttributesPlusRelatedObjectScalarAttributes == null)
      {
        var atts = DiscoverRelatedObjectScalarAttributes(typeof(T));
        scalarAttributesPlusRelatedObjectScalarAttributes = atts.Concat(scalarAttributes).ToList();
      }
      if (options.RecreateForeignKeysForEntityFrameworkEntities && scalarAttributesPlusRelatedObjectKeyAttributes == null)
      {
        var atts = DiscoverRelatedObjectKeyAttributes(typeof(T), objectContext);
        scalarAttributesPlusRelatedObjectKeyAttributes = atts.Concat(scalarAttributes).ToList();
      }

      if (options.FlattenRelatedObjects)
      {
        attributes = scalarAttributesPlusRelatedObjectScalarAttributes;
      }
      else if (objectContext != null)
      {
        attributes = scalarAttributesPlusRelatedObjectKeyAttributes;
      }
      else
      {
        attributes = scalarAttributes;
      }


    }


    static List<Attribute> DiscoverScalarAttributes(Type thisType)
    {     

      //Not a collection of entities, just an IEnumerable<String> or other scalar type.
      //So add just a single Attribute that returns the object itself
      if (IsScalarType(thisType))
      {
        return new List<Attribute> { new Attribute("Value","Value", thisType, t => t,false) };
      }


      //find all the scalar properties
      var allProperties = (from p in thisType.GetProperties()
                           where IsScalarType(p.PropertyType)
                           select p).ToList();

      //Look for a constructor with arguments that match the properties on name and type
      //(name modulo case, which varies between constructor args and properties in coding convention)
      //If such an "ordering constructor" exists, return the properties ordered by the corresponding
      //constructor args ordinal position.  
      //An important instance of an ordering constructor, is that C# anonymous types all have one.  So
      //this enables a simple convention to specify the order of columns projected by the EntityDataReader
      //by simply building the EntityDataReader from an anonymous type projection.
      //If such a constructor is found, replace allProperties with a collection of properties sorted by constructor order.
      foreach (var completeConstructor in from ci in thisType.GetConstructors()
                                         where ci.GetParameters().Count() == allProperties.Count()
                                         select ci)
      {
        var q = (from cp in completeConstructor.GetParameters()
                 join p in allProperties
                   on new { n = cp.Name.ToLower(), t = cp.ParameterType } equals new { n = p.Name.ToLower(), t = p.PropertyType }
                 select new { cp, p }).ToList();

        if (q.Count() == allProperties.Count()) //all constructor parameters matched by name and type to properties
        {
          //sort all properties by constructor ordinal position
          allProperties = ( from o in q
                            orderby o.cp.Position
                            select o.p ).ToList();
          break; //stop looking for an ordering consturctor
        }
                  
        
      }

      return allProperties.Select(p => new Attribute(p)).ToList();

    }
    static List<Attribute> DiscoverRelatedObjectKeyAttributes(Type thisType, ObjectContext objectContext)
    {

      var attributeList = new SortedList<string,Attribute>();

      
      //recreate foreign key column values
      //by adding Attributes for any key values of referenced entities
      //that aren't already exposed as scalar properties
      var mw = objectContext.MetadataWorkspace;
      var entityTypesByName = mw.GetItems<EntityType>(DataSpace.OSpace).ToLookup(e => e.FullName);

      //find the EntityType metadata for T
      EntityType thisEntity = entityTypesByName[thisType.FullName].First();
      var thisEntityKeys = thisEntity.KeyMembers.ToDictionary(k => k.Name);

      //TODOx use the NavigationProperties instead of the ENtityRelations -- too complicated
      //TODO fix the attribute naming.  Probably requires marking each attribtue as direct or related.
      

      var erProps = thisType.GetProperties()
                            .Where(p => typeof(EntityReference)
                            .IsAssignableFrom(p.PropertyType)).ToList();


      //For each EntityRelation property add add the keys of the related Entity
      foreach (var pi in erProps)
      {
        //Find the name of the CLR Type at the other end of the reference because we need to get its key attributes.
        //the property type is EntityReference<T>, we need T.
        string relatedEntityCLRTypeName = pi.PropertyType.GetGenericArguments().First().FullName;

        //Find the EntityType at the other end of the relationship because we need to get its key attributes.
        EntityType relatedEntityEFType = entityTypesByName[relatedEntityCLRTypeName].FirstOrDefault();
        if (relatedEntityEFType == null)
        {
          throw new InvalidOperationException("Cannot find EntityType for EntityReference Property " + pi.Name);
        }

        //Add attributes for each key value of the related entity.  These are the properties that
        //would probably appear in the storage object.  The names will be the same as they are on the
        //related entity, except prefixed with the related entity name,
        //and with a check to make sure that we're not introducing a duplicate.
        // so if you have
        //  if OrderItem.OrderID -> Order.ID   then the column will be Order_ID
        //  if OrderItem.OrderID -> Order.OrderID   then the column will be Order_OrderID
        foreach (var key in relatedEntityEFType.KeyMembers)
        {
          string targetKeyAttributeName = key.Name;

          //TODO it would be better to get the NavigationProperty and find the ToEndMember name
          //but the NavigationProperty doesn't have good way to get the EntityReference
          //or the related entity key.
          string referenceName;
          if (pi.Name.EndsWith("Reference",StringComparison.Ordinal))
          {
            referenceName = pi.Name.Substring(0, pi.Name.Length - "Reference".Length);
          }
          else  //there's no rule that the EntityReference named like this so, if not just use the target type
          {
            referenceName = pi.PropertyType.Name;

            //if there are multiple relations to the same target type, just uniqify them with an index
            int ix = erProps.Where(p => p.PropertyType == pi.PropertyType).ToList().IndexOf(pi);
            if (ix > 0)
            {
              referenceName = referenceName + ix.ToString(System.Globalization.CultureInfo.InvariantCulture);
            }
            
          }
          string fullName = referenceName + "_" + key.Name;
          

          //bind out local variables for the valueAccessor closure.
          Type kType = Type.GetType(key.TypeUsage.EdmType.FullName);
          PropertyInfo entityReferenceProperty = pi;

          Func<T, object> valueAccessor = o =>
          {
            EntityReference er = (EntityReference)entityReferenceProperty.GetValue(o, null);

            //for nullable foregn keys, just return null
            if (er.EntityKey == null)
            {
              return null;
            }
            object val = er.EntityKey.EntityKeyValues.First(k => k.Key == targetKeyAttributeName).Value;
            return val;
          };
          string name = key.Name;
          
          attributeList.Add(name, new Attribute(fullName, name, kType, valueAccessor, true));
        }


      }

      return attributeList.Values.ToList();

    }
    static List<Attribute> DiscoverRelatedObjectScalarAttributes(Type thisType)
    {
      
      var atts = new List<Attribute>();

      //get the related objects which aren't scalars, not EntityReference objects and not collections
      var relatedObjectProperties =
                        (from p in thisType.GetProperties()
                         where !IsScalarType(p.PropertyType)
                            && !typeof(System.Collections.IEnumerable).IsAssignableFrom(p.PropertyType)
                            && !typeof(EntityReference).IsAssignableFrom(p.PropertyType)
                            && !typeof(EntityKey).IsAssignableFrom(p.PropertyType)
                         select p).ToList();

      foreach (var rop in relatedObjectProperties)
      {
       var type = rop.PropertyType;
        //get the scalar properties for the related type
       var scalars = type.GetProperties().Where(p => IsScalarType(p.PropertyType)).ToList();

       foreach (var sp in scalars)
       {
         string attName = rop.Name + "_" + sp.Name;
         //create a value accessor which takes an instance of T, and returns the related object scalar
         var valueAccessor = Attribute.MakeRelatedPropertyAccessor<T,object>(rop, sp);
         string name = attName;
         Attribute att = new Attribute(rop.Name, attName,sp.PropertyType,valueAccessor,true);
         atts.Add(att);
       }

      }
      return atts;

    }



    #endregion

    #region Utility Methods
    static Type nullable_T = typeof(System.Nullable<int>).GetGenericTypeDefinition();
    static bool IsNullable(Type t)
    {
      return (t.IsGenericType
          && t.GetGenericTypeDefinition() == nullable_T);
    }
    static Type StripNullableType(Type t)
    {
      return t.GetGenericArguments()[0];
    }
    #endregion
    
    #region GetSchemaTable


    const string shemaTableSchema = @"<?xml version=""1.0"" standalone=""yes""?>
<xs:schema id=""NewDataSet"" xmlns="""" xmlns:xs=""http://www.w3.org/2001/XMLSchema"" xmlns:msdata=""urn:schemas-microsoft-com:xml-msdata"">
  <xs:element name=""NewDataSet"" msdata:IsDataSet=""true"" msdata:MainDataTable=""SchemaTable"" msdata:Locale="""">
    <xs:complexType>
      <xs:choice minOccurs=""0"" maxOccurs=""unbounded"">
        <xs:element name=""SchemaTable"" msdata:Locale="""" msdata:MinimumCapacity=""1"">
          <xs:complexType>
            <xs:sequence>
              <xs:element name=""ColumnName"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""ColumnOrdinal"" msdata:ReadOnly=""true"" type=""xs:int"" default=""0"" minOccurs=""0"" />
              <xs:element name=""ColumnSize"" msdata:ReadOnly=""true"" type=""xs:int"" minOccurs=""0"" />
              <xs:element name=""NumericPrecision"" msdata:ReadOnly=""true"" type=""xs:short"" minOccurs=""0"" />
              <xs:element name=""NumericScale"" msdata:ReadOnly=""true"" type=""xs:short"" minOccurs=""0"" />
              <xs:element name=""IsUnique"" msdata:ReadOnly=""true"" type=""xs:boolean"" minOccurs=""0"" />
              <xs:element name=""IsKey"" msdata:ReadOnly=""true"" type=""xs:boolean"" minOccurs=""0"" />
              <xs:element name=""BaseServerName"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""BaseCatalogName"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""BaseColumnName"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""BaseSchemaName"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""BaseTableName"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""DataType"" msdata:DataType=""System.Type, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""AllowDBNull"" msdata:ReadOnly=""true"" type=""xs:boolean"" minOccurs=""0"" />
              <xs:element name=""ProviderType"" msdata:ReadOnly=""true"" type=""xs:int"" minOccurs=""0"" />
              <xs:element name=""IsAliased"" msdata:ReadOnly=""true"" type=""xs:boolean"" minOccurs=""0"" />
              <xs:element name=""IsExpression"" msdata:ReadOnly=""true"" type=""xs:boolean"" minOccurs=""0"" />
              <xs:element name=""IsIdentity"" msdata:ReadOnly=""true"" type=""xs:boolean"" minOccurs=""0"" />
              <xs:element name=""IsAutoIncrement"" msdata:ReadOnly=""true"" type=""xs:boolean"" minOccurs=""0"" />
              <xs:element name=""IsRowVersion"" msdata:ReadOnly=""true"" type=""xs:boolean"" minOccurs=""0"" />
              <xs:element name=""IsHidden"" msdata:ReadOnly=""true"" type=""xs:boolean"" minOccurs=""0"" />
              <xs:element name=""IsLong"" msdata:ReadOnly=""true"" type=""xs:boolean"" default=""false"" minOccurs=""0"" />
              <xs:element name=""IsReadOnly"" msdata:ReadOnly=""true"" type=""xs:boolean"" minOccurs=""0"" />
              <xs:element name=""ProviderSpecificDataType"" msdata:DataType=""System.Type, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""DataTypeName"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""XmlSchemaCollectionDatabase"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""XmlSchemaCollectionOwningSchema"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""XmlSchemaCollectionName"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""UdtAssemblyQualifiedName"" msdata:ReadOnly=""true"" type=""xs:string"" minOccurs=""0"" />
              <xs:element name=""NonVersionedProviderType"" msdata:ReadOnly=""true"" type=""xs:int"" minOccurs=""0"" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:schema>";
    public override DataTable GetSchemaTable()
    {
      DataSet s = new DataSet();
      s.Locale = System.Globalization.CultureInfo.CurrentCulture;
      s.ReadXmlSchema(new System.IO.StringReader(shemaTableSchema));
      DataTable t = s.Tables[0];
      for (int i = 0; i < this.FieldCount; i++)
      {
        DataRow row = t.NewRow();
        row["ColumnName"] = this.GetName(i);
        row["ColumnOrdinal"] = i;

        Type type = this.GetFieldType(i);
        if (type.IsGenericType
          && type.GetGenericTypeDefinition() == typeof(System.Nullable<int>).GetGenericTypeDefinition())
        {
          type = type.GetGenericArguments()[0];
        }
        row["DataType"] = this.GetFieldType(i);
        row["DataTypeName"] = this.GetDataTypeName(i);
        row["ColumnSize"] = -1;
        t.Rows.Add(row);
      }
      return t;

    }
    #endregion

    #region IDataReader Members

    public override void Close()
    {
      closed = true;
    }

    public override int Depth
    {
      get { return 1; }
    }


    public override bool IsClosed
    {
      get { return closed; }
    }

    public override bool NextResult()
    {
      return false;
    }

    int entitiesRead = 0;
    public override bool Read()
    {
       bool rv =  enumerator.MoveNext();
       if (rv)
       {
         current = enumerator.Current;
         entitiesRead += 1;
       }
       return rv;
    }

    public override int RecordsAffected
    {
      get { return -1; }
    }

    #endregion

    #region IDisposable Members

    protected override void Dispose(bool disposing)
    {
      Close();
      base.Dispose(disposing);
    }

    #endregion

    #region IDataRecord Members

    public override int FieldCount
    {
      get
      {
        return attributes.Count;
      }
    }

    TField GetValue<TField>(int i)
    {
      TField val = (TField)attributes[i].GetValue(current);
      return val;
    }
    public override bool GetBoolean(int i)
    {
      return GetValue<bool>(i);
    }

    public override byte GetByte(int i)
    {
      return GetValue<byte>(i);
    }

    public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
    {

      var buf = GetValue<byte[]>(i);
      int bytes = Math.Min(length, buf.Length - (int)fieldOffset);
      Buffer.BlockCopy(buf, (int)fieldOffset, buffer, bufferoffset, bytes);
      return bytes;

    }

    public override char GetChar(int i)
    {
      return GetValue<char>(i);
    }

    public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
    {
      //throw new NotImplementedException();
      string s = GetValue<string>(i);
      int chars   = Math.Min(length, s.Length-(int)fieldoffset );
      s.CopyTo((int)fieldoffset, buffer, bufferoffset, chars);

      return chars;
    }

    //public override DbDataReader GetData(int i)
    //{
    //  throw new NotImplementedException();
    //}

    public override string GetDataTypeName(int i)
    {
      return attributes[i].Type.Name;
    }

    public override DateTime GetDateTime(int i)
    {
      return GetValue<DateTime>(i);
    }

    public override decimal GetDecimal(int i)
    {
      return GetValue<decimal>(i);
    }

    public override double GetDouble(int i)
    {
      return GetValue<double>(i);
    }

    public override Type GetFieldType(int i)
    {
      Type t = attributes[i].Type;
      if (!options.ExposeNullableTypes && IsNullable(t))
      {
        return StripNullableType(t);
      }
      return t;
    }

    public override float GetFloat(int i)
    {
      return GetValue<float>(i);
    }

    public override Guid GetGuid(int i)
    {
      return GetValue<Guid>(i);
    }

    public override short GetInt16(int i)
    {
      return GetValue<short>(i);
    }

    public override int GetInt32(int i)
    {
      return GetValue<int>(i);
    }

    public override long GetInt64(int i)
    {
      return GetValue<long>(i);
    }

    public override string GetName(int i)
    {
      Attribute a = attributes[i];
      if (a.IsRelatedAttribute && options.PrefixRelatedObjectColumns)
      {
        return a.FullName;
      }
      return a.Name;
    }

    public override int GetOrdinal(string name)
    {
      for (int i = 0; i < attributes.Count; i++)
      {
        var a = attributes[i];

         if (!a.IsRelatedAttribute && a.Name == name)
         {
           return i;
         }

         if (options.PrefixRelatedObjectColumns && a.IsRelatedAttribute && a.FullName == name )
         {
          return i;
         }

         if (!options.PrefixRelatedObjectColumns && a.IsRelatedAttribute && a.Name == name)
         {
           return i;
         }


      }
      return -1;
    }

    public override string GetString(int i)
    {
      return GetValue<string>(i);
    }



    public override int GetValues(object[] values)
    {
      for (int i = 0; i < attributes.Count; i++)
      {
        values[i] = GetValue(i);
      }
      return attributes.Count;
    }


    
    public override object GetValue(int i)
    {
      object o = GetValue<object>(i);
      if (!options.ExposeNullableTypes && o == null)
      {
        return DBNull.Value;
      }
      return o;
    }

    public override bool IsDBNull(int i)
    {
      object o = GetValue<object>(i);
      return (o == null);
    }

    public override object this[string name]
    {
      get { return GetValue(GetOrdinal(name)); }
    }

    public override object this[int i]
    {
      get { return GetValue(i); }
    }

    #endregion

    #region DbDataReader Members



    public override System.Collections.IEnumerator GetEnumerator()
    {
      return this.enumerator;
    }

    public override bool HasRows
    {
      get { throw new NotSupportedException(); }
    }
    #endregion

  }

  public class EntityDataReaderOptions
  {
    public static EntityDataReaderOptions Default
    {
      get { return new EntityDataReaderOptions(true, false, true, false); }
    }

    public EntityDataReaderOptions(
      bool exposeNullableTypes,
      bool flattenRelatedObjects,
      bool prefixRelatedObjectColumns,
      bool recreateForeignKeysForEntityFrameworkEntities)
    {
      this.ExposeNullableTypes = exposeNullableTypes;
      this.FlattenRelatedObjects = flattenRelatedObjects;
      this.PrefixRelatedObjectColumns = prefixRelatedObjectColumns;
      this.RecreateForeignKeysForEntityFrameworkEntities = recreateForeignKeysForEntityFrameworkEntities;
    }

    /// <summary>
    /// If true nullable value types are returned directly by the DataReader.
    /// If false, the DataReader will expose non-nullable value types and return DbNull.Value
    /// for null values.  
    /// When loading a DataTable this option must be set to True, since DataTable does not support
    /// nullable types.
    /// </summary>
    public bool ExposeNullableTypes { get; set; }

    /// <summary>
    /// If True then the DataReader will project scalar properties from related objects in addition
    /// to scalar properties from the main object.  This is especially useful for custom projecttions like
    ///         var q = from od in db.SalesOrderDetail
    ///         select new
    ///         {
    ///           od,
    ///           ProductID=od.Product.ProductID,
    ///           ProductName=od.Product.Name
    ///         };
    /// Related objects assignable to EntityKey, EntityRelation, and IEnumerable are excluded.
    ///
    /// If False, then only scalar properties from teh main object will be projected.         
    /// </summary>
    public bool FlattenRelatedObjects { get; set; }

    /// <summary>
    /// If True columns projected from related objects will have column names prefixed by the
    /// name of the relating property.  This appies to either from setting FlattenRelatedObjects to True,
    /// or RecreateForeignKeysForEntityFrameworkEntities to True.
    ///
    /// If False columns will be created for related properties that are not prefixed.  This can lead
    /// to column name collision.
    /// </summary>
    public bool PrefixRelatedObjectColumns { get; set; }

    /// <summary>
    /// If True the DataReader will create columns for the key properties of related Entities.
    /// You must pass an ObjectContext and have retrieved the entity with change tracking for this to work.
    /// </summary>
    public bool RecreateForeignKeysForEntityFrameworkEntities { get; set; }

  }

  public static class EntityDataReaderExtensions
  {

    /// <summary>
    /// Wraps the IEnumerable in a DbDataReader, having one column for each "scalar" property of the type T.  
    /// The collection will be enumerated as the client calls IDataReader.Read().
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    /// <returns></returns>
    public static IDataReader AsDataReader<T>(this IEnumerable<T> collection)
    {

      //For anonymous type projections default to flattening related objects and not prefixing columns
      //The reason being that if the programmer has taken control of the projection, the default should
      //be to expose everying in the projection and not mess with the names.
      if (typeof(T).IsDefined(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false))
      {
        var options = EntityDataReaderOptions.Default;
        options.FlattenRelatedObjects = true;
        options.PrefixRelatedObjectColumns = false;
        return new EntityDataReader<T>(collection,options);
      }
      return new EntityDataReader<T>(collection);
    }

    /// <summary>
    /// Wraps the IEnumerable in a DbDataReader, having one column for each "scalar" property of the type T.  
    /// The collection will be enumerated as the client calls IDataReader.Read().
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    /// <returns></returns>
    public static IDataReader AsDataReader<T>(this IEnumerable<T> collection, bool exposeNullableColumns, bool flattenRelatedObjects)
    {
      EntityDataReaderOptions options = new EntityDataReaderOptions(exposeNullableColumns, flattenRelatedObjects, true, false);
      
      return new EntityDataReader<T>(collection,options,null);
    }


    /// <summary>
    /// Enumerates the collection and copies the data into a DataTable.
    /// </summary>
    /// <typeparam name="T">The element type of the collection.</typeparam>
    /// <param name="collection">The collection to copy to a DataTable</param>
    /// <returns>A DataTable containing the scalar projection of the collection.</returns>
    public static DataTable ToDataTable<T>(this IEnumerable<T> collection)
    {
      DataTable t = new DataTable();
      t.Locale = System.Globalization.CultureInfo.CurrentCulture;
      t.TableName = typeof(T).Name;
      EntityDataReaderOptions options = EntityDataReaderOptions.Default;
      options.ExposeNullableTypes = false;
      EntityDataReader<T> dr = new EntityDataReader<T>(collection, options);
      t.Load(dr);
      return t;
    }

    /// <summary>
    /// Wraps the collection in a DataReader, but also includes columns for the key attributes of related Entities.
    /// </summary>
    /// <typeparam name="T">The element type of the collection.</typeparam>
    /// <param name="collection">A collection to wrap in a DataReader</param>
    /// <param name="cx">The Entity Framework ObjectContext, used for metadata access</param>
    /// <returns>A DbDataReader wrapping the collection.</returns>
    public static IDataReader AsDataReader<T>(this IEnumerable<T> collection, ObjectContext context) where T: EntityObject
    {
      EntityDataReaderOptions options = EntityDataReaderOptions.Default;
      options.RecreateForeignKeysForEntityFrameworkEntities = true;
      return new EntityDataReader<T>(collection,options,context);
    }

   

    /// <summary>
    /// Wraps the collection in a DataReader, but also includes columns for the key attributes of related Entities.
    /// </summary>
    /// <typeparam name="T">The element type of the collectin.</typeparam>
    /// <param name="collection">A collection to wrap in a DataReader</param>
    /// <param name="detachObjects">Option to detach each object in the collection from the ObjectContext.  This can reduce memory usage for queries returning large numbers of objects.</param>
    /// <param name="prefixReladObjectColumns">If True, qualify the related object keys, if False don't</param>
    /// <returns>A DbDataReader wrapping the collection.</returns>
    /// <summary>
    public static IDataReader AsDataReader<T>(this IEnumerable<T> collection, ObjectContext context, bool detachObjects, bool prefixRelatedObjectColumns) where T : EntityObject
    {
      EntityDataReaderOptions options = EntityDataReaderOptions.Default;
      options.RecreateForeignKeysForEntityFrameworkEntities = true;
      options.PrefixRelatedObjectColumns = prefixRelatedObjectColumns;

      if (detachObjects)
      {
        return new EntityDataReader<T>(collection.DetachAllFrom(context),options,context);
      }
      return new EntityDataReader<T>(collection,options,context);
    }
    static IEnumerable<T> DetachAllFrom<T>(this IEnumerable<T> col, ObjectContext cx)
    {
      foreach (var t in col)
      {
        cx.Detach(t);
        yield return t;
      }
    }

    /// <summary>
    /// Enumerates the collection and copies the data into a DataTable, but also includes columns for the key attributes of related Entities.
    /// </summary>
    /// <typeparam name="T">The element type of the collection.</typeparam>
    /// <param name="collection">The collection to copy to a DataTable</param>
    /// <param name="cx">The Entity Framework ObjectContext, used for metadata access</param>
    /// <returns>A DataTable containing the scalar projection of the collection.</returns>
    public static DataTable ToDataTable<T>(this IEnumerable<T> collection, ObjectContext context) where T: EntityObject
    {
      DataTable t = new DataTable();
      t.Locale = System.Globalization.CultureInfo.CurrentCulture;
      t.TableName = typeof(T).Name;

      EntityDataReaderOptions options = EntityDataReaderOptions.Default;
      options.RecreateForeignKeysForEntityFrameworkEntities = true;

      EntityDataReader<T> dr = new EntityDataReader<T>(collection, options, context);
      t.Load(dr);
      return t;
    }




  }
}

 

Bello vero?

Con questa estensione possiamo facilmente trasformare una IEnumerable in un IDataReader e usarlo nelle più comuni operazioni di lettura dati.

L’utilizzo è banale:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Samples.EntityDataReader;

namespace CSharpLight_CSH
{
    class user
    {
        public int ID_User { get; set; }
        public int ID_Role { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }
    }

    class role
    {
        public int ID_Role { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<role> rs = new List<role>();
            rs.Add(new role() { ID_Role = 1, Name = "Default", Description = "" });
            rs.Add(new role() { ID_Role = 2, Name = "J.Dev", Description = "" });
            rs.Add(new role() { ID_Role = 3, Name = "S.Dev", Description = "" });

            List<user> us = new List<user>();
            us.Add(new user() { ID_User = 1, ID_Role = 1, Name = "Fabio", Surname = "Arosio" });
            us.Add(new user() { ID_User = 1, ID_Role = 2, Name = "Claudio", Surname = "Arosio" });
            us.Add(new user() { ID_User = 1, ID_Role = 1, Name = "Lorenzo", Surname = "Arosio" });

            var us_rs = from u in us
                        where u.ID_Role == 1
                        select new
                        {
                            User_Name = u.Name,
                            User_Surname = u.Surname,
                            Role = rs.FirstOrDefault(r => r.ID_Role == u.ID_Role).Name
                        };

            //Ecco la bulkcopy
            System.Data.SqlClient.SqlBulkCopy SqlBulk = new System.Data.SqlClient.SqlBulkCopy("server=DBSERVER;database=DB;uid=USERID;pwd=PWD");
            SqlBulk.DestinationTableName = "TableInsertInto";
            SqlBulk.WriteToServer(us_rs.AsDataReader());

            //Oppure un Datatable
            System.Data.DataTable dt = new System.Data.DataTable("TestDT");
            dt.Load(us_rs.AsDataReader());

            //Semplice Vero?

            Console.ReadKey();
        }
    }
}