2011-02-10 11 views
7

Próbuję uruchomić kwerendę linq, ale potrzebuję wyniku jako datatable, ponieważ używam go do przechowywania rekordów z różnych zapytań w tym samym obiekcie viewstate.Najszybszy sposób na wypełnienie DataTable z kwerendy LINQ przy użyciu DataContext

Dwie wersje poniżej kompilują, ale zwracają pusty zestaw. Dokładny błąd to "Wartość nie może być pusta Nazwa parametru: źródło". (I tak mam zaznaczone są dane):

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>; 

Przykłady w tym thread nie wydają się działać w tej sytuacji albo.

Chyba mógłbym po prostu uruchomić polecenie sql query w staroświecki sposób, ale czy linq nie powinien być szybszy?

Odpowiedz

19

Twój problem jest taki:

as IEnumerable<DataRow> 

as kluczowe wykonuje bezpiecznej obsady, a nie konwersji, który wydaje się, że można by pomyśleć, że to robi. as kluczowe jest semantycznie samo jak robi to:

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") 
    }); 

wyjątkiem wersji z as nie rzuci wyjątek, gdy nie udaje mu się oddać swój obiekt kwerendy (który jest IQueryable<T>, gdzie T to typ anonimowy) do IEnumerable<DataRow> (którego nie jest).

Niestety, nie ma wbudowanej metody, o której jestem świadomy, która będzie wymagać przeliczenia konkretnego typu (na przykład twojego anonimowego typu w tym przykładzie) i przekształcenia go w DataTable. Pisanie jednego nie byłoby zbyt skomplikowane, ponieważ zasadniczo należałoby uzyskać właściwości odzwierciedlające, a następnie powtórzyć kolekcję i użyć tych właściwości do utworzenia kolumn w DataTable. Napiszę przykład w kilku.

Coś jak ten, umieszczony w klasie statycznej w przestrzeni nazw, że jesteś using, powinna zapewnić metodę rozszerzenia, które będą robić to, co chcesz:

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

widzę - tak to jest tylko ukrywanie InvalidCastException ? Jak mogę go wtedy wprowadzić do datatable? – JumpingJezza

+1

@JumpingJezza: Tak; "as" ma być używane w przypadkach, gdy wiesz, że obiekt może nie być typem, którego się spodziewasz i nie jest "wyjątkowy". Słowo kluczowe 'as' ma wartość' null', jeśli rzutowanie jest niepoprawne i (w wyniku) może być używane tylko z typami odniesienia i 'Nullable '. Zobacz edytowaną właśnie publikację dla przykładowej metody rozszerzenia, która może przekształcić dowolny 'IEnumerable ' w 'DataTable'. –

+0

Idealnie! Plus dowiedziałem się czegoś nowego o 'as' :) – JumpingJezza

Powiązane problemy