#c# #performance #linq #datatable
#c# #Производительность #linq #datatable
Вопрос:
Приведенный ниже метод выбирает права администратора и возвращает bool из кэшированной таблицы данных, будет ли он работать лучше с Linq?
Вы можете спросить, почему бы вам не протестировать это. Ну, из-за недостатка знаний я не могу написать это в Linq
DataRow[] result = PrivilegeMap.Select("privilegeActionId=" (int)actionId);
bool moderatorHasIt = Convert.ToBoolean(result[0]["moderatorHasIt"]);
bool adminHasIt = Convert.ToBoolean(result[0]["adminHasIt"]);
if (myRole == User.Role.Admin)
{
return Convert.ToBoolean(adminHasIt);
}
if (myRole == User.Role.Moderator)
{
return Convert.ToBoolean(moderatorHasIt);
}
else
{
return false;
}
Комментарии:
1. Я удалил свой ответ, потому что вспомнил, что DataTable поддерживает индексы на основе
Select
запросов.
Ответ №1:
Это могло быть.
Давайте предположим следующее:
-
Баланс начального размера, частоты чтения и частоты обновления базовой таблицы таков, что не имеет смысла заменять все это загрузкой всего этого в память и просто многократным просмотром этого.
-
Для каждого идентификатора есть только одна соответствующая строка.
-
В строке есть куча других интересных полей, о которых мы здесь не заботимся.
Тогда, если мы заменим PrivilegeMap на Linq2SQL Table<Privileges>
, эквивалентный код LINQ будет выглядеть примерно так:
var result = PrivilegeMap.Where(p => p.PrivilegeActionId == actionID).Select(p => new{p.ModeratorHasIt, p.AdminHasIt}).First()
if (myRole == User.Role.Admin)
{
return result.AdminHasIt;
}
if (myRole == User.Role.Moderator)
{
return result.ModeratorHasIt;
}
else
{
return false;
}
(Если на то пошло, var result = PrivilegeMap.Where(p => p.PrivilegeActionId == actionID).First(p => new{p.ModeratorHasIt, p.AdminHasIt})
также может быть записано как var result = (from p in PrivilegeMap where p.PrivilegeActionId == actionID select new{p.ModeratorHasIt, p.AdminHasIt}).First()
Это просто другой синтаксис для тех же операций LINQ).
Допустим, actionID
будет 2
ли ваш код преобразован в SQL следующим образом:
ВЫБЕРИТЕ * ИЗ привилегий, ГДЕ privilegeActionId=2
Приведенный выше Linq был бы превращен в:
ВЫБЕРИТЕ СТАТУС администратора TOP 1, moderatorHasIt ИЗ привилегий, ГДЕ privilegeActionId
Вы можете видеть, как, если бы это была таблица с большим количеством столбцов и / или если бы было более одной совпадающей строки, это могло бы быть намного эффективнее.
(Если бы PrivilegeMap была перечислимой, но не запрашиваемой, это превратилось бы в операцию, в которой все это загружалось и сканировалось, и, следовательно, вообще не было эффективным).
С другой стороны, код для создания этого SQL может быть более сложным, и это требует некоторой работы по настройке объектов сущности привилегий. Если бы это была одноразовая операция, она, конечно, не стоила бы того ни с точки зрения эффективности разработчика, ни с точки зрения времени выполнения, но в противном случае это могло бы принести пользу обоим.
Однако обратите внимание, что в обоих наших случаях мы выполняем запросы без необходимости. Я бы на самом деле заменил мой на:
if (myRole == User.Role.Admin)
{
return PrivilegeMap.Where(p => p.PrivilegeActionId == actionID).Select(p => p.AdminHasIt).First();
}
if (myRole == User.Role.Moderator)
{
return PrivilegeMap.Where(p => p.PrivilegeActionId == actionID).Select(p => p.ModeratorHasIt);
}
else
{
return false;
}
Который будет либо запрашивать только adminHasIt, только moderatorHasIt, либо вообще не запрашивать, поскольку мы гарантированно получаем false для любого другого значения myRole
независимо от состояния базы данных.
Аналогично, вы получаете гораздо более простое улучшение с:
if(myRole != User.Role.Admin amp;amp; myRole != User.Role.Moderator)
return false;
DataRow[] result = PrivilegeMap.Select("privilegeActionId=" (int)actionId);
if (myRole == User.Role.Admin)
{
return Convert.ToBoolean(result[0]["adminHasIt"]);
}
if (myRole == User.Role.Moderator)
{
return Convert.ToBoolean(result[0]["moderatorHasIt"]);
}
Здесь мы сначала полностью избегаем запроса к базе данных, если мы не можем его использовать, преобразуем только поле, о котором мы заботимся, и впоследствии не преобразуем bool в bool . Такого рода локальное мышление о том, какие данные фактически используются, намного проще выполнить, и, хотя большинство таких сбережений невелики, некоторые из них велики (этот потенциально может быть), и они являются скорее делом привычки, чем сложными компромиссами.
Комментарии:
1. Пожалуйста, хотя я только сейчас вижу, что в вашем вопросе говорилось, что таблица была кэширована. В этом случае вы бы быстрее воспользовались предложением @StriplingWarrior использовать вместо этого словарь. Тем не менее, вы, конечно, могли бы запрашивать ту же таблицу (будь то кэшированный DataTable или список<Privilige> для Linq2Objects) для других вещей, что еще раз меняет баланс. В последнем случае производительность между linq и вашим подходом намного ближе.
Ответ №2:
Вероятно, с использованием LINQ было бы быстрее, потому что datatable не нужно было бы анализировать строку вашего запроса, но вы получите наибольшее преимущество, изменив представление данных, чтобы избежать запросов к datatable в первую очередь.
Создайте IDictionary<int, bool>
, представляющий интересующие вас привилегии, с ключом actionId
. Затем, когда вам нужно выполнить поиск, вы можете просто вернуть dict[actionId
.
В любом случае, я подозреваю, что это, вероятно, случай преждевременной оптимизации: вы тестировали свою программу и обнаружили, что этот фрагмент кода занимает значительную часть вашего времени обработки?
Комментарии:
1. Спасибо, и нет, это будет вызвано только несколькими пользователями. Я просто хотел знать, будет ли linq иметь большое значение
2. @nLL: Тогда ваше время лучше использовать в другом месте. 🙂
3. Базе данных придется что -то анализировать, независимо от того, исходит ли это из Linq, Datatable или SQL с ручными кодами.
4. @JonHanna: Предполагая, что таблица данных находится в памяти (в OP сказано, что это кэшированная таблица данных), вы можете выполнять итерации по строкам программно, не обращаясь к базе данных. В этом случае LINQ to Objects не нужно было бы так анализировать запрос, как выполнить его.
5. Вы правы, я пропустил слово «кэшированный» выше, поэтому, хотя я упомянул о возможности в своем ответе, я затем написал, как будто это не так.
Ответ №3:
Приведенный вами код неполон, поскольку myRole
переменная не определена.
Имея это в виду, я бы предложил записать это в инструкции switch и двигаться дальше, пока это не будет определено как проблема. Плюс это было бы легче читать (на мой взгляд).