【问题标题】:LINQ - select by property nameLINQ - 按属性名称选择
【发布时间】:2020-11-03 04:00:57
【问题描述】:

我有以下课程:

public class User
{
    public string userName { get; set; }
    public bool active { get; set; }
}

示例:

{
   userName: John,
   active: true
},
{
   userName: Mary,
   active: true
}

如何通过属性名称查询“活跃”用户? 这是我尝试过的,但我得到了:

“无法翻译 LINQ 表达式 ...。要么以可翻译的形式重写查询,要么通过插入对 AsEnumerable()、AsAsyncEnumerable() 的调用显式切换到客户端评估, ToList() 或 ToListAsync()"

Type t = typeof(User);
PropertyInfo p = t.GetProperty("active");

int totalActive = userContext.Users.Where(u => Convert.ToBoolean(p.GetValue(u)) == true).Count();

【问题讨论】:

  • 你不知道编译时属性的名称吗?这是为什么?无论如何,客户评估应该按照消息的建议进行(例如,userContext.Users.ToList().Where(...))。
  • 第一个 active 不是属性...它是一个字段。 GetProperty 找不到。第二...为什么ut是同一类型时需要反射。
  • @LegacyCode - 嗯,它是一个自动属性。
  • @MineKrafter 这不是我回复它的时候。 janzen 将其更改为自动属性。

标签: c# entity-framework linq asp.net-core


【解决方案1】:

如果由于某种原因您需要动态执行此操作,则需要手动构建 expression tree(检查 Queryable.Where 签名):

public class User
{
    public string userName {get;set;}
    public bool active {get;set;}
}

Type t = typeof(User);
PropertyInfo p = t.GetProperty("active");

var prmtr = Expression.Parameter(t);
var value = Expression.Constant(true);
var comprasion = Expression.Equal(Expression.Property(prmtr, p), value);
var expr = Expression.Lambda<Func<User, bool>>(comprasion, prmtr);

int totalActive = userContext.Users.Where(expr).Count();

【讨论】:

    【解决方案2】:

    正如错误所说,您需要在尝试过滤 Where 子句中的数据之前调用 AsEnumerable()。

    Type t = typeof(User);
    PropertyInfo p = t.GetProperty("active");
    
    int totalActive = userContext.Users.AsEnumerable().Where(u => Convert.ToBoolean(p.GetValue(u)) == true).Count();
    

    这是因为 linq 将尝试将您的表达式转换为 sql 查询,并且您在 where 语句上使用了一个函数,linq 将无法转换此表达式,因为该函数在 sql 上不存在。

    AsEnumerable() 将强制查询运行并将数据拉入内存,然后 c# 将应用过滤。

    【讨论】:

    • 我会非常犹豫是否将其用于数据库,特别是如果 Users 表有大量行..
    【解决方案3】:
    int totalActive = userContext.Users.Where(u => u.active).Count();
    

    【讨论】:

      【解决方案4】:

      这是一个关于Client vs. Server Evaluation的问题。

      因为你在where条件中使用了一些sql server端无法识别的方法,所以可以将当前的linq全部转换到客户端执行。

      即添加 AsEnumerable()ToList()userContext.Users

      例子:

      int totalActive = userContext.Users.AsEnumerable().Where(u => Convert.ToBoolean(p.GetValue(u)) == true).Count();
      

      另外一种方式,你也可以将所有的linq语句转换成sql server-side认可的语句来执行,正如上面@Guru Stron提供的答案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-26
        • 2017-01-16
        • 1970-01-01
        • 2020-02-22
        相关资源
        最近更新 更多