Почему этот оператор LINQ join не работает?
У меня есть этот LINQ-запрос:
// types...
LinkedList<WeightedItem> itemScores = new LinkedList<WeightedItem>();
var result = from i in _ctx.Items
join s in itemScores on i.Id equals s._id
orderby s._score descending
select new ItemSearchResult(i, s._score);
// this fails:
return result.ToList();
, который генерирует эту ошибку:
не удалось создать постоянное значение типа System.Коллекции.Родовой.IEnumerable '1'.
Только примитивные типы ('такие как int32, String и GUID') поддерживаются в этом контексте.
[EDIT] вот код WeightedItem
:
public class WeightedItem
{
public int _id;
public decimal? _score;
public WeightedItem(int id, decimal? score)
{
_id = id;
_score = score;
}
}
вы видите, что я сделал не так? Код компилируется отлично, и оба _ctx.Элементы и itemScores содержит правильные значения.
3 ответов
Да, он будет компилироваться нормально - проблема в том, что он не может перевести ее в SQL. Когда вы ссылаетесь на" локальные " значения, Entity framework должен решить, что с ними делать, когда ему нужно создать SQL-запрос. Он в основном не может справиться с выполнением соединения между коллекцией в памяти и таблицей базы данных.
одно может работа будет использовать Contains
вместо. Я не знаю,LinkedList<T>
будет работать для этого, но я верю List<T>
делает, по крайней мере в LINQ to SQL:
List<int> requiredScoreIds = itemScores.Select(x => x._id).ToList();
var tmp = (from i in _ctx.Items
where requiredScoreIds.Contains(i.Id)
orderby s._score descending
select i).AsEnumerable();
// Now do the join in memory to get the score
var result = from i in tmp
join s in itemScores on i.Id equals s._id
select new ItemSearchResult(i, s._score);
теперь это делает соединение в запросе в памяти, что несколько ненужно. Вместо этого вы можете использовать словарь:
List<int> requiredScoreIds = itemScores.Select(x => x._id).ToList();
var tmp = (from i in _ctx.Items
where requiredScoreIds.Contains(i.Id)
orderby s._score descending
select i).AsEnumerable();
// Create a map from score ID to actual score
Dictionary<int, decimal?> map = itemScores.ToDictionary(x => x._id,
x => x._score);
var result = tmp.Select(i => new ItemSearchResult(i, map[i.Id]));
вы не можете вступить между собой в памяти, и объекта, поддерживающую запросы. Вам нужно сделать что-то вроде этого:
var criteria = itemScores.Select(x => x._id).ToList();
var result_tag = (from i in _ctx.Items
where criteria.Contains(i.ID)
select i).ToList();
var result = from i in result_tag
join s in itemScores on i.ID equals s._id
orderby s._score descending
select new ItemSearchResult(i, s._score);
на всякий случай таблица, представленная _ctx.Элементы не большой один, и вы не заботитесь о загрузке вся таблица в памяти, а затем отфильтровать его в памяти, вы можете просто поменять порядок элементов в оператор join, как в следующем фрагменте:
LinkedList<WeightedItem> itemScores = new LinkedList<WeightedItem>();
var result = from s in itemScores
join i in _ctx.Items on s._id equals i.Id
orderby s._score descending
select new ItemSearchResult(i, s._score);
return result.ToList();
в исходном операторе был вызван метод расширения Queryable:
IQueryable<TResult> Queryable.Join<TOuter, TInner, TKey, TResult>(
this IQueryable<TOuter> outer,
IEnumerable<TInner> inner,
Expression<Func<TOuter, TKey>> outerKeySelector,
Expression<Func<TInner, TKey>> innerKeySelector,
Expression<Func<TOuter, TInner, TResult>> resultSelector
)
в то время как в замененном вызывается метод перечисляемого расширения:
IEnumerable<TResult> Enumerable.Join<TOuter, TInner, TKey, TResult>(
this IEnumerable<TOuter> outer,
IEnumerable<TInner> inner,
Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, TInner, TResult> resultSelector
)
в последние заявление на полный _ctx.Таблицы загружается в память, а затем присоединился к, через Linq к объектам, в список itemScores (я не знаю о LinkedList, я пробовал его со списком).
я добавил этот ответ в основном потому, что кто-то может тип соединения в обратном порядке и она работайте, даже не осознавая, что произойдет в базе данных.
Я бы не предложил присоединиться таким образом, хотя это может быть полезно для приложений backoffice, когда задействованные таблицы состоит из нескольких записей, и приложение не страдает от ухудшения производительности. Это решение, в конце концов, сохраняет код более чистым.