在 LINQ 中动态选择一个属性
本文关键字:一个 属性 LINQ 动态 选择 | 更新日期: 2023-09-27 18:36:29
我有一个属性,其名称在变量(userFilters.property
)中,可以是字符串/枚举/数字类型。
我想动态选择该属性的不同值。 我该怎么做。
var query = CreatePincodeQuery(useFilters);
//userFilters.property contains the property to be selected
Expression<Func<PincodeData, string>> selectExpr = null;
IList<string> list = query.Select(selectExpr)
.Distinct()
.OrderBy(selectExpr)
.ToList();
return list.;
我应该创建类型Expression<Func<PincodeData, string>> selectExpr
的表达式,以用作select&orderBy block的一部分。
我该怎么做?
我查看了此处,此处和此处提供的解决方案,但无法理解如何修改它们以满足我的需要。
编辑
想出了下面的解决方案,正如预期的那样它不起作用,我如何将值转换为字符串。
Func<TEntity, string> CreateNewStatement<TEntity>(string field)
{
//https://stackoverflow.com/questions/16516971/linq-dynamic-select
var xParameter = Expression.Parameter(typeof(TEntity), "o");
var property = Expression.Property(xParameter, typeof(TEntity).GetProperty(field));
var lambda = Expression.Lambda<Func<TEntity, string>>(property, xParameter);
return lambda.Compile();
}
编辑
我将类型从字符串更改为对象,但在为枚举类型创建 lambda 时仍然失败,可能是什么原因
在最后一种情况下,当您将返回类型更改为 object 时,您没有将值类型转换为它。这个错误相当普遍,因为我们 C# 开发人员编写自己的 C# 代码,习惯于依赖 C# 编译器自动将值类型转换为对象。但是,表达式不是这种情况,因此您需要显式进行转换。
您的特定情况在下面解决,但是!这是非常非常容易的。由于您正在根据最终用户输入动态构建查询,因此后续步骤可能会很快变得复杂(例如,使用逻辑条件进行过滤)。发生这种情况后,请考虑使用动态 LINQ,它对于此类任务非常强大。
public class Entity
{
public string StringProp { get; }
public int IntProp { get; }
public DayOfWeek EnumProp { get; }
public Entity(string stringProp, int intProp, DayOfWeek enumProp)
{
StringProp = stringProp;
IntProp = intProp;
EnumProp = enumProp;
}
}
[Test]
public void Expressions()
{
var entities = new List<Entity>
{
new("Prop3", 3, DayOfWeek.Wednesday),
new("Prop2", 2, DayOfWeek.Tuesday),
new("Prop1", 1, DayOfWeek.Monday),
};
const string stringPropName = nameof(Entity.StringProp);
var stringStatement = CreateNewStatement<Entity>(stringPropName);
const string intPropName = nameof(Entity.IntProp);
var intStatement = CreateNewStatement<Entity>(intPropName);
const string enumPropName = nameof(Entity.EnumProp);
var enumStatement = CreateNewStatement<Entity>(enumPropName);
IList<object> listOfStrings = entities.Select(stringStatement).Distinct().OrderBy(i => i).ToList();
TestContext.WriteLine(string.Join(",", listOfStrings));
IList<object> listOfInts = entities.Select(intStatement).Distinct().OrderBy(i => i).ToList();
TestContext.WriteLine(string.Join(",", listOfInts));
IList<object> listOfEnums = entities.Select(enumStatement).Distinct().OrderBy(i => i).ToList();
TestContext.WriteLine(string.Join(",", listOfEnums));
}
private static Func<TEntity, object> CreateNewStatement<TEntity>(string fieldName)
{
var parameter = Expression.Parameter(typeof(TEntity), "o");
var propertyInfo = typeof(TEntity).GetProperty(fieldName);
if (propertyInfo == null)
throw new InvalidOperationException($"Property '{fieldName}' not found");
Expression body = Expression.Property(parameter, propertyInfo);
if (propertyInfo.PropertyType.IsValueType)
body = Expression.Convert(body, typeof(object));
var lambda = Expression.Lambda<Func<TEntity, object>>(body, parameter);
return lambda.Compile();
}