Linq to Entities - SQL“IN” 条款

发布于 2024-07-20 17:14:47 字数 170 浏览 4 评论 0 原文

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

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

如何在 LINQ to Entities 查询中复制该查询? 有可能吗?

In T-SQL you could have a query like:

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

How would you replicate that in a LINQ to Entities query? Is it even possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

请别遗忘我 2024-07-27 17:14:47

你需要彻底改变你的思考方式。 您不是通过“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;
}

在这种情况下,我个人的偏好可能是方法语法, 知道发生了什么,但正如您所看到的,这大大压缩了代码。

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

另一种方法甚至不使用 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;
}

You need to turn it on its head in terms of the way you're thinking about it. Instead of doing "in" to find the current item's user rights in a predefined set of applicable user rights, you're asking a predefined set of user rights if it contains the current item's applicable value. This is exactly the same way you would find an item in a regular list in .NET.

There are two ways of doing this using LINQ, one uses query syntax and the other uses method syntax. Essentially, they are the same and could be used interchangeably depending on your preference:

Query Syntax:

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

Method Syntax:

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

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

My personal preference in this instance might be method syntax because instead of assigning the variable, I could do the foreach over an anonymous call like this:

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

Syntactically this looks more complex, and you have to understand the concept of lambda expressions or delegates to really figure out what's going on, but as you can see, this condenses the code a fair amount.

It all comes down to your coding style and preference - all three of my examples do the same thing slightly differently.

An alternative way doesn't even use LINQ, you can use the same method syntax replacing "where" with "FindAll" and get the same result, which will also work in .NET 2.0:

foreach(User u in users.FindAll(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
    //Do stuff on each selected user;
}
伪心 2024-07-27 17:14:47

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

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

This should suffice your purpose. It compares two collections and checks if one collection has the values matching those in the other collection

fea_Features.Where(s => selectedFeatures.Contains(s.feaId))
娇柔作态 2024-07-27 17:14:47

在这种情况下我将选择内部连接。 如果我使用 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(); 

Contains 的缺点

假设我有两个列表对象。

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

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

I will go for Inner Join in this context. If I would have used contains, it would iterate 6 times despite if the fact that there are just one match.

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

Disadvantages of Contains

Suppose I have two list objects.

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

Using Contains, it will search for each List 1 item in List 2 that means iteration will happen 49 times !!!

佼人 2024-07-27 17:14:47

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

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

This could be the possible way in which you can directly use LINQ extension methods to check the in clause

var result = _db.Companies.Where(c => _db.CurrentSessionVariableDetails.Select(s => s.CompanyId).Contains(c.Id)).ToList();
风为裳 2024-07-27 17:14:47

我还尝试使用类似 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.Where 会引发以下异常:

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

在 MSDN 论坛中找到了这一点,记住这一点可能会有所帮助。

I also tried to work with an SQL-IN-like thing - querying against an Entity Data Model. My approach is a string builder to compose a big OR-expression. That's terribly ugly, but I'm afraid it's the only way to go right now.

Now well, that looks like this:

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

Working with GUIDs in this context: As you can see above, there is always the word "GUID" before the GUID ifself in the query string fragments. If you don't add this, ObjectQuery<T>.Where throws the following exception:

The argument types 'Edm.Guid' and
'Edm.String' are incompatible for this
operation., near equals expression,
line 6, column 14.

Found this in MSDN Forums, might be helpful to have in mind.

对你的占有欲 2024-07-27 17:14:47

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)和将这些谓词组合在一起以创建完整谓词的表达式

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

An alternative method to BenAlabaster answer

First of all, you can rewrite the query like this:

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

Certainly this is more 'wordy' and a pain to write but it works all the same.

So if we had some utility method that made it easy to create these kind of LINQ expressions we'd be in business.

with a utility method in place you can write something like this:

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

This builds an expression that has the same effect as:

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

But which more importantly actually works against .NET 3.5 SP1.

Here is the plumbing function that makes this possible:

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

I'm not going to try to explain this method, other than to say it essentially builds a predicate expression for all the values using the valueSelector (i.e. p => p.User_Rights) and ORs those predicates together to create an expression for the complete predicate

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

轮廓§ 2024-07-27 17:14:47

真实例子:

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;

Real example:

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;
忆梦 2024-07-27 17:14:47

这不完全是 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());
}

This isn't exactly the IN operator, but it might help you get the expected result and maybe a more generic approach (as it allows two collections to be compared) : INTERSECT

here's a working example

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

I guess performance should be benchmarked (against the currently accepted answer) to fully validate this solution...

EDIT :

As Gert Arnold asked for an example (EF 6) :
This piece of code gives me any user whose first and/or last name matches "John" or "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());
}
宣告ˉ结束 2024-07-27 17:14:47

查询语法:

string[] month = { "jan", "feb", "mar" };

var qry = from c in populationdata
            where c.birthmonth in month
            select c;

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

Query syntax:

string[] month = { "jan", "feb", "mar" };

var qry = from c in populationdata
            where c.birthmonth in month
            select c;

Will select records from "populationdata" where the month is "in" the array of strings "month".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文