【问题标题】:Write WHERE conditions with Enum values using AsQueryable in Entity Framework在实体框架中使用 AsQueryable 编写带有枚举值的 WHERE 条件
【发布时间】:2021-02-22 16:40:48
【问题描述】:

为警告生成错误

'Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning: 无法翻译 LINQ 表达式 'where Convert([p].Status, Enum).GetDisplayName().Contains(__searchBy_8)' 并将在本地进行评估。'。可以通过传递事件 ID 抑制或记录此异常 'RelationalEventId.QueryClientEvaluationWarning' 到 'DbContext.OnConfiguring' 或 'AddDbContext' 中的 'ConfigureWarnings' 方法。

我正在将状态枚举的值保存到数据库表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 && dtParams.EndDate != null)
{
    query = query.Where(s => s.CreatedAt >= dtParams.StartDate.Value.Date && 
                             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 &&  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.QueryClientEvaluationWarning:
生成错误 无法翻译 LINQ 表达式 'where Convert([p].Status, Enum).GetDisplayName().Contains(__searchBy_8)' 并将在本地进行评估。'。通过将事件 ID 'RelationalEventId.QueryClientEvaluationWarning' 传递给 'DbContext.OnConfiguring' 或 'AddDbContext' 中的 'ConfigureWarnings' 方法,可以抑制或记录此异常。

导致此异常的行是我将搜索文本与枚举值进行比较的地方

 (r.Status.GetDisplayName().Contains(searchBy))
 
 

如果有人正在搜索文本 Resolved ,我如何将枚举的显示名称与搜索字符串进行比较,我必须从 db 中获取 Status Resolved 的所有记录

【问题讨论】:

    标签: .net entity-framework linq enums


    【解决方案1】:

    出现此警告是因为 GetDisplayName() 是在您的代码中实现的自定义方法,无法被数据库访问或转换为任何类型的 sql 语句。因此 EF 需要从数据库中加载所有实体并在内存中执行过滤(您应该避免这种情况,因为对数据库进行过滤要快得多)。

    您需要将用户搜索的术语“逆向工程”为实际枚举值,例如像这样

    var filteredStatus = Enum.GetValues<Status>()
                             .Where(value => value.GetDisplayName().Contains(searchBy))
                             .ToList();
    
    

    然后在您的查询中而不是使用r.Status.GetDisplayName().Contains(searchBy) 使用filteredStatus.Contains(r.Status)

    【讨论】:

    • 这里 Enum.GetValues 是一个数组,不知道如何将 Where 应用于数组。我在 Where 所以我添加了如下所示的编译异常并立即尝试此解决方案 Enum.GetValues(typeof(Status)).Cast().Where(v
    • @Mat 很抱歉,我使用了错误的 GetValues() 重载 - 我已将答案更新为正确的答案
    • 我们可以将这种逆向工程应用到子类枚举吗? r.Messages.Any(p => p.Status.GetDisplayName().Contains(searchBy) )
    • @Mat 我没有看到您在原始问题的评论中提到的代码,因此我没有上下文 - 但一般来说,这个解决方案也适用于其他子类枚举跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-13
    相关资源
    最近更新 更多