Запрос Linq с Содержит работает только с IQueryable находится во внешней переменной - PullRequest
7 голосов
/ 06 октября 2011

Я использую Entity Framework с Linq to Entities, пытаясь выбрать некоторые данные из моей базы данных. Когда я создаю запрос Linq, использующий метод IQueryable<int>.Contains, он может фильтровать данные, только если я использую внешнюю переменную! Позвольте мне показать пример.

Этот блок кода, отлично работает :

var volumes = (from v in work.VolumeAdditiveRepository.All
             where v.AdditivesID == AdditivesID
             select v.MetricID);
var metrics =
    from m in work.MetricRepository.All
    where !volumes.Contains(m.ID)
    select m;

Если вы посмотрите внимательно, вы увидите, что я использую переменную volumes внутри этого фрагмента в состоянии where. Если я скопирую содержимое этой переменной и вставлю ее в переменную metrics, что приведет к приведенному ниже коду, возникнет ошибка: "Unable to create a constant value of type 'CalculadoraRFS.Models.Domain.VolumeAditivo'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.".

var metrics =
    from m in work.MetricRepository.All
    where !(from v in work.VolumeAdditiveRepository.All
             where v.AdditivesID == AdditivesID
             select v.MetricID).Contains(m.ID)
    select m;

Как замена переменных может вызвать такую ​​ошибку ?! Я делаю (конечно) что-то не так?
Спасибо!


UPDATE:

На самом деле, я обнаружил, что проблема в шаблоне репозитория или DbContext, как и @jhamm, кажется, является проблемой. Фрагмент ниже также не работает:

var query = from m in work._context.Metric
               where !(from v in work._context.VolumeAdditive
                       where v.AdditivesID == AdditivesID
                       select v.MetricID).Contains(m.ID)
               select m;

Но фрагмент ниже работает. Я просто взял контекст из класса UnitOfWork, хотя он очень просто определен там: public CalculadoraRFSContext _context = new CalculadoraRFSContext();.

var _context = new CalculadoraRFSContext();
var query = from m in _context.Metric
               where !(from v in _context.VolumeAdditive
                       where v.AdditivesID == AdditivesID
                       select v.MetricID).Contains(m.ID)
               select m;

Теперь я действительно запутался в этом! Разве это не должно работать так, как ожидалось?!

1 Ответ

6 голосов
/ 07 октября 2011

Я использовал LINQPad , чтобы использовать модель EF Database First для запроса аналогичного типа. Как объединенные, так и отдельные запросы дали одинаковые правильные результаты и сгенерировали один и тот же SQL. Вот ссылка о том, как использовать LINQPad с Entity Framework . Одним из отличий может быть использование шаблона репозитория, я его не использую. Я бы порекомендовал провести тестирование с первым запросом, чтобы увидеть, какой SQL генерируется. После выполнения запроса в LINQPad есть вкладка SQL, которая может помочь в устранении неполадок, глядя на сгенерированный SQL.

Если у вас все еще возникают проблемы с комбинированным оператором LINQ, следующим хорошим шагом будет попытка использования объекта Entity Framework без объектов Repository. Если этот запрос работает, возможно, что-то не так с вашими объектами репозитория.

// Guessing that Metric and VolumeAdditive are the EF Entities
// LINQPad database dropdown sets the context so they were not set it in these samples
var metrics =
    from m in Metric
    where !(from v in VolumeAdditive
             where v.AdditivesID == AdditivesID
             select v.MetricID).Contains(m.ID)
    select m;

Чтобы выяснить, в чем заключается проблема, я бы затем использовал MetricRepository с объектом VolumeAdditive EF.

var metrics =
    from m in work.MetricRepository.All
    where !(from v in VolumeAdditive
             where v.AdditivesID == AdditivesID
             select v.MetricID).Contains(m.ID)
    select m;

Тогда я бы переключил их на использование объекта Metric EF с VolumeAdditiveRepository.

var metrics =
    from m in Metric
    where !(from v in work.VolumeAdditiveRepository.All
             where v.AdditivesID == AdditivesID
             select v.MetricID).Contains(m.ID)
    select m;

Исходя из сгенерированного SQL и того, какие запросы работают, я думаю, что это должно помочь вам указать правильное направление. Это основано на удалении части проблемы, пока она не работает. Затем добавьте их обратно, пока они не сломаются, чтобы указать, где проблема. Эти шаги должны быть выполнены с использованием небольших добавочных изменений, чтобы минимизировать проблемное пространство.


Обновление:

Основываясь на новой информации, давайте попробуем разделить новую информацию на новые вопросы, на которые нам нужно ответить.

Возможно, выражение LINQ не может понять, что делать с work._context.VolumeAdditive в предложении where. Итак, давайте проверим эту теорию, используя следующее. Это устанавливает контекст для одной переменной вместо использования work._context.

var _context = work._context;
var query = from m in _context.Metric
           where !(from v in _context.VolumeAdditive
                   where v.AdditivesID == AdditivesID
                   select v.MetricID).Contains(m.ID)
           select m;

Возможно, использование оператора let для определения MetricID может решить эту проблему.

var metrics =
    from m in work.MetricRepository.All
    let volumes = from v in work.VolumeAdditiveRepository.All
             where v.AdditivesID == AdditivesID
             select v.MetricID
    where !volumes.Contains(m.ID)
    select m;

На основании результатов этих тестов, а также смешивания и сопоставления предыдущих 3 тестов / вопросов мы должны приблизиться к ответу. Когда я сталкиваюсь с подобными вопросами, я стараюсь задавать себе вопросы с поддающимися проверке ответами. По сути, я пытаюсь использовать научный метод, чтобы сузить проблему и найти решение.

...