2015-01-02 42 views
2

我想创建一个基于自己的id获取EntityFramework对象而不传入lambda表达式作为参数的方法GetById()的通用方法。对于下面的代码,实体T的类型为Message,对GetById()已实现的类已知,并且具有属性MessageId以及其他几个属性。 MessageId名称已在下面的示例中进行了硬编码,因为这仍然是实验性的 - 从T中提取id属性名称在以后很容易修复。使用Func构建LambdaExpression <,>其中returnType是IQueryable <T>

我一直在努力寻找一种方法来构建一个简单的LambdaExpression,其中IQueryable<T>作为参数类型,并希望有人会有这样的线索可以完成。我想要IQueryable<T>的原因是因为我的底层渠道工厂提供程序需要此更复杂的查询。

在下面的代码var exp = Expression.Lambda<...>行示出了我想与落得表达式函数类型定义,但行给出了异常:

不能用于返回类型System.Boolean的表达键入IQueryable的

这是因为体内有Boolean类型,而我的表情参数queryParamtRetIQueryable<Message>类型。此外,如果我将主体类型更改为IQueryable<Message>,我无法找到属性MessageId,因为该类型不再输入T,而是Message,但输入的类型为IQueryable<T>

public T GetById(int id) 
{ 
    var queryParamLeft = Expression 
     .Parameter(typeof(System.Data.Entity.DbSet<T>), "o"); 
    var queryParamRet = Expression 
     .Parameter(typeof(IQueryable<T>), "o"); 
    var entityFrameworkType = Expression 
     .Parameter(typeof(T), "o"); 
    var queryProperty = Expression 
     .PropertyOrField(entityFrameworkType, "MessageId"); 
    var body = Expression 
     .Equal(queryProperty, Expression.Constant(id)); 
    var exp = Expression 
     .Lambda<Func<System.Data.Entity.DbSet<T>, IQueryable<T>>>(
      body, 
      queryParamRet); 

    var returnXml = DoWithChannel(channel 
         => channel.Load(serializer.Serialize(exp))); 
} 
+1

它看起来像你缺少的一步你表达式 - 如果它在c#中被编写为LINQ语句,它会是什么样子? 'dbSet.Where(o => o.MessageId == id)'也许? – Rhumborl

+1

您错过了'Expression.Call(Queryable.Where,Expression.Lambda <...>)' – Aron

回答

1

TLDR:写出你想创建一个表达式的代码,然后故意创建的表达,将它们合并为外表达开始之前与任何内部表达式。


如果你写你预期的代码作为一个功能,它会是这个样子

public static IQueryable<T> FilterADbSet(DbSet<T> dbSet) 
{ 
    return Queryable.Where<T>(dbSet, o => o.MessageId == 34); 
} 

DbSet<T>类型的输入参数,IQueryable<T>类型的输出,并呼吁Queryable.Where<T>与参数的dbSet变量和一个表达式。

从外部开始,您首先需要构建表达式以传递给where子句。你已经在你的代码中完成了。

接下来,您需要为where子句创建lambda表达式。

var whereClause = Expression.Equal(queryProperty, Expression.Constant(id)); 

var whereClauseLambda = Expression.Lambda<Func<T, bool>>(whereClause, entityFrameworkType); 

接下来,如注释所示,您需要使用Expression.Call来创建主体。

我的最终结果使您的代码工作如下。

static Expression<Func<IQueryable<T>, IQueryable<T>>> WhereMethodExpression = v => v.Where(z => true); 
static MethodInfo WhereMethod = ((MethodCallExpression)WhereMethodExpression.Body).Method; 

public T GetById(int id) 
{ 
    var queryParamLeft = Expression 
     .Parameter(typeof(System.Data.Entity.DbSet<T>), "dbSet"); 

    var entityFrameworkType = Expression 
     .Parameter(typeof(T), "entity"); 

    var queryProperty = Expression 
     .PropertyOrField(entityFrameworkType, "MessageId"); 

    var whereClause = Expression 
     .Equal(queryProperty, Expression.Constant(id)); 

    var whereClauseLambda = Expression.Lambda<Func<T, bool>>(whereClause, entityFrameworkType); 

    var body = Expression.Call(
     WhereMethod, 
     queryParamLeft, 
     whereClauseLambda 
     ); 

    var exp = Expression 
     .Lambda<Func<System.Data.Entity.DbSet<T>, IQueryable<T>>>(
      body, 
      queryParamLeft); 

    var returnXml = DoWithChannel(channel 
         => channel.Load(serializer.Serialize(exp))); 

} 
  1. 我使用的表达式来获取Queryable.Where<T>
  2. 你的身体表达的MethodInfo对象中需要通过queryParamLeft。queryParamRet不需要
相关问题