Linq to Entities - SQL“IN”子句

问题描述 投票:0回答:10

在 T-SQL 中你可以有这样的查询:

SELECT * FROM Users WHERE User_Rights IN ("Admin", "User", "Limited")

如何在 LINQ to Entities 查询中复制它?还可能吗?

.net linq linq-to-entities in-clause
10个回答
390
投票

你需要彻底改变你的思考方式。您不是通过“in”在一组预定义的适用用户权限中查找当前项目的用户权限,而是询问一组预定义的用户权限是否包含当前项目的适用值。这与您在 .NET 的常规列表中查找项目的方式完全相同。

使用 LINQ 有两种方法可以实现此目的,一种使用查询语法,另一种使用方法语法。本质上,它们是相同的,可以根据您的喜好互换使用:

查询语法:

var selected = from u in users
               where new[] { "Admin", "User", "Limited" }.Contains(u.User_Rights)
               select u

foreach(user u in selected)
{
    //Do your stuff on each selected user;
}

方法语法:

var selected = users.Where(u => new[] { "Admin", "User", "Limited" }.Contains(u.User_Rights));

foreach(user u in selected)
{
    //Do stuff on each selected user;
}

在这种情况下,我个人的偏好可能是方法语法,因为我可以通过匿名调用执行 foreach,而不是分配变量,如下所示:

foreach(User u in users.Where(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
    //Do stuff on each selected user;
}

从语法上看,这看起来更复杂,您必须理解 lambda 表达式或委托的概念才能真正弄清楚发生了什么,但正如您所看到的,这使代码变得相当精简。

这一切都取决于您的编码风格和偏好 - 我的所有三个示例都以略有不同的方式执行相同的操作。

另一种方法甚至不使用 LINQ,您可以使用相同的方法语法,将“where”替换为“FindAll”并获得相同的结果,这也适用于 .NET 2.0:

foreach(User u in users.FindAll(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
    //Do stuff on each selected user;
}

24
投票

这应该足以满足您的目的。它比较两个集合并检查一个集合是否具有与另一个集合中的值匹配的值

fea_Features.Where(s => selectedFeatures.Contains(s.feaId))

10
投票

在这种情况下我将选择内部连接。如果我使用 contains,它会迭代 6 次,尽管事实上只有一个匹配项。

var desiredNames = new[] { "Pankaj", "Garg" }; 

var people = new[]  
{  
    new { FirstName="Pankaj", Surname="Garg" },  
    new { FirstName="Marc", Surname="Gravell" },  
    new { FirstName="Jeff", Surname="Atwood" }  
}; 

var records = (from p in people join filtered in desiredNames on p.FirstName equals filtered  select p.FirstName).ToList(); 

包含的缺点

假设我有两个列表对象。

List 1      List 2
  1           12
  2            7
  3            8
  4           98
  5            9
  6           10
  7            6

使用 Contains,它将在 List 2 中搜索每个 List 1 项,这意味着迭代将发生 49 次!!!


6
投票

这可能是您可以直接使用 LINQ 扩展方法来检查 in 子句的可能方式

var result = _db.Companies.Where(c => _db.CurrentSessionVariableDetails.Select(s => s.CompanyId).Contains(c.Id)).ToList();

3
投票

我还尝试使用类似 SQL-IN 的东西 - 针对“实体数据模型”进行查询。我的方法是使用字符串生成器来组成一个大的 OR 表达式。这太难看了,但恐怕这是目前唯一的出路。 现在看起来像这样:

Queue<Guid> productIds = new Queue<Guid>(Products.Select(p => p.Key)); if(productIds.Count > 0) { StringBuilder sb = new StringBuilder(); sb.AppendFormat("{0}.ProductId = Guid\'{1}\'", entities.Products.Name, productIds.Dequeue()); while(productIds.Count > 0) { sb.AppendFormat(" OR {0}.ProductId = Guid\'{1}\'", entities.Products.Name, productIds.Dequeue()); } }

在此上下文中使用 GUID

:正如您在上面所看到的,查询字符串片段中 GUID 本身之前始终有单词“GUID”。如果你不添加这个,ObjectQuery<T>.Where会抛出以下异常:


参数类型“Edm.Guid”和 'Edm.String' 与此不兼容 操作., 接近等于表达式, 第 6 行,第 14 列。

在 MSDN 论坛中找到此内容,记住可能会有所帮助。


2
投票

首先,您可以像这样重写查询:

var matches = from Users in people where Users.User_Rights == "Admin" || Users.User_Rights == "Users" || Users.User_Rights == "Limited" select Users;

当然,这更“啰嗦”并且写起来很痛苦,但它仍然有效。

因此,如果我们有一些实用方法可以轻松创建此类 LINQ 表达式,我们就可以开展业务了。

有了实用方法,您可以编写如下内容:

var matches = ctx.People.Where( BuildOrExpression<People, string>( p => p.User_Rights, names ) );

这将构建一个具有相同效果的表达式:

var matches = from p in ctx.People where names.Contains(p.User_Rights) select p;

但更重要的是它实际上适用于 .NET 3.5 SP1。

这是使这成为可能的管道功能:

public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>( Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values ) { if (null == valueSelector) throw new ArgumentNullException("valueSelector"); if (null == values) throw new ArgumentNullException("values"); ParameterExpression p = valueSelector.Parameters.Single(); if (!values.Any()) return e => false; var equals = values.Select(value => (Expression)Expression.Equal( valueSelector.Body, Expression.Constant( value, typeof(TValue) ) ) ); var body = equals.Aggregate<Expression>( (accumulate, equal) => Expression.Or(accumulate, equal) ); return Expression.Lambda<Func<TElement, bool>>(body, p); }

我不会尝试解释此方法,只是说它本质上使用 valueSelector(即 p => p.User_Rights)为所有值构建一个谓词表达式,并将这些谓词组合在一起以创建一个表达式完整谓词

来源:

http://blogs.msdn.com/b/alexj/archive/2009/03/26/tip-8-writing-where-in-style-queries-using-linq-to-entities.aspx


0
投票

var trackList = Model.TrackingHistory.GroupBy(x => x.ShipmentStatusId).Select(x => x.Last()).Reverse(); List<int> done_step1 = new List<int>() {2,3,4,5,6,7,8,9,10,11,14,18,21,22,23,24,25,26 }; bool isExists = trackList.Where(x => done_step1.Contains(x.ShipmentStatusId.Value)).FirstOrDefault() != null;



0
投票
INTERSECT

这是一个工作示例

var selected = users.Where(u => new[] { "Admin", "User", "Limited" }.Intersect(new[] {u.User_Rights}).Any() ); OR var selected = users.Where(u => new[] {u.User_Rights}.Intersect(new[] { "Admin", "User", "Limited" }).Any() );

我想应该对性能进行基准测试(针对当前接受的答案)以充分验证该解决方案......

编辑:

正如 Gert Arnold 要求的一个例子(EF 6): 这段代码为我提供了名字和/或姓氏与“John”或“Doe”匹配的任何用户:

// GET: webUsers public async Task<ActionResult> Index() { var searchedNames = new[] { "John", "Doe" }; return View( await db .webUsers .Where(u => new[] { u.firstName, u.lastName }.Intersect(searchedNames).Any()) .ToListAsync() ); //return View(await db.webUsers.ToListAsync()); }



0
投票

string[] month = { "jan", "feb", "mar" }; var qry = from c in populationdata where c.birthmonth in month select c;

将从“populationdata”中选择记录,其中月份位于字符串数组“month”中。


-15
投票

where (t.MyTableId == 1 || t.MyTableId == 2 || t.MyTableId == 3)

© www.soinside.com 2019 - 2024. All rights reserved.