【问题标题】:C# lambda IN clause errorC# lambda IN 子句错误
【发布时间】:2011-07-29 15:01:37
【问题描述】:

我在使用 C# 和 lambdas 创建 IN 子句时遇到问题。

我有以下方法GetUserList(string filtersByRoles)

变量string filtersByRoles 可以保存逗号分隔的值,例如:“1,2”或“1,2,3”或“1,3,4”等...每个数字代表唯一的数字一个角色(也就是 RoleId)。

然后我有以下 C# lambda 查询:

var query = _userRepository.GetUserList();

它返回一个IQueryable<User>,其中User 是我的EntityFramework 中的一个表。

一旦我验证了filtersByRoles 参数是否不为空或空,我需要创建一个IN 子句,例如:

if (!string.IsNullOrEmpty(filtersByRoles))
{
   //Convert *filtersByRoles* to an array of integers
   int[] myArray = filtersByRoles.Split(',').Select(x => int.Parse(x)).ToArray();

   //Make the IN clause
   query = query.Where(u => myArray.Contains(u.RoleId));
}

上面的代码可以编译...但是在 RUNTIME 它失败并显示以下错误消息:

LINQ to Entities 无法识别“Boolean”方法 包含[Int32](System.Collections.Generic.IEnumerable`1[System.Int32], int32)' 方法,并且这个方法不能翻译成store 表达。


我设法找到了解决方法,但它涉及到调用 .ToList() 方法,我相信该方法会从我的数据库中获取所有数据,然后添加 Where() 子句。 但这不会破坏目的或产生一些性能问题吗?

这就是我所做的:

if (!string.IsNullOrEmpty(filtersByRoles))
{
      string[] myArray = filtersByRoles.Split(',');
      query = query.ToList().Where(u => myArray.Contains(u.RoleId.ToString())).AsQueryable();
}

我希望调用.ToList() 并避免获取所有数据。

还有其他方法可以实现吗?

编辑: 我正在使用 Entity Framework 1.0 和 .NET Framework 3.5

谢谢 真诚的

文斯

【问题讨论】:

  • 您使用的是什么版本的 .NET?
  • 废话...抱歉...框架 3.5 不是 4.0 :-(
  • 错误告诉你linq优化器不知道如何将Contains翻译成sql中的相应过程。我从来没有尝试过,但你能建立多个 Where's 并将它们链接在一起吗?比如 .Where( u=> u.RoleId == myArray[0] ).Where( u => u.RoleId == myArray[1]) ...
  • 现在离开办公室......但会在周末尝试一下,谢谢 Brian!

标签: c# linq entity-framework lambda


【解决方案1】:

这是我的 2 美分:

也许 Dynamic LinQ 将有助于解决您的问题: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

您可以将 Where 子句构建为字符串,例如:

string sWhereClause = "1 = 1";
foreach(string rId in filtersByRoles.Split(','))
   sWhereClause += " OR RoleId = " + rId;

(我建议使用StringBuilder而不是+concat,但为了这个答案,没关系)

然后

query = query.Where(sWhereClause);

虽然我还没有尝试过,但这听起来很适合解决您的问题。虽然看起来像 SQL 注入……嗯,可以带来改进。

编辑: 作为第二个想法,我设法提出了这个新想法:

string filterByRoles = "1,2,3";
query = query.Where(new Func<User, bool>(u => {
   return filterByRoles.Contains(u.RoleId.ToString());
})).AsQueryable();

这样,你可以在 Func{ ... } 委托中添加任何你想要的代码,只要它返回一个布尔值(我假设你的 TInput 是一个“用户”类,当然改变它以使用一个对应你的需要)。

希望这会有所帮助!

【讨论】:

  • 现在离开办公室......但会在周末尝试一下,谢谢 Flo!
【解决方案2】:

【讨论】:

  • 不幸的是,我使用的是 EF 1.0
【解决方案3】:

根据您的一些回复,我设法提取了如下内容:

int[] myArray = filtersByRoles.Split(',').Select(x => int.Parse(x)).ToArray();
int count = myArray.Count();
int role1;
int role2;
int role3;
int role4;

switch (myArray.Length)
{
    case 1:
        role1 = myArray[0];

        query = query.Where(u => u.RoleId.Equals(role1));
        break;

    case 2:
        role1 = myArray[0];
        role2 = myArray[1];

        query = query.Where(u => u.RoleId.Equals(role1)
                              || u.RoleId.Equals(role2));
        break;

    case 3:
        role1 = myArray[0];
        role2 = myArray[1];
        role3 = myArray[2];

        query = query.Where(u => u.RoleId.Equals(role1)
                              || u.RoleId.Equals(role2)
                              || u.RoleId.Equals(role3));
        break;

    case 4:
        role1 = myArray[0];
        role2 = myArray[1];
        role3 = myArray[2];
        role4 = myArray[3];

        query = query.Where(u => u.RoleId.Equals(role1)
                              || u.RoleId.Equals(role2)
                              || u.RoleId.Equals(role3)
                              || u.RoleId.Equals(role4));
        break;
}

直接尝试使用 myArray[xxx] 时:

query = query.Where(u => u.RoleId.Equals(myArray[0]));

我得到了这个:

LINQ to 不支持 LINQ 表达式节点类型“ArrayIndex” 实体。

因此创建了 4 个(整数)变量!

它现在可以工作,但可能需要一些优化……

谢谢

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-14
    • 2019-04-18
    • 1970-01-01
    • 1970-01-01
    • 2017-05-24
    • 1970-01-01
    相关资源
    最近更新 更多