为IQueryable< T>生成表达式。 [英] Generating An Expression For An IQueryable<T>

查看:59
本文介绍了为IQueryable< T>生成表达式。的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用LINQ-> WCF数据服务-> EF,它支持LINQ的子集,但有一些警告。一旦学习了各种技巧和变通方法,我就不会遇到任何麻烦,但是我想制作一个可重用的表达式生成器,用于仅比较a的 Date 部分。 DateTime



通过常规EF,您可以使用 EntityFunctions.TruncateTime (EF< 6)或 DbFunctions.TruncateTime (EF6 +),但这不适用于数据服务。



到目前为止,我的解决方案是重复构建where子句的混乱之处。

  .Where(x => x.DateProperty.Year == DateToCompare.Year&& 
x.DateProperty.Month == DateToCompare.Month&&
x.DateProperty.Day == DateToCompare.Day);

不得不反复写(但这行得通)真是令人讨厌,所以我试图创建一些东西例如:

  .WhereDate(x => x.DateProperty,DateToCompare); 

任何类似的事情都可以做,只是简短而甜美且易读-我讨厌重复的不必要代码。 / p>

结构不是问题,我知道我需要使用 IQueryable< T> Func< T,DateTime> (或 Expression< Func< T,DateTime>> )和 DateTime 并返回 IQueryable< T>

  public static IQueryable< T> WhereDate< T>(此IQueryable< T数据,Func< T,DateTime>>选择器,DateTime日期)
{
返回数据.Where(/ * Something * /);
};

在我遇到麻烦的地方,需要这样做并构建一个可以放在where子句中的表达式而不违反表达式树的限制。我不完全确定如何进行现有查询并将我自己的where语句添加到表达式中,而不执行 .where ,我认为这可能是关键。我想我需要输入 Expression< Func< T,DateTime>> 并构建一些东西来使用它来添加 Expression< Func< T,布尔>



任何人对此都有一定的经验,或者知道我应该阅读哪些文档?



这里最大的障碍是您不能将基于语句的lambda转换为表达式,并且不能将不受支持的函数传递给数据服务 EF。据我所知,这使得所有朴素的解决方案都是不可能的。

解决方案

这是我来的解决方案在阅读了很多有关该主题的内容之后:

  private static IQueryable< T> _whereDate< T(此IQueryable< T数据,MemberExpression date1Expression,ParameterExpression参数,DateTime日期)
{
var date1Year = Expression.Property(date1Expression, Year);
var date1Month = Expression.Property(date1Expression, Month);
var date1Day = Expression.Property(date1Expression, Day);
var date2Year = Expression.Constant(date.Year);
var date2Month = Expression.Constant(date.Month);
var date2Day = Expression.Constant(date.Day);
var yearsEqual = Expression.Equal(date1Year,date2Year);
var monthsEqual = Expression.Equal(date1Month,date2Month);
var daysEqual = Expression.Equal(date1Day,date2Day);
var allPartsEqual = Expression.AndAlso(Expression.AndAlso(daysEqual,monthsEqual),yearsEqual); // Day-> Month->年,以便尽快有效地删除尽可能多的内容。
var whereClause = Expression.Call(typeof(Queryable), Where,new Type [] {data.ElementType},data.Expression,Expression.Lambda(allPartsEqual,parameter));
返回data.Provider.CreateQuery< T>(whereClause);
}

公共静态IQueryable< T> WhereDateT(此IQueryable< T数据,Expression< Func< T,DateTime?
var nullableDateProperty =(PropertyInfo)selectorMemberExpression.Member;
varentityExpression = Expression.Parameter(typeof(T));
var date1Expression = Expression.Property(entityExpression,nullableDateProperty);
返回数据。_whereDate(Expression.PropertyOrField(date1Expression, Value),entityExpression,date);
}

公共静态IQueryable< T> WhereDateT(此IQueryable< T数据,Expression< Func< T,DateTime>>选择器,DateTime日期)
{
var selectorMemberExpression =((MemberExpression)selector.Body);
var dateProperty =(PropertyInfo)selectorMemberExpression.Member;
var entityExpression = Expression.Parameter(typeof(T));
返回数据。_whereDate(Expression.Property(entityExpression,dateProperty),entityExpression,date);
}

它分为多个功能来减少冗余代码并同时支持 DateTime DateTime?



我意识到没有可以检查可空版本是否缺少价值的东西-我会尽快添加,但是我想为其他任何人提供解决方案向他们学习,确保没有人浪费时间向我解释。为了提高效率和可读性,我总是反复浏览几次代码,记录函数,注释不清楚的事物,确保不会出现意外的 Exception ,但这是在此之前发生的。 。如果您逐字使用此代码,请记住这一点(如果您这样做,请告诉我,我想知道我并没有浪费时间发布此代码)。


I'm using LINQ->WCF Data Services->EF, which supports a subset of LINQ with a few caveats. I've had no trouble with that once learning the tricks and workarounds for various things, but I'd like to make a reusable expression generator for comparing only the Date portion of a DateTime.

With regular EF you can use EntityFunctions.TruncateTime (EF<6) or DbFunctions.TruncateTime (EF6+), but this doesn't work over data services.

My solution so far has been to repeatedly build this mess of a where clause:

.Where(x => x.DateProperty.Year == DateToCompare.Year && 
            x.DateProperty.Month == DateToCompare.Month && 
            x.DateProperty.Day == DateToCompare.Day);

That's just nasty to have to repeatedly write (but it works), so I was trying to create something like:

.WhereDate(x => x.DateProperty, DateToCompare);

Anything similar would do, just short and sweet and readable - I detest repetitive unnecessary-feeling code.

The structure isn't a problem, I know I need something that takes IQueryable<T>, Func<T, DateTime> (or Expression<Func<T, DateTime>>), and DateTime and returns IQueryable<T>.

public static IQueryable<T> WhereDate<T>(this IQueryable<T> data, Func<T, DateTime>> selector, DateTime date)
{
    return data.Where(/*Something*/);
};

Where I'm having trouble is taking this and building an expression that can be put into that where clause without violating the restrictions of expression trees. I'm not entirely sure how to take an existing query and add my own where statement to the expression without doing a .Where, which I think might be the key here. I think I need to take in an Expression<Func<T, DateTime>> and build something that uses that to add an Expression<Func<T, bool>> to the tree and return it as anIQueryable`.

Anybody got some experience with this, or know which docs I should be reading?

The biggest barriers here are that you can't turn a statement-based lambda into an expression, and you can't pass unsupported functions into the data service or EF. This makes all of the naïve solutions impossible, and as far as I know leaves manual expression manipulation.

解决方案

Here's the solution I came up with after reading a lot about the topic:

private static IQueryable<T> _whereDate<T>(this IQueryable<T> data, MemberExpression date1Expression, ParameterExpression parameter, DateTime date)
{
    var date1Year = Expression.Property(date1Expression, "Year");
    var date1Month = Expression.Property(date1Expression, "Month");
    var date1Day = Expression.Property(date1Expression, "Day");
    var date2Year = Expression.Constant(date.Year);
    var date2Month = Expression.Constant(date.Month);
    var date2Day = Expression.Constant(date.Day);
    var yearsEqual = Expression.Equal(date1Year, date2Year);
    var monthsEqual = Expression.Equal(date1Month, date2Month);
    var daysEqual = Expression.Equal(date1Day, date2Day);
    var allPartsEqual = Expression.AndAlso(Expression.AndAlso(daysEqual, monthsEqual), yearsEqual); //Day->Month->Year to efficiently remove as many as possible as soon as possible.
    var whereClause = Expression.Call(typeof(Queryable), "Where", new Type[] { data.ElementType }, data.Expression, Expression.Lambda(allPartsEqual, parameter));
    return data.Provider.CreateQuery<T>(whereClause);
}

public static IQueryable<T> WhereDate<T>(this IQueryable<T> data, Expression<Func<T, DateTime?>> selector, DateTime date)
{
    var selectorMemberExpression = ((MemberExpression)selector.Body);
    var nullableDateProperty = (PropertyInfo)selectorMemberExpression.Member;
    var entityExpression = Expression.Parameter(typeof(T));
    var date1Expression = Expression.Property(entityExpression, nullableDateProperty);
    return data._whereDate(Expression.PropertyOrField(date1Expression, "Value"), entityExpression, date);
}

public static IQueryable<T> WhereDate<T>(this IQueryable<T> data, Expression<Func<T, DateTime>> selector, DateTime date)
{
    var selectorMemberExpression = ((MemberExpression)selector.Body);
    var dateProperty = (PropertyInfo)selectorMemberExpression.Member;
    var entityExpression = Expression.Parameter(typeof(T));
    return data._whereDate(Expression.Property(entityExpression, dateProperty), entityExpression, date);
}

It's split into multiple functions to reduce redundant code and support both DateTime and DateTime?.

I realize there's no check for a lack of value on the nullable version - that's something I'll add soon enough, but I wanted to get the solution up for anybody else to learn from and make sure nobody wastes their time explaining this to me. I always go through my code a few times for efficiency and readability, documenting the functions, commenting unclear things, making sure no unexpected Exceptions can arise, but this is prior to that. Just keep that in mind if you use this code verbatim (and if you do, let me know, I'd like to know I didn't waste the effort of posting this).

这篇关于为IQueryable&lt; T&gt;生成表达式。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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