Linq to Entities - SQL "IN"条款

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

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

如何复制在一个LINQ实体查询?这可能吗?

224651 次浏览

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

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

< em >查询语法:< / em >

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;
}

< em >方法语法:< / em >

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;
}

我还尝试了一个类似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”和 的电火花。字符串`不兼容 操作。,近似等于表达式, 第6行,第14列。

在MSDN论坛上找到了这个,可能会有帮助。

马蒂亚斯

... 期待下一个版本的。net和实体框架,当一切都变得更好。:)

这应该够你的目的了。它比较两个集合,并检查其中一个集合的值是否与另一个集合中的值匹配

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

严重吗?你们从没用过

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

在这种情况下,我将使用内连接。如果我使用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次!!

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

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

一个替代BenAlabaster回答的方法

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

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)为所有值构建一个谓词表达式,并将这些谓词or在一起,为完整的谓词创建一个表达式

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

真实的例子:

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;

这不是确切的IN操作符,但它可能帮助你得到预期的结果,也许是一种更通用的方法(因为它允许比较两个集合):相交

下面是一个工作示例

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": < / p >

// 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());
}