在 T-SQL 中,您可以进行如下查询:
SELECT * FROM Users WHERE User_Rights IN ("Admin", "User", "Limited")
您将如何在 LINQ to Entities 查询中复制它?甚至可能吗?
在 T-SQL 中,您可以进行如下查询:
SELECT * FROM Users WHERE User_Rights IN ("Admin", "User", "Limited")
您将如何在 LINQ to Entities 查询中复制它?甚至可能吗?
你需要从你思考它的方式来改变它。不是在预定义的一组适用用户权限中查找当前项目的用户权限,而是询问一组预定义的用户权限是否包含当前项目的适用值。这与在 .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;
}
这应该足以满足您的目的。它比较两个集合并检查一个集合的值是否与另一个集合中的值匹配
fea_Features.Where(s => selectedFeatures.Contains(s.feaId))
在这种情况下,我会选择 Inner Join。如果我使用包含,它会迭代 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
使用包含,它将搜索列表 2 中的每个列表 1 项目,这意味着迭代将发生 49 次!!!
这可能是您可以直接使用 LINQ 扩展方法检查 in 子句的可能方式
var result = _db.Companies.Where(c => _db.CurrentSessionVariableDetails.Select(s => s.CompanyId).Contains(c.Id)).ToList();
我还尝试使用类似 SQL-IN 的东西 - 查询Entity Data Model。我的方法是一个字符串构建器来组成一个大的 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 ifself 之前总是有单词“GUID”。如果您不添加此内容,则会ObjectQuery<T>.Where
引发以下异常:
参数类型“Edm.Guid”和“Edm.String”与此操作不兼容。接近等于表达式,第 6 行,第 14 列。
在 MSDN 论坛中找到了这个,可能会有所帮助。
马蒂亚斯
... 期待 .NET 和 Entity Framework 的下一个版本,届时一切都会变得更好。:)
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 在一起以创建一个完整的表达式谓词
真实例子:
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 运算符,但它可能会帮助您获得预期的结果,并且可能是一种更通用的方法(因为它允许比较两个集合):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());
}
严重地?你们从来没用过
where (t.MyTableId == 1 || t.MyTableId == 2 || t.MyTableId == 3)