LINQ - 按属性名称选择

Posted

技术标签:

【中文标题】LINQ - 按属性名称选择【英文标题】:LINQ - select by property name 【发布时间】: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 将其更改为自动属性。 【参考方案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提供的答案。

【讨论】:

以上是关于LINQ - 按属性名称选择的主要内容,如果未能解决你的问题,请参考以下文章

C# LINQ - 按属性分组列表,然后按不同组选择

C# - 使用属性名称作为字符串按属性排序的代码[重复]

在按属性选择时,后代中的XML到Linq元素会出现对象引用错误

Linq 按产品排序但显示类别

如何使用 LINQ 在 XML 中按名称获取元素

jquery 按属性名称选择每个此值附加文本