Select не работает на IQueryable, но работает на IList
У меня есть две строки кода, одна
AllItems().Where(c => c.Id== id)
.Select(d => new Quality(d.QualityType)).ToList();
а другой
AllItems().Where(c => c.Id== id).ToList()
.Select(d => new Quality(d.QualityType)).ToList();
Разница только во втором утверждении ToList()
называется после Where
заявление. Второй норматив работает просто отлично.
В первом операторе используется конструктор без параметров по умолчанию вместо конструктора с параметром. поэтому список создается, но объекты в списке инициализируются значениями по умолчанию, а не d.QualityType.
Вы можете увидеть полный источник рассматриваемого файла в (Метод: GetBestQualityInHistory)
https://github.com/kayone/NzbDrone/blob/master/NzbDrone.Core/Providers/HistoryProvider.cs
** Редактировать: после дальнейшего изучения, это, кажется, ошибка SubSonic, если последний ToList
заменяется OrderBy
дозвуковой бросает The construtor 'Void .ctor(NzbDrone.Core.Repository.Quality.QualityTypes, Boolean)' is not supported
,
2 ответа
Если SubSonic работает так же, как Entity Framework, вы не можете использовать конструкторы с параметрами - вы должны использовать безпараметрические конструкторы и инициализаторы. Мое объяснение очень высокого уровня заключается в том, что запрос не выполняется как есть - он переводится в SQL, и поэтому вы должны использовать инициализаторы свойств, чтобы дерево выражений знало, какие свойства в проецируемом типе должны заполняться значениями. При использовании конструктора с параметрами дерево выражений не знает, к чему принадлежит передаваемый параметр (оно не проверяет содержимое конструктора). Реальный конструктор (без параметров) вызывается после выполнения Tolist
и набор результатов материализуется в QuantityType
экземпляров.
Это не совсем ответ, и я собирался сделать это комментарием, но мне нужно больше места для фрагмента кода.
Судя по коду SubSonic, я почти уверен, что в некоторых случаях, когда вы получаете ошибки "конструктор не поддерживается", SS по какой-то причине пытается проанализировать ваш new ...()
утверждение в SQL. Этот метод является частью SQL Formatter, и похоже, что он обрабатывает только DateTime:
protected override NewExpression VisitNew(NewExpression nex)
{
if (nex.Constructor.DeclaringType == typeof(DateTime))
{
// ...omitted for brevity...
}
throw new NotSupportedException(string.Format("The construtor '{0}' is not supported", nex.Constructor));
}
Я думаю, что это было бы нормально, если бы вы сделали что-то вроде:
someData.Where (data => data.CreatedDate <= new DateTime (2011, 12, 01)).Select (data => data)
Тогда что newDateTime
будет переведен в SQL. Поэтому, как бы вы ни изменили Linq, чтобы получить это исключение, я думаю, что это то, что происходит. Я предполагаю, что это потому, что если вы добавите .OrderBy()
после .Select()
тогда вы больше не вызываете OrderBy для IQueryable того, что возвращает AllItems(), а вместо этого пытаетесь упорядочить с помощью того, что возвращается.Select(), который является перечисляемым новым объектом качества, поэтому SS, вероятно, пытается повернуть все это в SQL.
Мне интересно, будет ли это работать правильно, если вы изменили это?
AllItems().Where(c => c.Id== id)
.OrderBy(d => d.QualityType)
.Select(d => new Quality(d.QualityType));