Самый быстрый способ заполнить DataTable из запроса LINQ, используя DataContext - PullRequest
7 голосов
/ 10 февраля 2011

Я пытаюсь выполнить запрос linq, но мне нужен результат в виде таблицы данных, так как я использую его для хранения записей из разных запросов в одном и том же объекте viewstate.

2 версии ниже компилируются, но возвращаютсяпустой набор.Точная ошибка: "Значение не может быть нулевым. Имя параметра: источник" .(и да, я проверил, есть ли данные):

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

Примеры в этой теме не работают вэта ситуация тоже.

Полагаю, я мог бы просто выполнить команду sql query, но разве linq не должен быть быстрее?

1 Ответ

19 голосов
/ 10 февраля 2011

Ваша проблема заключается в следующем:

as IEnumerable<DataRow>

Ключевое слово as выполняет безопасное приведение, а не преобразование, что, как вам кажется, может показаться. Ключевое слово as семантически аналогично тому, как это делается:

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

За исключением того, что версия с as не будет выдавать исключение, если не удается привести ваш объект запроса (который является IQueryable<T>, где T является анонимным типом) в IEnumerable<DataRow> (который не «т).

К сожалению, я не знаю встроенного метода, который бы использовал перечислимый конкретный тип (например, ваш анонимный тип в этом примере) и превратил его в DataTable. Написание одного не было бы слишком сложным, так как вам, по сути, нужно рефлексивно получить свойства, а затем выполнить итерацию по коллекции и использовать эти свойства для создания столбцов в DataTable. Я приведу пример в нескольких.

Что-то вроде этого, помещенное в статический класс в пространстве имен, которое вы using, должно предоставить метод расширения, который будет делать то, что вы хотите:

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