在 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 时仍然失败,可能是什么原因

在 LINQ 中动态选择一个属性

在最后一种情况下,当您将返回类型更改为 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();
    }