【问题标题】:How to filter IQueryable by a new field如何按新字段过滤 IQueryable
【发布时间】:2021-07-04 04:48:59
【问题描述】:

仅当来自前端的参数过滤器为真时,我必须通过 DocumentsUnread > 0 设置过滤器,否则我从查询中返回所有 EmployeeListItem。 DocumentUnread 字段是由其他两个字段相减生成的,它不存在于表 DB 中,而 DocumentCount 是通过 Count 方法生成的。

public async Task<EmployeeList> GetEmployeesDownloadStatus(int pageNumber, string searchText, bool filter)
{
    int pageSize = 10;

    IQueryable<EmployeeListItem> employees = 
        from e in context.Employees
        join u in context.Users on e.EmployeeId equals u.Employee.EmployeeId
        join d in context.EmployeeDocuments on e.EmployeeId equals d.EmployeeId into dj
        from d in dj.DefaultIfEmpty()

        where 
        string.IsNullOrWhiteSpace(searchText) ||
            (!string.IsNullOrWhiteSpace(searchText) && (e.FirstName).Contains(searchText)) ||
            (!string.IsNullOrWhiteSpace(searchText) && (e.LastName).Contains(searchText)) ||
            (!string.IsNullOrWhiteSpace(searchText) && (e.FiscalCode).Contains(searchText)) ||
            (!string.IsNullOrWhiteSpace(searchText) && (e.IdentificationNumber).Contains(searchText))
        group new { e, d, u } by new { e.EmployeeId, e.FirstName, e.LastName, e.FiscalCode, e.IdentificationNumber, e.EmploymentStartDate, u.LastAccessDate } into g
        orderby g.Key.FirstName, g.Key.LastName


        select new EmployeeListItem
        {
            EmployeeId = g.Key.EmployeeId,
            FirstName = g.Key.FirstName,
            LastName = g.Key.LastName,
            FiscalCode = g.Key.FiscalCode,
            IdentificationNumber = g.Key.IdentificationNumber,
            EmploymentStartDate = g.Key.EmploymentStartDate.ToString("dd/MM/yyyy"),
            DocumentCount = g.Count(),
            DocumentOpened = g.Sum(s => s.d.DownloadedByEmployee ? 1 : 0),
            DocumentsUnread = g.Count() - g.Sum(s => s.d.DownloadedByEmployee ? 1 : 0),
            LastAccessDate = g.Key.LastAccessDate
        };

    decimal total = Convert.ToDecimal(employees.Count()) / Convert.ToDecimal(pageSize);

    EmployeeList EmployeeList = new BusinessLogic.Dto.EmployeeList
    {
        TotalPagesNumber = decimal.ToInt32(Math.Ceiling(total)),
        Employees = await employees.Skip(pageNumber * pageSize).Take(pageSize).ToListAsync()
    };
    return EmployeeList;
}

我尝试在 Skip 方法之前使用.Where(item =&gt; item.DocumentsUnread &gt; 0).,但出现此错误

The LINQ expression 'DbSet<Employee>()
    .Join(
        inner: DbSet<User>()
            .LeftJoin(
                inner: DbSet<Employee>(), 
                outerKeySelector: u => EF.Property<Nullable<int>>(u, "EmployeeId"), 
                innerKeySelector: e0 => EF.Property<Nullable<int>>(e0, "EmployeeId"), 
                resultSelector: (o, i) => new TransparentIdentifier<User, Employee>(
                    Outer = o, 
                    Inner = i
                )), 
        outerKeySelector: e => e.EmployeeId, 
        innerKeySelector: u => u.Inner.EmployeeId, 
        resultSelector: (e, u) => new TransparentIdentifier<Employee, TransparentIdentifier<User, Employee>>(
            Outer = e, 
            Inner = u
        ))
    .LeftJoin(
        inner: DbSet<EmployeeDocument>(), 
        outerKeySelector: ti => ti.Outer.EmployeeId, 
        innerKeySelector: e1 => e1.EmployeeId, 
        resultSelector: (ti, e1) => new TransparentIdentifier<TransparentIdentifier<Employee, TransparentIdentifier<User, Employee>>, EmployeeDocument>(
            Outer = ti, 
            Inner = e1
        ))
    .Where(ti0 => False || True && ti0.Outer.Outer.FirstName.Contains(__searchText_0) || True && ti0.Outer.Outer.LastName.Contains(__searchText_0) || True && ti0.Outer.Outer.FiscalCode.Contains(__searchText_0) || True && ti0.Outer.Outer.IdentificationNumber.Contains(__searchText_0))
    .GroupBy(
        keySelector: ti0 => new { 
            EmployeeId = ti0.Outer.Outer.EmployeeId, 
            FirstName = ti0.Outer.Outer.FirstName, 
            LastName = ti0.Outer.Outer.LastName, 
            FiscalCode = ti0.Outer.Outer.FiscalCode, 
            IdentificationNumber = ti0.Outer.Outer.IdentificationNumber, 
            EmploymentStartDate = ti0.Outer.Outer.EmploymentStartDate, 
            LastAccessDate = ti0.Outer.Inner.Outer.LastAccessDate
         }, 
        elementSelector: ti0 => new { 
            e = ti0.Outer.Outer, 
            d = ti0.Inner, 
            u = ti0.Outer.Inner.Outer
         })
    .OrderBy(e2 => e2.Key.FirstName)
    .ThenBy(e2 => e2.Key.LastName)
    .Select(e2 => new EmployeeListItem{ 
        EmployeeId = e2.Key.EmployeeId, 
        FirstName = e2.Key.FirstName, 
        LastName = e2.Key.LastName, 
        FiscalCode = e2.Key.FiscalCode, 
        IdentificationNumber = e2.Key.IdentificationNumber, 
        EmploymentStartDate = e2.Key.EmploymentStartDate.ToString("dd/MM/yyyy"), 
        DocumentCount = e2
            .Count(), 
        DocumentOpened = e2
            .Sum(s => s.d.DownloadedByEmployee ? 1 : 0), 
        DocumentsUnread = e2
            .Count() - e2
            .Sum(s => s.d.DownloadedByEmployee ? 1 : 0), 
        LastAccessDate = e2.Key.LastAccessDate 
    }
    )
    .Where(e3 => e3.DocumentsUnread > 0)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

我尝试使用 EmployeeList 中的 remove 方法,但这会导致分页出现问题。

我应该翻译成 linq 这个:

having COUNT(*) - COALESCE(SUM(CASE
    WHEN [e1].[DownloadedByEmployee] = CAST(1 AS bit) THEN 1
    ELSE 0
END), 0)>0

我对其进行了测试并且工作正常,但我不知道如何对 linq 做同样的事情。

感谢您的帮助,我希望我正确地解释了问题

【问题讨论】:

  • 您绝对可以在IQueryable&lt;T&gt; 上应用Where 扩展方法,检查您的使用部分,可能缺少一些东西。
  • 这是实体框架吗?哪个版本?
  • 是的,Microsoft.EntityFrameworkCore 版本 5.0.1
  • 尝试在投影前加过滤器,可能EF翻译不出来

标签: c# linq iqueryable ef-core-5.0


【解决方案1】:

您可以使用.where(item =&gt; !filter || item.DocumentsUnread &gt; 0)

如果过滤器参数为假,item.DocumentsUnread &gt; 0 将不会被检查。

【讨论】:

  • EmployeeList EmployeeList = new BusinessLogic.Dto.EmployeeList { TotalPagesNumber = decimal.ToInt32(Math.Ceiling(total)), Employees = await employees.where(item => !filter || item.DocumentsUnread > 0).Skip(pageNumber * pageSize).Take(pageSize).ToListAsync() };像这样?
  • 遗憾的是它返回相同的错误。谢谢
  • 尝试在组后添加where子句:where !filter || g.Count() - g.Sum(s =&gt; s.d.DownloadedByEmployee ? 1 : 0) &gt; 0
【解决方案2】:

感谢您的帮助! 我是这样解决的:

        public async Task<EmployeeList> GetEmployeesDownloadStatus(int pageNumber, string searchText, bool showOnlyDocumentsUnread)
        {
            int pageSize = 10;

            IQueryable<EmployeeListItem> employees = from e in context.Employees
                                                     join u in context.Users on e.EmployeeId equals u.Employee.EmployeeId
                                                     join d in context.EmployeeDocuments on e.EmployeeId equals d.EmployeeId into dj
                                                     from d in dj.DefaultIfEmpty()
                                                     group new { e, d, u } by new { e.EmployeeId, e.FirstName, e.LastName, e.FiscalCode, e.IdentificationNumber, e.EmploymentStartDate, u.LastAccessDate } into g
                                                     where string.IsNullOrWhiteSpace(searchText) ||
                                                           (!string.IsNullOrWhiteSpace(searchText) && (g.Key.FirstName).Contains(searchText) ||
                                                           (g.Key.LastName).Contains(searchText) ||
                                                           (g.Key.FiscalCode).Contains(searchText) ||
                                                           (g.Key.IdentificationNumber).Contains(searchText)) &&
                                                           (!showOnlyDocumentsUnread || g.Count() - g.Sum(s => s.d.DownloadedByEmployee ? 1 : 0) > 0)
                                                     orderby g.Key.FirstName, g.Key.LastName
                                                     select new EmployeeListItem
                                                     {
                                                         EmployeeId = g.Key.EmployeeId,
                                                         FirstName = g.Key.FirstName,
                                                         LastName = g.Key.LastName,
                                                         FiscalCode = g.Key.FiscalCode,
                                                         IdentificationNumber = g.Key.IdentificationNumber,
                                                         EmploymentStartDate = g.Key.EmploymentStartDate.ToString("dd/MM/yyyy"),
                                                         DocumentCount = g.Count(),
                                                         DocumentOpened = g.Sum(s => s.d.DownloadedByEmployee ? 1 : 0),
                                                         DocumentsUnread = g.Count() - g.Sum(s => s.d.DownloadedByEmployee ? 1 : 0),
                                                         LastAccessDate = g.Key.LastAccessDate
                                                     };

                decimal total = Convert.ToDecimal(employees.Count()) / Convert.ToDecimal(pageSize);
                EmployeeList EmployeeList = new BusinessLogic.Dto.EmployeeList
                {
                    TotalPagesNumber = decimal.ToInt32(Math.Ceiling(total)),
                    Employees = await employees.Skip(pageNumber * pageSize).Take(pageSize).ToListAsync()
                };
                return EmployeeList;
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-20
    • 1970-01-01
    • 2016-06-15
    • 1970-01-01
    • 1970-01-01
    • 2015-07-23
    • 2014-08-30
    • 2017-06-08
    相关资源
    最近更新 更多