使用Entity框架的sql IN子句的动态linq查询表达式树

时间:2013-10-23 06:38:48

标签: c# linq dynamic expression in-clause

我想在EF 6.0中为sql IN子句创建一个动态linq表达式,代码为第一个approch。请注意,我是Expressions的新手。我想要实现的是

select * from Courses where CourseId in (1, 2, 3, 4)
//CourseId is integer

正常的linq查询看起来像这样。但我想动态查询它

string[] ids = new string[]{"1", "2", "3", "4"};
var courselist = DBEntities.Courses.Where(c => ids.Contains(SqlFunctions.StringConvert((decimal?)c.CourseId)))

有两种方法可以进行动态表达 1)一种方法是循环通过id并使表达式为
以下代码将在调试视图中创建以下表达式

{f => ((StringConvert(Convert(f.CourseId)).Equals("23") Or StringConvert(Convert(f.CourseId)).Equals("2")) Or StringConvert(Convert(f.CourseId)).Equals("1"))}

动态表达

var param = Expression.Parameters(typeof(Course), "f")
MemberExpression property = Expression.PropertyOrField(param, "CourseId");                   
MethodInfo mi = null;
MethodCallExpression mce = null;
if (property.Type == typeof(int))
{
   var castProperty = Expression.Convert(property, typeof(double?));
   var t = Expression.Parameter(typeof(SqlFunctions), "SqlFunctions");
   mi = typeof(SqlFunctions).GetMethod("StringConvert", new Type[] { typeof(double?) });   
   mce = Expression.Call(null,mi, castProperty);
}
mi = typeof(string).GetMethod("Equals", new Type[]{ typeof(string)});            
BinaryExpression bex = null;
if (values.Length <= 1)
{
   return Expression.Lambda<Func<T, bool>>(Expression.Call(mce, mi,     Expression.Constant(values[0]), param));
}
//var exp1 = Expression.Call(mce, mi, Expression.Constant(values[0]));
for (int i = 0; i < values.Length; i++)
{               
   if (bex == null)
   {
      bex = Expression.Or(Expression.Call(mce, mi, Expression.Constant(values[i])), Expression.Call(mce, mi, Expression.Constant(values[i + 1])));
      i++;
   }
   else             
      bex = Expression.Or(bex, Expression.Call(mce, mi, Expression.Constant(values[i])));

}//End of for loop
return Expression.Lambda<Func<T, bool>>(bex, param);

2)我试过的第二种方式(调试视图)

{f => val.Contains("23")} //val is parameter of values above
我试过的动态表达式是

var param = Expression.Parameters(typeof(Course), "f")
MemberExpression property = Expression.PropertyOrField(param, "CourseId"); 
var micontain = typeof(Enumerable).GetMethods().Where(m => m.Name == "Contains" && m.GetParameters().Length == 2).Single().MakeGenericMethod(typeof(string));
var mc = Expression.Call(micontain, Expression.Parameter(values.GetType(), "val"), Expression.Constant("2"));//NOTE: I haven't use CourseId for now as i am getting conversion error
return Expression.Lambda<Func<T, bool>>(mc, param);

我收到以下错误

  • LINQ to Entities无法识别方法'System.String StringConvert(System.Nullable`1 [System.Double])'方法,这个 当我使用时,方法无法转换为商店表达式 第一种方法。我知道我不能在EF中使用ToString,这就是为什么我使用SqlFunction但它不适用于我。
  • 参数'val'未使用第二种方法在指定的LINQ to Entities查询表达式中绑定

我在过去4天尝试这个。我用谷歌搜索,但没有找到任何合适的解决方案。请帮帮我。

2 个答案:

答案 0 :(得分:8)

经过多次努力,我找到了解决问题的方法。 我想实现这个SQL查询

select * from Courses where CourseId in (1, 2, 3, 4)

使用Linq to Entities,但我想将(1,2,3,4)列表动态传递给linq查询。我为此创建了一个Extension类。

public static class LinqExtensions
{
   public static Expression<Func<T, bool>> False<T>() { return f => false; } 
   public static Expression<Func<T, bool>> In<T, TValue>(this Expression<Func<T, bool>> predicate,string propertyName, List<TValue> values)
   {            
           var param = predicate.Parameters.Single();
           MemberExpression property = Expression.PropertyOrField(param, propertyName);            
           var micontain = typeof(List<TValue>).GetMethod("Contains");            
           var mc = Expression.Call(Expression.Constant(values), micontain, property);
           return Expression.Lambda<Func<T, bool>>(mc, param);
   }
}

使用LinqExtensions

var pred = LinqExtensions.False<Course>(); //You can chain In function like  LinqExtensions.False<Course>().In<Course, int>("CourseId", inList);
var inList= new List<int>(){1, 2, 3}; //Keep in mind the list must be of same type of the Property that will be compared with. In my case CourseId is integer so the in List have integer values
pred =pred.In<Course, int>("CourseId", inList); //TValue is int. As CourseId is of type int.
var data = MyEntities.Courses.Where(pred);

我希望这可能对某些人有益

答案 1 :(得分:0)

你看过

的类型吗?
var courselist = DBEntities.Courses.Where(c => ids.Contains(c.CourseId)))

以上声明不会返回实际的课程列表。查询尚未执行。它只返回IQuereable。当您实际调用.ToList()方法

时执行查询

所以,你的解决方案是......

  1. 使用for循环创建ID数组,然后只需运行以下查询

  2. var courselist = DBEntities.Courses.Where(c => ids.Contains(c.CourseId))).ToList()