如何进行一个 Func<object[],Expression<Func<T,bool>>> 的动态操作?

5

我正在开发一个 .Find(objects[] keys) 方法,动态创建一个 lambda 函数来查询数据库。

基本上我想要的是:

var mykeys = new Guid("37ec1659-b35e-46c9-a7fc-e9802644ca1a");
IQueryable<T> database ;
Func<object[],Expression<Func<T,bool>>> objectFinder = CreateKeyExpression(typeof(T));
var foundObject = database.FirstOrDefault(objectFinder(mykeys));

并且

private LambdaExpression CreateKeyExpression(Type C1Type)
{

    ParameterExpression instanceParameter = Expression.Parameter(C1Type);
    ParameterExpression keyParameters = Expression.Parameter(typeof(object[]));

    PropertyInfo[] objectKeys = C1Type.GetKeyProperties().ToArray();
    var expr = Expression.Equal( Expression.TypeAs( Expression.Property(instanceParameter,objectKeys[0]),typeof(object)),
      Expression.ArrayIndex(keyParameters,Expression.Constant(0))); 


    for (int i = 1; i < objectKeys.Length; i++)
    {
        expr = Expression.AndAlso(expr, Expression.Equal(
            Expression.Property(instanceParameter,objectKeys[i]),
            Expression.ArrayIndex(keyParameters,Expression.Constant(i))
            )); 

    }

    var lmp= Expression.Lambda(expr, instanceParameter);

    return Expression.Lambda(lmp, keyParameters);
}

有什么办法可以实现这个目标吗?上面的代码会给我一个Func<object[],Func<T,bool>>,这会把IQueryable转成IEnumerable,意味着它不会在数据库端执行。
2个回答

2

您需要使用Expression.Constant方法而不是Expression.ArrayIndex,因为您将无法使用FirstOrDefault方法传递带有键值的数组到表达式中。

private static LambdaExpression CreateKeyExpression(Type C1Type, object[] parameters)
{
    var instanceParameter = Expression.Parameter(C1Type);
    PropertyInfo[] objectKeys = C1Type.GetKeyProperties().ToArray();

    var expr = Expression.Equal(Expression.Property(instanceParameter, objectKeys[0]),
        Expression.Constant(parameters[0], objectKeys[0].PropertyType));


    for (int i = 1; i < objectKeys.Length; i++)
    {
        expr = Expression.AndAlso(expr, Expression.Equal(
            Expression.Property(instanceParameter, objectKeys[i]),
            Expression.Constant(parameters[i], objectKeys[i].PropertyType)));
    }
    return Expression.Lambda(expr, instanceParameter);
}

var parameters = new object[] { Guid.NewGuid(), Guid.NewGuid() };
var lambdaExpression = CreateKeyExpression(typeof(TestClass), parameters);
var testClasses = new List<TestClass>() { new TestClass { Id = (Guid)parameters[0], Id1 = (Guid)parameters[1] } };
var testClass = testClasses.AsQueryable().FirstOrDefault((Expression<Func<TestClass, bool>>)lambdaExpression);

有趣的是,我用了不同的方法解决了这个问题。在CreateKeyExpression中放置object[]参数不是一个选项。我想能够为给定的C1Type缓存表达式,这就是为什么我要求一个Func<Object[], Expression<Func<T,bool>>>。这将让我为每种类型缓存它,而不是在使用新的键集时每次生成它。如果其他人需要,我也会发布我的解决方案。谢谢。 - Poul K. Sørensen

0
private Expression<Func<object[], Expression<Func<C1Source, bool>>>> CreateKeyExpression<C1Source>()
{

    ParameterExpression instanceParameter = Expression.Parameter(typeof(C1Source));
    ParameterExpression keyParameters = Expression.Parameter(typeof(object[]));

    PropertyInfo[] objectKeys = typeof(C1Source).GetKeyProperties().ToArray();
    var expr = Expression.Equal( Expression.Property(instanceParameter,objectKeys[0]),
     Expression.Convert( 
        Expression.ArrayIndex(keyParameters,Expression.Constant(0)),
        objectKeys[0].PropertyType)
        ); 

    for (int i = 1; i < objectKeys.Length; i++)
    {
        expr = Expression.AndAlso(expr, Expression.Equal(
            Expression.Property(instanceParameter,objectKeys[i]),
            Expression.Convert(
                Expression.ArrayIndex(keyParameters,Expression.Constant(i)),
                objectKeys[i].PropertyType)
                ); 

    }

    var lmp= Expression.Lambda(expr, instanceParameter);

    return Expression.Lambda<Func<object[], Expression<Func<C1Source, bool>>>>(lmp, keyParameters);

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接