MethodCallExpression orderby

时间:2014-06-04 18:29:16

标签: c# linq entity-framework

我正在尝试使用表达式树构建一个lambda查询,以使其在整个应用程序中使用。我基于here找到的代码进行了初步尝试。我更改了代码,以便它进行比较以返回100个实体,其LastName就在searchText之前,如下所示:

TEntity entity = entitySet.FirstOrDefault();
string searchName = entity.GetType().GetProperty("SearchName").ToString();
searchText = "Baker";
int records = 100;

IQueryable<TEntity> queryableData = entitySet.AsQueryable<TEntity>();
var param = Expression.Parameter(typeof(TEntity), searchName);
var body = Expression.LessThan(Expression.Call(
    typeof(string), 
    "Compare", 
    null, 
    Expression.PropertyOrField(param, searchName), 
    Expression.Constant(searchText)), 
    Expression.Constant(0));

var lambda = Expression.Lambda<Func<TEntity, bool>>(body, param);

MethodCallExpression whereCallExpression = Expression.Call(
    typeof(Queryable),
    "Where",
    new Type[] { typeof(TEntity) },
    queryableData.Expression,
    lambda);

var data = entitySet.AsQueryable<TEntity>().Provider.CreateQuery<TEntity>(whereCallExpression).Take(records);

上面的代码有效,它从数据库中返回100个实体,它们位于我的searchText之前,但它们是错误的实体,因为它们在entitySet中不是有序的。所以,我需要在Expression树中使用一个OrderBy子句,以便我得到“Azure”,Axel,Avis“等。

我试过了:

 MethodCallExpression orderByCallExpression = Expression.Call(
    typeof(Queryable),
    "OrderByDescending",
    new Type[] { typeof(TEntity), typeof(TEntity) },
    whereCallExpression,
    Expression.Lambda<Func<TEntity, string>>(param, new ParameterExpression[] { param }));

var data = entitySet.AsQueryable<TEntity>().Provider.CreateQuery<TEntity>(orderByCallExpression).Take(records);

我收到错误“System.Core.dll中发生类型'System.ArgumentException'的异常但未在用户代码中处理

附加信息:MyEntity类型的表达式'不能用于返回类型'System.String'“

堆栈跟踪:

at System.Linq.Expressions.Expression.ValidateLambdaArgs(类型delegateType,Expression&amp; body,ReadOnlyCollection`1参数)

at System.Linq.Expressions.Expression.Lambda [TDelegate](表达式主体,字符串名称,布尔值tailCall,IEnumerable`1参数)

at System.Linq.Expressions.Expression.Lambda [TDelegate](表达式body,Boolean tailCall,IEnumerable`1参数)

所以,我看了here并尝试了这个:

Expression<Func<TEntity, string>> sortExp = l => l.SearchName);

MethodCallExpression orderByCallExpression = Expression.Call(
    typeof(Queryable),
    "OrderByDescending",
    new Type[] { typeof(TEntity), typeof(string) },
    whereCallExpression,
    sortExp);

我收到错误“mscorlib.dll中发生类型'System.NotSupportedException'的异常但未在用户代码中处理

其他信息:LINQ to Entities不支持指定的类型成员“SearchName”。仅支持初始值设定项,实体成员和实体导航属性。“

我认为问题在于SearchName属性是一个字符串,其中包含我想要OrderBy的实体上的字段名称。我试过了:

l => (string)l.GetType().GetProperty("SearchName").GetValue(SearchName);

但是这给了我错误:“mscorlib.dll中发生了'System.NotSupportedException'类型的异常,但未在用户代码中处理

附加信息:LINQ to Entities无法识别方法'System.Object GetValue(System.Object)'方法,并且此方法无法转换为商店表达式。“

TEntity 基于EntityObject

SearchName 是TEntity上string类型的属性,用于保存数据库中字段的名称。

entitySet 的类型为ObjectSet<TEntity>

任何建议都将不胜感激。

更新和解决方案 我发现代码here让我思考的方向不同。以下是适用于SearchName排序的代码:

var type = typeof(TEntity);
var property = type.GetProperty(searchName);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);

MethodCallExpression orderByCallExpression = Expression.Call(
        typeof(Queryable),
        "OrderByDescending",
        new Type[] { typeof(TEntity), typeof(string) },
        whereCallExpression,
        Expression.Quote(orderByExp));                                   

var data = entitySet.AsQueryable<TEntity>().Provider.CreateQuery<TEntity>(orderByCallExpression).Take(records);

3 个答案:

答案 0 :(得分:0)

如果SearchName是未存储在数据库中的字段,则应使用[ignore]属性或this.Ignore(t => t.Property);流畅的API在实体模型中忽略它。

答案 1 :(得分:0)

所以你想要一个像

这样的谓词
l => l.GetPropertyValue(l.SearchName) == "MyString"

其中GetPropertyValue指定由SearchString命名的字段的值。即使您构造包含此逻辑的表达式树,EF也无法将其转换为SQL。 SQL是静态类型的。您无法通过动态名称访问列。

解决这个问题。例如,构造一个表达式:

l =>
  (
   l.SearchName == "MyField1" ? l.MyField1 :
   l.SearchName == "MyField2" ? 2.MyField1 :
   l.SearchName == "MyField3" ? 3.MyField1 :
   null
  ) == "MyString"

l =>
   l.SearchName == "MyField1" && l.MyField1 == "MyString" ||
   l.SearchName == "MyField2" && l.MyField2 == "MyString" ||
   l.SearchName == "MyField3" && l.MyField3 == "MyString" ||
   false

答案 2 :(得分:0)

以下是我用于SearchName不是表中字段的解决方案。

                 if (entity.SearchName == "LastFirstName")
                {
                    //// Construct Expressions to hold the propert values
                    Expression lastNameExp = Expression.PropertyOrField(param, "LastName");
                    Expression firstNameExp = Expression.PropertyOrField(param, "FirstName");

                    //// Construct a String.Concat method. 
                    MethodInfo methodInfo = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) });

                    //// Combine the LastName + FirstName for the compare
                    MethodCallExpression combinedExp = Expression.Call(methodInfo, lastNameExp, firstNameExp);