Подтвердить что ты не робот

Создайте комбинированный DataTable из двух таблиц данных, связанных с LINQ. С#

У меня есть следующий код, который заполняет dataTable1 и dataTable2 двумя простыми SQL-запросами, dataTableSqlJoined заполняется из одних и тех же таблиц, но объединяется.

Я пытаюсь написать запрос LINQ, который может создать dataTableLinqJoined, как если бы он был создан с использованием SQL. В моем примере ниже он возвращает только значения из dataTable1.

У меня есть проблема с тем, что нужно поставить в SELECT запроса linq. Как создать новый DataRow, содержащий все столбцы из обоих DataRows. Я не буду знать точные имена столбцов/схемы запросов до времени выполнения.

sqlCommand = new SqlCommand("SELECT ID, A, B FROM Table1", sqlConnection, sqlTransaction);
sqlAdapter = new SqlDataAdapter(sqlCommand);
DataTable dataTable1 = new DataTable();
sqlAdapter.Fill(dataTable1);

sqlCommand = new SqlCommand("SELECT ID, C, D FROM Table2", sqlConnection, sqlTransaction);
sqlAdapter = new SqlDataAdapter(sqlCommand);
DataTable dataTable2 = new DataTable();
sqlAdapter.Fill(dataTable2);

sqlCommand = new SqlCommand("SELECT Table1.ID, A, B, Table2.ID, C, D FROM Table1 INNER JOIN Table2 ON Table1.ID = Table2.ID", sqlConnection, sqlTransaction);
sqlAdapter = new SqlDataAdapter(sqlCommand);
DataTable dataTableSqlJoined = new DataTable();
sqlAdapter.Fill(dataTableSqlJoined);

var dataRows =
    from
        dataRows1 in dataTable1.AsEnumerable()
    join
        dataRows2 in dataTable2.AsEnumerable()
    on
        dataRows1.Field<int>("ID") equals dataRows2.Field<int>("ID")
    select
        dataRows1; // + dataRows2;

DataTable dataTableLinqJoined = dataRows.CopyToDataTable();

Для немного больше фона, объединенный запрос очень интенсивен БД и вызывает проблемы с производительностью. Данные, возвращаемые первым запросом, довольно статичны и могут быть сильно кэшированы. Данные, возвращаемые вторым запросом, постоянно изменяются, но быстро запускаются и поэтому не нужно кэшировать. Существует также много кода, основанного на передаче объединенного DataTable, и поэтому для передачи данных в другом формате доступно не так много возможных вариантов.

4b9b3361

Ответ 1

Вы еще не просмотрели эту страницу?

КАК: Реализовать вспомогательный класс DataSet JOIN в Visual С#.NET

Если для вас не достаточно LINQy, вы можете разбить данные строки на массивы объектов:

DataTable targetTable = dataTable1.Clone();
var dt2Columns = dataTable2.Columns.OfType<DataColumn>().Select(dc => 
    new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, dc.ColumnMapping));
targetTable.Columns.AddRange(dt2Columns.ToArray());
var rowData =
    from row1 in dataTable1.AsEnumerable()
    join row2 in dataTable2.AsEnumerable()
        on row1.Field<int>("ID") equals row2.Field<int>("ID")
    select row1.ItemArray.Concat(row2.ItemArray).ToArray();
foreach (object[] values in rowData)
    targetTable.Rows.Add(values);

Я думаю, что примерно так же сложно, как вы это сделаете, и я объясню, почему: это схема.

A DataRow не является независимым объектом; это зависит от его владения DataTable и не может жить без него. Нет поддерживаемого способа создания "отключенного" DataRow; метод расширения CopyToDataTable() работает с строками, которые уже существуют в одном DataTable и просто копируют схему из источника (помните, что каждый DataRow имеет ссылку на родителя Table) перед копированием самих строк (скорее всего, используя ImportRow, хотя я на самом деле не открыл Reflector для проверки).

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

Затем вы можете, наконец, создать строки - но только по одному, так как DataTable и связанный с ним DataRowCollection не выставляют никаких методов для добавления нескольких строк за раз. Конечно, вы могли бы добавить свой собственный метод расширения для DataRowCollection, чтобы сделать этот "взгляд" более приятным:

public static void AddRange(this DataRowCollection rc,
    IEnumerable<object[]> tuples)
{
    foreach (object[] data in tuples)
        rc.Add(tuples);
}

Тогда вы можете избавиться от foreach в первом методе и заменить его на:

targetTable.Rows.AddRange(rowData);

Хотя это действительно просто перемещает многословие, а не устраняет его.

В нижней строке, пока вы работаете с устаревшей иерархией классов DataSet, всегда будет немного круто. Расширения Linq to DataSet хороши, но они являются только расширениями и не могут изменять ограничения выше.

Ответ 2

Aaronaught это было здорово. Но хотелось бы добавить несколько улучшений в ваш код LINQy. Добавляя столбцы из таблицы данныхTable2 в Target, вероятность того, что в таблице Target (в которой мы присоединяемся) уже есть несколько столбцов. Итак, идем.

DataTable targetTable = dataTable1.Clone();
var dt2Columns = dataTable2.Columns.OfType<DataColumn>().Select(dc => 
new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, dc.ColumnMapping));
var dt2FinalColumns=from dc in dt2Columns.AsEnumerable()
                    where targetTable.Columns.Contains(dc.ColumnName) == false
                    select dc;
targetTable.Columns.AddRange(dt2FinalColumns.ToArray());
var rowData =from row1 in dataTable1.AsEnumerable()
             join row2 in dataTable2.AsEnumerable()
             on row1.Field<int>("ID") equals row2.Field<int>("ID")
             select row1.ItemArray.Concat(row2.ItemArray.Where(r2=> row1.ItemArray.Contains(r2)==false)).ToArray();
foreach (object[] values in rowData)
targetTable.Rows.Add(values);

Надеюсь, это было бы полезно для таких парней, как я.

Ответ 3

Простите меня, если я буду похож на идиота.

Я думаю, вы должны подготовить финальную таблицу (со всеми полями таблицы A и таблицы B).
И вместо того, чтобы использовать LINQ, выполните объединение, а затем выполните ForEach в результате и вставьте значение в окончательный файл данных.

Псевдокод:

dt1.Join(dt2).Where(...). ForEach (строка = > код для чтения содержимого анонимного объекта и добавления его в finalTable.Rows)

Ответ 4

select new {
    ID = dataRows1.ID,  // no need to select dataRows2.ID, because of JOIN.
    A = dataRows1.A,
    B = dataRows1.B,
    C = dataRows2.C,
    D = dataRows2.D 
};