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));
Другие вопросы по тегам