Попытка реализовать группировку в IEnumerable для потоковой передачи из базы данных
В настоящее время приложение, с которым я работаю, использует строго типизированный DataSet
s для работы с данными из БД. У нас есть таблица под названием COM_ControlIn
это представляет собой "файл", и несколько других таблиц связаны с управляющей таблицей. Тот, от которого мне нужно получить поток, называется COM_GenericTransactionItems
, В этой таблице есть столбец COMControlIn_UID
который связывает его с контрольной таблицей, как следует из названия.
У нас есть несколько методов для извлечения данных из этой таблицы, например, один, который находит все записи для данного COMControlIn_UID
, но проблема со всем этим заключается в том, что они извлекают все записи одновременно, что становится проблемой сейчас, когда огромное количество данных заставляет нас достигать предела памяти.NET. Весь наш существующий код использует строго типизированные наборы данных, построенные из XSD, сгенерированных Visual Studio из схемы базы данных.
Моя идея заключалась в использовании IEnumerable
"потоковые" партии записей из базы данных вместо извлечения всего сразу, сохраняя при этом строго типизированные наборы данных, которые мы использовали ранее, для обеспечения совместимости без существенных изменений. Код, который я написал, выглядит примерно так:
COM_GenericTransactionItemsDS com_GenericTransactionItemsDS = new COM_GenericTransactionItemsDS();
long lastUID = 0;
using (SqlConnection sqlConnection = new SqlConnection("...")
{
sqlConnection.Open();
SqlCommand sqlCommand = new SqlCommand("SELECT MAX(UID) FROM COM_GenericTransactionItems WHERE COMControlIn_UID = " + p_COMControlIn_UID, sqlConnection);
//because apparently I'm not allowed to straight cast...
long maxUID = Convert.ToInt64(sqlCommand.ExecuteScalar());
while (lastUID < maxUID)
{
com_GenericTransactionItemsDS.Clear();
using (SqlDataAdapter sqlDataAdapter = new SqlDataAdapter())
{
//Build Select
string strSQL = "SELECT TOP(" + fetchAmount + ") " + SQL_Columns + " FROM COM_GenericTransactionItems " +
"WHERE COMControlIn_UID = " + p_COMControlIn_UID.ToString() + " AND UID > " + lastUID + " ORDER BY UID";
//Get Data
sqlDataAdapter.SelectCommand = new SqlCommand(strSQL, sqlConnection);
sqlDataAdapter.SelectCommand.CommandTimeout = Convert.ToInt32(context.strContext[(int)eCCE_Context._COMMAND_TIMEOUT]);
sqlDataAdapter.Fill(com_GenericTransactionItemsDS, "COM_GenericTransactionItems");
lastUID = com_GenericTransactionItemsDS.COM_GenericTransactionItems.Max(r => r.UID);
}
yield return com_GenericTransactionItemsDS;
}
}
Он очень хорошо работает для извлечения данных и значительно сократил использование памяти, но я столкнулся с проблемой чуть дальше.
Мне нужно сгруппировать элементы в этой таблице по определенному столбцу (дате), но понятие этого противоречит всему пакетному подходу, потому что вам нужно знать, как выглядит весь ваш набор данных для группировки.
Я не могу выполнить группировку в SQL, потому что мне нужны данные в виде пары ключ-значение, которую Линк давал мне, прежде чем я переключился на использование этого метода (если у меня нет способа сделать это в SQL).
Когда я пытаюсь использовать SelectMany
чтобы сгладить все мои ряды в один перечисляемый я получаю RowNotInTableException
всякий раз, когда я пытаюсь получить доступ к любому из них. Я действительно не знаю, что еще попробовать.
Для справки: это запрос Linq, который я использую для группировки:
var dateGroups = from row in p_COM_GenericTransactionItemsDS.SelectMany(c => c.COM_GenericTransactionItems) group row by (DateTime)row[tableDefinitions.CaptureDate] into groups select groups;
Я думаю, что проблема заключается в том, как я возвращаю данные из своего потокового метода, но я не знаю, как еще это сделать. В идеале я хотел бы извлечь все строки из наших таблиц данных в IEnumerable
и просто повторить это, но DataRows
не сохраняйте схему таблицы (я читал, что схема хранится в DataTable
они связаны с), поэтому, как только вы удалите их из набора данных, они практически бесполезны.
1 ответ
Я решил свою проблему. Я изменил свой метод потоковой передачи, чтобы перебирать элементы, которые он получает в пакете, делать их копии и возвращать их один за другим, например, так:
foreach (COM_GenericTransactionItemsDS.COM_GenericTransactionItemsRow row in com_GenericTransactionItemsDS.COM_GenericTransactionItems.Rows)
{
lastUID = row.UID;
COM_GenericTransactionItemsDS.COM_GenericTransactionItemsRow newRow = com_GenericTransactionItemsDS.COM_GenericTransactionItems.NewCOM_GenericTransactionItemsRow();
newRow.ItemArray = row.ItemArray;
yield return newRow;
}