2011-02-10 30 views
7

Estoy intentando ejecutar una consulta linq pero necesito el resultado como una tabla de datos porque estoy usando eso para almacenar registros de diferentes consultas en el mismo objeto viewstate.La forma más rápida de llenar DataTable desde la consulta LINQ usando DataContext

Las 2 versiones siguientes compilan pero devuelven un conjunto vacío. El error exacto es "El valor no puede ser nulo. Nombre del parámetro: origen". (Y sí He comprobado que hay datos): no parecen

MyDatabaseDataContext db = new MyDatabaseDataContext(conn); 
IEnumerable<DataRow> queryProjects = 
    (from DataRow p in db.STREAM_PROJECTs.AsEnumerable() 
    where p.Field<int>("STREAM_ID") == StreamID 
    select new 
    { 
     PROJECT_ID = p.Field<int>("PROJECT_ID"), 
     PROJECT_NAME = p.Field<string>("PROJECT_NAME") 
    }) as IEnumerable<DataRow>; 
DataTable results = queryProjects.CopyToDataTable<DataRow>(); 

...

//(from p in db.STREAM_PROJECTs.AsEnumerable() 
//where p.STREAM_ID == StreamID 
//select new 
//{ 
// p.PROJECT_NAME, 
// p.PROJECT_ID 
//}) as IEnumerable<DataRow>; 

Los ejemplos de este thread a trabajar en esta situación tampoco.

Supongo que podría ejecutar un comando de consulta SQL a la antigua usanza, pero ¿no se supone que es más rápido?

Respuesta

19

Su problema es el siguiente:

as IEnumerable<DataRow> 

La palabra clave as realiza una conversión segura, no una conversión, que parece que se podría pensar que se está haciendo. La palabra clave as es semánticamente lo mismo que hacer esto:

IEnumerable<DataRow> queryProjects = 
    (IEnumerable<DataRow>)(from DataRow p in db.STREAM_PROJECTs.AsEnumerable() 
    where p.Field<int>("STREAM_ID") == StreamID 
    select new 
    { 
     PROJECT_ID = p.Field<int>("PROJECT_ID"), 
     PROJECT_NAME = p.Field<int>("PROJECT_NAME") 
    }); 

Excepto la versión con as no va a lanzar una excepción cuando no puede emitir su objeto de consulta (que es un IQueryable<T>, donde T es un tipo anónimo) a un IEnumerable<DataRow> (que no es).

Desafortunadamente, no hay un método incorporado que conozca que tome un enumerable de un tipo concreto (como su tipo anónimo en este ejemplo) y lo convierta en un DataTable. Escribir uno no sería demasiado complicado, ya que esencialmente necesitaría obtener las propiedades de forma reflexiva, luego iterar sobre la colección y usar esas propiedades para crear columnas en un DataTable. Voy a publicar un ejemplo en algunos.

Algo como esto, se coloca en una clase estática dentro de un espacio de nombres que eres using, debe proporcionar un método de extensión que va a hacer lo que quiere:

public static DataTable ToDataTable<T>(this IEnumerable<T> source) 
{ 
    PropertyInfo[] properties = typeof(T).GetProperties(); 

    DataTable output = new DataTable(); 

    foreach(var prop in properties) 
    { 
     output.Columns.Add(prop.Name, prop.PropertyType); 
    } 

    foreach(var item in source) 
    { 
     DataRow row = output.NewRow(); 

     foreach(var prop in properties) 
     { 
      row[prop.Name] = prop.GetValue(item, null); 
     } 

     output.Rows.Add(row); 
    } 

    return output; 
} 
+0

veo - por lo que se acaba escondiendo la InvalidCastException ? ¿Cómo lo meto en la tabla de datos? – JumpingJezza

+1

@JumpingJezza: Sí; 'as' se debe usar en casos en los que sepa que el objeto podría no ser del tipo que está esperando y no es un caso" excepcional ". La palabra clave 'as' se evalúa como' null' si el molde no es válido, y (como resultado) solo se puede usar con tipos de referencia y 'Nullable '. Vea la edición que acabo de publicar para un método de extensión de muestra que puede convertir cualquier 'IEnumerable ' en 'DataTable'. –

+0

¡Perfecto! Además, descubrí algo nuevo sobre 'as' :) – JumpingJezza

Cuestiones relacionadas