首页 > 解决方案 > 如何使用 C# 表达式 API 使对象列表成为 Lamba 中的参数

问题描述

我正在尝试动态创建一个谓词以传递给 linq where 子句。这是一个通用方法,它接受两个相同类型的列表和属性名称列表进行比较。

void SomeMethod<T>(List<T> oldRecords, List<T> newRecords, List<string> propertiesOfT)
{
    // dynamically build predicate for this
    var notMatch = oldRecords.Where(o => !newRecords.Any(n => n.Prop1 == o.Prop1 && n.Prop2 == o.Prop2)).ToList();

    // do somethind with notMatch

}

我想转换这个:

var notMatch = oldRecords.Where(o => !newRecords.Any(n => n.Prop1 == o.Prop1 && n.Prop2 == o.Prop2)).ToList();

为达到这个:

var predicate = "n => n.Prop1 == o.Prop1 && n.Prop2 == o.Prop2"; // sudo code
var notMatch = oldRecords.Where(o => !newRecords.Any(predicate));

或这个

var predicate = "o => !newRecords.Any(n => n.Prop1 == o.Prop1 && n.Prop2 == o.Prop2)" // sudo code
var notMatch = oldRecords.Where(predicate);

动态创建表达式时如何填充 newRecords?以及如何在表达式中引用参数 o 和参数 n。

我已经做到了这一点:

//construct the two parameters
var o = Expression.Parameter(typeof(T), "o");
var n = Expression.Parameter(typeof(T), "n");

// How to I go about populating o with values and n with values
// from oldRecords and newRecords? or is that no neccessary

var property = Expression.Property(o, typeof(T).GetProperty("Id").Name);

var value = Expression.Constant(Convert.ChangeType("12345", typeof(T).GetProperty("Id").PropertyType), typeof(T).GetProperty("Id").PropertyType);

BinaryExpression binaryExpression = Expression.MakeBinary(ExpressionType.Equal, property, value);

任何 sudo 代码或线索在哪里寻找实现这一目标?

标签: c#expression

解决方案


有了反射,这很容易。你只需要考虑一下。这是工作版本。

void SomeMethod<T>(List<T> oldRecords, List<T> newRecords, List<string> propertiesOfT)
{
    // get the list of property to match
    var properties = propertiesOfT.Select(prop => typeof(T).GetProperty(prop)).ToList();


    // Get all old record where we don't find any matching new record where all the property equal that old record
    var notMatch = oldRecords.Where(o => !newRecords.Any(n => properties.All(prop => prop.GetValue(o).Equals(prop.GetValue(n))))).ToList();            
}

这是我尝试过的样本集,它有效

public class test
{
    public int id { get; set; } = 0;
    public string desc { get; set; } = "";
    public test(string s, int i)
    {
        desc = s;id = i;
    }
}

private void Main()
{
    var oldRecords = new List<test>()
    {
        new test("test",1),
        new test("test",2)
    };

    var newRecords = new List<test>()
    {
        new test("test1",1),
        new test("test",2)
    };

    SomeMethod(oldRecords, newRecords, new List<string>() { "id", "desc" });
}

推荐阅读