linq .Where() 问题 "Type of condition expression cannot be determined"

linq .Where() issue "Type of condition expression cannot be determined"

我希望能够根据字符串是否为 null 在 Linq 查询中传递不同的变量。

string site = null;
int q = a number;

var data = db.tbl_table12345
             .Where(site == null
                      ? d => d.stuff_id == org 
                             && d.Date.Month == q
                             && d.Date.Year == year 
                             && d.Q1 != (int?)null
                      : d => d.stuff_id == org
                             && d.Service == site
                             && d.Date.Month == q 
                             && d.Date.Year == year
                             && d.Q1 != (int?)null)
             .GroupBy(d => d.Q1)
             .Select(d => new
                          {
                              q1 = d.Key,
                              total = d.Count()
                          });

所以在上面的例子中,如果 site == null 那么我们执行一个没有 d.Service == site 参数的 .Where 搜索。否则除了查询的其余部分之外,还会使用服务参数。这可能吗?

你的语法错误

 .Where(d => site == null 
          ? d.stuff_id == org 
                 && d.Date.Month == q && d.Date.Year == year 
                 && d.Q1 != (int?)null
          : d.stuff_id == org
                 && d.Service == site && d.Date.Month == q 
                 && d.Date.Year == year && d.Q1 != (int?)null)

如果三元运算符是 "inside" lambda,它应该可以工作。

string site = null;
int q = a number;

var data = db.tbl_table12345
             .Where(d => site == null 
                    ? d.stuff_id == org 
                      && d.Date.Month == q 
                      && d.Date.Year == year 
                      && d.Q1 != (int?)null 
                    : d.stuff_id == org 
                      && d.Service == site 
                      && d.Date.Month == q 
                      && d.Date.Year == year 
                      && d.Q1 != (int?)null)
             .GroupBy(d => d.Q1)
             .Select(d => new 
                    { 
                         q1 = d.Key,
                         total = d.Count() 
                    }); 

分解表达式可以让您更好地形象化您的逻辑。

string site = null;
int month = a number;

Expression<Func<SomeType, bool>> nullExpression =
    d => d.stuff_id == org 
         && SqlFunctions.DatePart("MONTH", d.Date) == month
         && SqlFunctions.DatePart("YEAR", d.Date) == year
         && d.Q1 != (int?)null;

Expression<Func<SomeType, bool>> notNullExpression =
    d => d.stuff_id == org
         && SqlFunctions.DatePart("MONTH", d.Date) == month
         && SqlFunctions.DatePart("YEAR", d.Date) == year
         && d.Q1 != (int?)null
         && d.Service == site;

var expression = site == null ? nullExpression : notNullExpression

var data = db.tbl_table12345
             .Where(expression)
             .GroupBy(d => d.Q1)
             .Select(d => new { q1 = d.Key, total = d.Count() });

或使用表达式树:

var expression = BuildWhere(org, month, year, site);
var data = db.tbl_table12345
         .Where(expression)
         .GroupBy(d => d.Q1)
         .Select(d => new { q1 = d.Key, total = d.Count() });

这里有一个方法可以建立你的 where Expression<Func<SomeType, bool>.

public Expression BuildWhere(int org, int month, int year, string service = null)
{
    var datePartMethod =
        typeof(SqlFunctions)
            .GetMethod("DatePart",
                       new[]
                       {
                           typeof(string),
                           typeof(DateTime?)
                       });

    // Variable d
    var variable =
        Expression.Variable(typeof(SomeType));

    var orgConstant =
        Expression.Constant(org);
    // d.stuff_id
    var stuffId =
        Expression.Property(variable, "stuff_id");

    // d.stuff_id == org
    var stuffIdEquals =
        Expression.Equal(stuffId, orgConstant);

    // d.Date cast into Nullable DateTime
    var date =
        Expression.Convert(
            Expression.Property(variable, "Date"),
            typeof(DateTime?));

    var monthPartConstant =
        Expression.Constant("MONTH");
    // month cast to nullable int
    var monthConstant =
        Expression.Convert(
            Expression.Constant(month),
            typeof(int?));
    var yearPartConstant =
        Expression.Constant("YEAR");
    // year cast to nullable int
    var yearConstant =
        Expression.Convert(
            Expression.Constant(year),
            typeof(int?));

    // SqlFunctions.DatePart("MONTH", d.Date)
    var invokeDatePartMonthPart =
        Expression.Call(
            datePartMethod,
            monthPartConstant,
            date);
    // SqlFunctions.DatePart("YEAR", d.Date)
    var invokeDatePartYearPart =
        Expression.Call(
            datePartMethod,
            yearPartConstant,
            date);

    // SqlFunctions.DatePart("MONTH", d.Date) == month
    var dateMonthEquals =
        Expression.Equal(
            invokeDatePartMonthPart,
            monthConstant);

    // SqlFunctions.DatePart("MONTH", d.Date) == year
    var dateYearEquals =
        Expression.Equal(
            invokeDatePartYearPart,
            yearConstant);

    // d.Q1
    var q1 = Expression.Property(variable, "Q1");
    var nullConstant =
        Expression.Constant((int?) null);
    // d.Q1 != (int?) null
    var q1NotEquals =
        Expression.NotEqual(
            q1,
            nullConstant);

    // d.stuff_id == org
    // && SqlFunctions.DatePart("MONTH", d.Date) == month
    // && SqlFunctions.DatePart("YEAR", d.Date) == year
    // && d.Q1 != (int?) null
    var andExpression = 
        Expression.AndAlso(stuffIdEquals,
            Expression.AndAlso(dateMonthEquals,
            Expression.AndAlso(dateYearEquals,
                q1NotEquals)));

    // Add d.Service only when not null
    if(service != null)
    {
        // d.Service
        var serviceConstant =
            Expression.Constant(service);
        var serviceProperty =
            Expression.Property(
                variable,
                "Service");

        // d.Service == service
        var serviceEquals =
            Expression.Equal(
                serviceProperty,
                serviceConstant);

        andExpression =
            Expression.AndAlso(
                andExpression,
                serviceEquals);
    }

    // Creates a lambda to represent the logic
    var parameter = Expression.Parameter(typeof(SomeType));
    return Expression
        .Lambda<Func<SomeType, bool>>(
            andExpression,
            parameter);
}

如果您想在满足条件时向查询添加额外的过滤器,那么该构造应该在查询本身之外,LINQ 可以很容易地做到这一点:

var query = db.tbl_table12345
    .Where(d => d.stuff_id == org
                    && d.Date.Month == q
                    && d.Date.Year == year
                    && d.Q1 != (int?)null);
if (site != null)
    query = query.Where(d => d.Service == site);
var data = query.GroupBy(d => d.Q1)
    .Select(d => new
    {
        q1 = d.Key,
        total = d.Count()
    });

从代码的外观来看,我认为您正在使用 Entity Framework 进行查询。如果是这样,则不允许使用 d.Date.Month 之类的内容,因为 EF 本身不知道如何将其正确转换为 SQL。您需要使用 SqlFunctions class(特别是 DatePart 方法)才能使此查询正常工作。使用@Servy 的解决方案作为开始:

var query = db.tbl_table12345
    .Where(d => d.stuff_id == org
             && SqlFunctions.DatePart("MONTH", d.Date) == q
             && SqlFunctions.DatePart("YEAR", d.Date) == year
             && d.Q1 != (int?)null);
if (site != null)
    query = query.Where(d => d.Service == site);

var data = query.GroupBy(d => d.Q1)
    .Select(d => new
    {
        q1 = d.Key,
        total = d.Count()
    });

使用这种方法的另一个很好的理由是上面的所有 LINQ 子句(WhereGroupBySelect)都采用延迟执行(参见 here 用于延迟执行与立即执行方法的列表),这意味着只有一个查询将发送到您的数据库以进行最终 data,并且仅当您以某种方式实际使用该变量时。