Перечисления основного фильтра EF хранятся в виде строки с оператором LIKE

#c# #.net-core #entity-framework-core #ef-core-3.1

#c# #.net-core #entity-framework-core #ef-core-3.1

Вопрос:

У меня есть Person модель с Gender папкой перечисления, которая хранится в виде строки в базе данных. Я хочу сделать запрос для фильтрации данных по подстроке пола. Например, если query.SearchLike это "Fe" или "em" , я бы хотел вернуть всех лиц женского пола. К сожалению, приведенный ниже код выдает исключение.

 builder.Entity<Person>().Property(x => x.Gender).HasConversion<string>();
  
 public async Task<IList<Person>> ListAsync(PersonsQuery query)
{
  IQueryable<Person> queryable = _context.Persons.AsNoTracking();

  return await queryable
    .Where(x => x.Gender.ToString().Contains(query.SearchLike))
    .ToListAsync();
}
  

Исключение:

Выражение LINQ ‘DbSetr n .Где(x => x.Пол.Не удалось перевести toString().Contains(__query_SearchLike_0))’. Либо перепишите запрос в форме, которая может быть переведена, либо переключитесь на оценку клиента явно, вставив вызов AsEnumerable() , AsAsyncEnumerable() , ToList() или ToListAsync() . См. https://go.microsoft.com/fwlink/?linkid=2101038 для получения дополнительной информации.

Комментарии:

1. что является исключением?

2. В этом проблема с преобразованиями. Они делают фильтрацию практически невозможной.

3. И что может быть практическим решением?

4. Сохраняйте Gender как int вместо строки. Сопоставьте поиск с перечислением и найдите все Person s, соответствующие любым значениям перечисления, соответствующим вашему поиску. Это тоже должно быть намного более производительным

5. Почему вы выполняете преобразование? Просто определите свои перечисления как строковые свойства. Если вы хотите иметь строковые значения. в противном случае для повышения производительности и даже для других целей лучше хранить его в базе данных в виде целых чисел.

Ответ №1:

Насколько я понимаю, вы должны использовать функции, подобные EF. Попробуйте что-то вроде этого:

 public async Task<IList<Person>> ListAsync(PersonsQuery query)
{
  IQueryable<Person> queryable = _context.Persons.AsNoTracking();

  return await queryable
    .Where(x => EF.Functions.Like(x.Gender, "%"   query.SearchLike   "%")
    .ToListAsync();
}
  

Комментарии:

1. EF.Functions.Like() ожидается два строковых параметра, но x.Gender является перечислением. Когда я вызываю x.Gender.ToString() , то получаю исключение, в котором говорится: «Выражение LINQ не удалось перевести».

Ответ №2:

Я нашел решение. Сначала нужно привести перечисление к object , а затем к string .

 public async Task<IList<Person>> ListAsync(PersonsQuery query)
{
  IQueryable<Person> queryable = _context.Persons.AsNoTracking();

  return await queryable
    .Where(x => ((string)(object)x.Gender).Contains(query.SearchLike))
    .ToListAsync();
}
  

Связанный вопрос в репозитории Github ядра EF.

Ответ №3:

Вы можете использовать FromSql метод для выполнения необработанного оператора SQL

 public async Task<IList<Person>> ListAsync(PersonsQuery query)
{    
    var param = $"%{query.SearchLike}%"
    return await _context.Persons
        .FromSql("SELECT * FROM Persons WHERE Gender LIKE {0}" , param)
        .ToListAsync();
}