如何修改表达式< Func< ???,bool>>的类型参数? [英] How to modify type parameter of Expression<Func<???, bool>>?

查看:116
本文介绍了如何修改表达式< Func< ???,bool>>的类型参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个以下的实例:

 表达式< Func< IRequiredDate,bool>> 

我希望将其转换为以下内容的实例,因此可用于运行查询在实体框架中:

 表达式< Func< TModel,bool>> 

这将允许我使用通用的过滤查询来实现IRequiredDate的任何Model,例如: / p>

  //在某些存储库函数中:
var query = DbContext.Set< Order>()
。 FilterByDateRange(DateTime.Today,DateTime.Today);

var query = DbContext.Set< Note>()
.FilterByDateRange(DateTime.Today,DateTime.Today);

var query = DbContext.Set& Complaint>()
.FilterByDateRange(DateTime.Today,DateTime.Today);


//通用功能可以过滤任何实现IRequiredDate的模型
public static IQueryable< TModel> FilterByDate< TModel>(IQueryable< TModel>查询,DateTime startDate,DateTime endDate)其中TModel:IRequiredDate
{
//这不会工作,因为E / F不会接受类型为IRequiredDate的表达式,即使TModel实现了IRequiredDate
// Expression< Func< IRequiredDate,bool>> dateRangeFilter = x => x.Date> = startDate&&& x.Date< = endDate;
// query = query.Where(dateRangeFilter);

//这也不会工作,x.Date作为IRequiredDate的成员编译成表达式,而不是TModel,所以E / F敲回它的原因是相同的:
// Expression< Func< TModel,bool>>> dateRangeFilter = x => x.Date> = startDate&&& x.Date< = endDate;
// query = query.Where(dateRangeFilter);

//所有你需要的是lov ....呃...这样的东西:
表达式< Func< IRequiredDate,bool>> dateRangeFilter = x => x.Date> = startDate&&& x.Date< = endDate;
表达式< Func< TModel,bool>>> dateRangeFilterForType = ConvertExpressionType< IRequiredDate,TModel>(dateRangeFilter); //必须将表达式从一个类型转换为另一个类型
query = query.Where(dateRangeFilterForType)// Ahhhh。这将工作。

返回查询;
}

public static ConvertExpressionType< TInterface,TModel>(Expression& Func< TInterface,bool>>表达式)
其中TModel:TInterface //它必须实现接口,因为我们即将翻译它们
{
Expression< Func< TModel,bool>> newExpression = null;

// TODO:如何将表达式的内容转换为newExpression,修改
//泛型类型参数?

return newExpression;
}

我明白他们是不同的类型,不能转换。然而,我想知道是否有办法创建一个新的 Expression&FunC< TModel,bool>> ,然后根据表达式< Func< IRequiredDate,bool>> 提供,将任何类型引用从 IRequiredDate 切换到 TModel 在此过程中。



可以这样吗?

解决方案>

所以实际做映射的方法不是 ,但是遗憾的是,我没有一个很好的方法来概括它。这是一个方法,它需要一个 Func< T1,TResult> 并将其映射到代理,其中参数是比 T1

  public static Expression< Func< NewParam,TResult>> Foo< NewParam,OldParam,TResult>(
Expression< Func< OldParam,TResult>>表达式)
其中NewParam:OldParam
{
var param = Expression.Parameter (NewParam));
return Expression.Lambda expression.Body.Replace(expression.Parameters [0],param)
,param);
}

这使用替换方法替换另一个表达式的所有实例。定义是:

 内部类ReplaceVisitor:ExpressionVisitor 
{
private readonly Expression from,to;
public ReplaceVisitor(Expression from,Expression to)
{
this.from = from;
this.to = to;
}
public override表达式访问(表达式节点)
{
return node == from? to:base.Visit(node);
}
}

public static Expression替换(此表达式表达式,
表达式searchEx,表达式replaceEx)
{
返回新ReplaceVisitor searchEx,replaceEx).Visit(expression);
}

现在我们可以使用这个方法(应该给出一个更好的名称)所以:

 表达式< Func< object,bool>> oldExpression =无论如何
表达式< Func< string,bool>>> newExpression =
Foo< string,object,bool>(oldExpression);

当然,由于 Func 实际上是协变的对于其参数,我们可以确保对此方法的任何调用都会生成不会添加运行时故障点的表达式。



您可以轻松地为此创建版本 Func< T1,T2,TResult> 等等,通过16种不同类型的 Func if你想要的,只是为每个创建一个参数表达式,并用新的替换所有旧的参数表达式。这将是乏味的,但只是遵循这种模式。鉴于旧参数和新参数类型都需要有一个通用的参数,并且没有办法推断参数,那将会变得混乱。


I have an instance of the following:

Expression<Func<IRequiredDate, bool>>

I wish to convert it to an instance of the following, so it can be used to run a query in Entity Framework:

Expression<Func<TModel, bool>>

This will allow me to utilize a generic filtering query to any Model which implements IRequiredDate, eg.:

// In some repository function:
var query = DbContext.Set<Order>()
     .FilterByDateRange(DateTime.Today, DateTime.Today);

var query = DbContext.Set<Note>()
     .FilterByDateRange(DateTime.Today, DateTime.Today);

var query = DbContext.Set<Complaint>()
     .FilterByDateRange(DateTime.Today, DateTime.Today);


// The general purpose function, can filter for any model implementing IRequiredDate
public static IQueryable<TModel> FilterByDate<TModel>(IQueryable<TModel> query, DateTime startDate, DateTime endDate) where TModel : IRequiredDate
{
    // This will NOT WORK, as E/F won't accept an expression of type IRequiredDate, even though TModel implements IRequiredDate
    // Expression<Func<IRequiredDate, bool>> dateRangeFilter = x => x.Date >= startDate && x.Date <= endDate;
    // query = query.Where(dateRangeFilter);

    // This also WON'T WORK, x.Date is compiled into the expression as a member of IRequiredDate instead of TModel, so E/F knocks it back for the same reason:
    // Expression<Func<TModel, bool>> dateRangeFilter = x => x.Date >= startDate && x.Date <= endDate;
    // query = query.Where(dateRangeFilter);

    // All you need is lov.... uh... something like this:
    Expression<Func<IRequiredDate, bool>> dateRangeFilter = x => x.Date >= startDate && x.Date <= endDate;
    Expression<Func<TModel, bool>> dateRangeFilterForType = ConvertExpressionType<IRequiredDate, TModel>(dateRangeFilter); // Must convert the expression from one type to another
    query = query.Where(dateRangeFilterForType) // Ahhhh. this will work.

    return query;
}

public static ConvertExpressionType<TInterface, TModel>(Expression<Func<TInterface, bool>> expression)
where TModel : TInterface // It must implement the interface, since we're about to translate them
{
    Expression<Func<TModel, bool>> newExpression = null;

    // TODO: How to convert the contents of expression into newExpression, modifying the
    // generic type parameter along the way??

    return newExpression;
}

I understand that they are different types and cannot be cast. However I am wondering if there is a way to create a new Expression<Func<TModel, bool>>, then rebuild it based on the contents of the Expression<Func<IRequiredDate, bool>> provided, switching any type references from IRequiredDate to TModel in the process.

Can this be done?

解决方案

So the method to actually do the mapping isn't that hard, but sadly there isn't a good way that I can see of generalizing it. Here is a method that takes a Func<T1, TResult> and maps it to a delegate where the parameter is something more derived than T1:

public static Expression<Func<NewParam, TResult>> Foo<NewParam, OldParam, TResult>(
    Expression<Func<OldParam, TResult>> expression)
    where NewParam : OldParam
{
    var param = Expression.Parameter(typeof(NewParam));
    return Expression.Lambda<Func<NewParam, TResult>>(
        expression.Body.Replace(expression.Parameters[0], param)
        , param);
}

This uses the Replace method to replace all instances of one expression with another. The definition is:

internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

Now we can use this method (which should be given a better name) like so:

Expression<Func<object, bool>> oldExpression = whatever;
Expression<Func<string, bool>> newExpression =
    Foo<string, object, bool>(oldExpression);

And of course since Func is actually covariant with respect to its parameters, we can be sure that any calls to this method generate expressions that won't add runtime failure points.

You could trivially make versions of this for Func<T1, T2, TResult>, and so on and so forth up through the 16 different types of Func if you wanted, just creating a parameter expression for each, and replacing all of the old ones with new ones. It'd be tedious, but just following the pattern. Given that there needs to be a generic argument for both the old and new parameter types though, and that there's no way of inferring the arguments, that'd get...messy.

这篇关于如何修改表达式&lt; Func&lt; ???,bool&gt;&gt;的类型参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆