Первое, на что нужно обратить внимание, - это то, что предложенный вами запрос не может быть полностью оценен как SQL, поскольку для коллекции, содержащей не примитивные значения, нет эквивалента SQL, tagSearchValues
.Это заставляет EF автоматически переключаться на оценку на стороне клиента .То есть он извлекает в память все сущности, которые удовлетворяют условию stuffFilter
, и все их теги, а затем применяет предикат тегов.Это, очевидно, неэффективно.
Во-вторых, запрос неточный.Объекты, содержащие теги с определенными ключами и теги с определенными значениями, не совпадают с тегами, содержащими определенные комбинации ключ / значение .Требуется запрос, соответствующий каждой комбинации, например:
db.MainEntities.Where(...)
.Where(m => tagSearchValues
.Any(t => m.Tags.Any(mt => mt.Key == t.Key
&& mt.Value == t.Value)))
Однако, если вы это сделаете, EF снова превратится в неэффективную оценку на стороне клиента, и вам даже придется применить Include
илиленивая загрузка себя, чтобы вытащить теги в память.(Более того, по какой-то причине EF запускает тонны избыточных запросов).
Дело в том, что EF (как и другие ORM) плохо ориентируется на такие парные сравнения на стороне сервера.Поэтому вам нужен построитель предикатов для построения предикатов тегов.Есть несколько предикатов, например, в Linqkit .Я использую этот , потому что это красиво и просто.Рецепт таков: создайте предикат и примените его в Where()
:
var tagPredicate = PredicateBuilder.True<MainEntity>();
if (tagSearchValues.Any())
{
tagPredicate = PredicateBuilder.False<MainEntity>();
foreach (var tag in tagSearchValues)
{
tagPredicate = tagPredicate.Or(m => m.Tags
.Any(t => t.Key == tag.Key
&& t.Value == tag.Value));
}
}
var query = _dbContext.MainEntities
.Where(m => string.IsNullOrWhiteSpace(stuff) || m.Stuff == stuff)
.Where(tagPredicate);
... // Use query
Я использую Or
, потому что я предполагаю (из вашего запроса), что вы хотите, чтобы сущности имели any тег в поисковых тегах.Вот почему я начинаю с предиката PredicateBuilder.True
, поэтому запрос будет возвращать результаты, если нет поисковых тегов, аналогичных вашему исходному запросу.