#.net #entity-framework #linq #enums
#.net #entity-framework #linq #перечисления
Вопрос:
Ошибка, сгенерированная для предупреждения
‘Microsoft.EntityFrameworkCore.Query.Предупреждение о проверке запроса: выражение LINQ ‘where Convert([p].Status, Enum).getDisplayName().Содержит(__searchBy_8)’не удалось перевести и будет оцениваться локально.’. Это исключение можно подавить или зарегистрировать, передав идентификатор события ‘RelationalEventId.QueryClientEvaluationWarning’ в метод ‘ConfigureWarnings’ в ‘DbContext.OnConfiguring’ или ‘AddDbContext’.
Я сохраняю значение status enum в таблице базы данных Complaints
public enum Status
{
Pending = 0,
Error = 1,
Cancelled = 2,
Delayed = 3,
Resolved = 4
}
Я должен применить фильтрацию к запросу по строке поиска вместе с некоторой другой фильтрацией, и я пытаюсь сделать это следующим образом
// Apply Relevant SearchModel filters first
var query = context.Complaints
.Include(s => s.Messages)
.ThenInclude(p => p.User).AsQueryable();
if (dtParams.StartDate != null amp;amp; dtParams.EndDate != null)
{
query = query.Where(s => s.CreatedAt >= dtParams.StartDate.Value.Date amp;amp;
s.CreatedAt <= dtParams.EndDate.Value.Date);
}
string searchBy = dtParams.Search?.Value;
if (!string.IsNullOrEmpty(searchBy))
{
query = query.Where(r => r.ComplaintNo.Contains(searchBy) ||
r.CreatorUsername.Contains(searchBy) ||
(r.CreatedAt != null amp;amp; r.CreatedAt.ToString().Contains(searchBy)) ||
(r.Status.GetDisplayName().Contains(searchBy)) ||
r.Messages.Any(p => p.StatusDescription.Contains(searchBy))
);
}
// Convert the Db context to Custom ViewModel which will then be rendered on to a DataTable
var dtQuery = query.SelectMany(x => x.Messages, (complaint, message) => new { complaint, message })
.Select(p => new ListTableViewModel
{
ComplaintNo = $"<a href="{Url.Action("GetLabels", new { orderNo = p.complaint.ComplaintNo })}" target="_blank"> {p.complaint.ComplaintNo}</a>",
Tracking = GenerateTrackingUrl(p.complaint),
Creator = p.complaint.CreatorUsername,
CreatedAt = p.complaint.CreatedAt.ToString("dd/MM/yy"),
Status = $"<span class="badge label-{p.complaint.Status}">{p.complaint.Status.GetDisplayName()}</span>",
Info = p.message.StatusDescription
}).ToList();
Я получал исключение, подобное приведенному ниже
Ошибка, сгенерированная для предупреждения «Microsoft.EntityFrameworkCore.Query.Предупреждение о проверке запроса:
Выражение LINQ ‘where Преобразовать([p].Статус, перечисление).getDisplayName().Содержит(__searchBy_8)’не удалось перевести и будет оцениваться локально.’. Это исключение можно подавить или зарегистрировать, передав идентификатор события ‘RelationalEventId.QueryClientEvaluationWarning’ в метод ‘ConfigureWarnings’ в ‘DbContext.OnConfiguring’ или ‘AddDbContext’.
Строка, вызывающая это исключение, — это то, где я сравниваю текст поиска со значением enum
(r.Status.GetDisplayName().Contains(searchBy))
Как я могу сравнить отображаемое имя перечисления со строкой поиска, если кто-то ищет разрешенный текст, я должен извлечь все записи о состоянии, разрешенные из БД
Ответ №1:
Это предупреждение возникает потому, что getDisplayName() — это пользовательский метод, реализованный в вашем коде, к которому база данных не может получить доступ или преобразовать в какой-либо оператор sql. Поэтому EF необходимо загрузить все объекты из базы данных и выполнить фильтрацию в памяти (чего следует избегать, поскольку фильтрация в базе данных выполняется намного быстрее).
Что вам нужно, так это «перепроектировать» термин, который искал пользователь, для фактического значения enum, например, вот так
var filteredStatus = Enum.GetValues<Status>()
.Where(value => value.GetDisplayName().Contains(searchBy))
.ToList();
а затем в вашем запросе вместо использования r.Status.GetDisplayName().Contains(searchBy)
use filteredStatus.Contains(r.Status)
Комментарии:
1. Здесь Enum.getValues — это массив, и я не уверен, как мы можем применить Where к массиву. Я получаю исключение компиляции в Where, поэтому я добавил, как показано ниже, и теперь пытаюсь это решение Enum.getValues(typeof(Status)). Приведение <Status>().Где(v
2. @Mat Прошу прощения, что я использовал неправильную перегрузку getValues() — я обновил свой ответ до правильного
3. Можем ли мы применить это обратное проектирование и к перечислениям дочерних классов? r.Messages. Любой (p => p.Status. getDisplayName().Содержит(searchBy) )
4. @Mat Я не вижу кода, который вы упоминаете в своем комментарии в своем первоначальном вопросе, и поэтому у меня нет контекста, но в целом да, это решение будет работать и для других перечислений дочерних классов