简体   繁体   English

SELECT和WHERE LINQ子句可以组合吗?

[英]Can SELECT and WHERE LINQ clause be combined?

Here is what I have done to Select users into my model and then remove all the null records: 以下是我在Select用户到我的模型中所做的工作,然后删除所有null记录:

        model.Users = users
            .Select(u =>
        {
            var membershipUser = Membership.GetUser(u.UserName);
            return membershipUser != null
                ? new UserBriefModel
                {
                    Username = u.UserName,
                    Fullname = u.FullName,
                    Email = membershipUser.Email,
                    Roles = u.UserName.GetRoles()
                }
                : null;
        })
            .Where(u => u != null)
            .ToList();

Wondering if there is a way to combine the SELECT and WHERE clause. 想知道是否有办法组合SELECTWHERE子句。

I tried: 我试过了:

        model.Users = users
            .Select(u =>
            {
                var membershipUser = Membership.GetUser(u.UserName);
                if (membershipUser != null)
                    return new UserBriefModel
                    {
                        Username = u.UserName,
                        Fullname = u.FullName,
                        Email = membershipUser.Email,
                        Roles = u.UserName.GetRoles()
                    };
            })
            .ToList();

But the intellisense suggest a syntax error. 但智能感知提示语法错误。 Which forces me to add a return null statement: 这迫使我添加一个return null语句:

        model.Users = users
            .Select(u =>
            {
                var membershipUser = Membership.GetUser(u.UserName);
                if (membershipUser != null)
                    return new UserBriefModel
                    {
                        Username = u.UserName,
                        Fullname = u.FullName,
                        Email = membershipUser.Email,
                        Roles = u.UserName.GetRoles()
                    };
                return null;
            })
            .ToList();

So what is the correct way to write this SELECT statement so only valid records are selected into my model? 那么编写这个SELECT语句的正确方法是什么,所以只有有效的记录被选入我的模型?

Conceptually you actually have three operations here: 从概念上讲,您实际上有三个操作:

  1. project the user name to a membership user 将用户名投影到成员资格用户
  2. filter out null membership users 过滤掉空会员用户
  3. project the membership users to a model 将成员用户投射到模型

That is how your query should be looking. 就是你的查询应该如何看待。 Your very first query has already tried to combine steps 1 and 3 together, but you're struggling because step two really should be in the middle of the two, and the hoops that you need to jump through to get around that aren't pretty. 你的第一个查询已经尝试将第1步和第3步结合在一起,但是你正在努力,因为第二步真的应该在两者的中间,而你需要跳过的箍来解决它们并不漂亮。

The query actually becomes simpler and readable (and becomes idiomatic LINQ code) when you represent all three operations individually. 当您单独表示所有三个操作时,查询实际上变得更简单和可读(并成为惯用的LINQ代码)。

model.Users = users
    .Select(user => new
    {
        user,
        membershipUser = Membership.GetUser(user.UserName)
    })
    .Where(pair => pair.membershipUser != null)
    .Select(pair => new UserBriefModel
    {
        Username = pair.user.UserName,
        Fullname = pair.user.FullName,
        Email = pair.membershipUser.Email,
        Roles = pair.user.UserName.GetRoles()
    })
    .ToList();

This is a query that can also be written more effectively in query syntax: 这是一个查询,也可以在查询语法中更有效地编写:

model.Users = from user in users
                let membershipUser = Membership.GetUser(user.UserName)
                where membershipUser != null
                select new UserBriefModel
                {
                    Username = user.UserName,
                    Fullname = user.FullName,
                    Email = membershipUser.Email,
                    Roles = user.UserName.GetRoles()
                };

As for the literal question of whether or not you can combine the projecting an filtering into a single LINQ operation, it is certainly possible. 至于你是否可以将过滤投射结合到单个LINQ操作中的字面问题,它肯定是可能的。 It would be an inappropriate solution to the problem, but the use of SelectMany can allow you to filter and project at the same time. 这对问题来说是不合适的解决方案,但使用SelectMany可以让您同时进行过滤和投影。 This can be done by projecting the item to either a one item sequence containing the value that you want to project it to or an empty sequence based on the predicate. 这可以通过将项目投影到包含要将其投影到的值的一个项目序列或基于谓词的空序列来完成。

model.Users = users
    .SelectMany(u =>
    {
        var membershipUser = Membership.GetUser(u.UserName);
        return membershipUser != null
            ? new[]{ new UserBriefModel
            {
                Username = u.UserName,
                Fullname = u.FullName,
                Email = membershipUser.Email,
                Roles = u.UserName.GetRoles()
            }}
            : Enumerable.Empty<UserBriefModel>();
    }).ToList();

Of course, every time you use this code, a kitten is killed. 当然,每次使用此代码时,都会杀死一只小猫。 Don't kill kittens; 不要杀小猫; use the earlier query instead. 请改用之前的查询。

I don't think this is possible, Select will map everything 1-1 as far as I know...if you're trying to filter you will need a Where . 我不认为这是可能的,据我所知, Select会映射所有内容...如果你想过滤你将需要一个Where

edit edit: I no longer believe SelectMany can do it (As Servy has shown). 编辑编辑:我不再相信SelectMany可以做到(正如Servy所示)。

I don't know about any Linq method which will allow you to arbitrary add or to not add the value into the resulting IEnumerable. 我不知道任何Linq方法,它将允许您任意添加或不将值添加到生成的IEnumerable中。

To do it the lambda(selector, predicate, filter...) should be able to control this addition. 要做到这一点,lambda(选择器,谓词,过滤器......)应该能够控制这个添加。 And only predicates(Where) are able to do it. 只有谓词(Where)才能做到。 In your case you will have to execute predicate(Where) and Select. 在您的情况下,您将必须执行谓词(Where)和Select。 There is no combinational method which will do both for you at the same time, except one non-direct method described in the end of the answer. 除了答案末尾描述的一种非直接方法之外,没有任何组合方法可以同时为您做这两种方法。

model.Users = users
   .Where(u => Membership.GetUser(u.UserName) != null)
   .Select(u =>
    {
       return new UserBriefModel
         {
             Username = u.UserName,
             Fullname = u.FullName,
             Email = Membership.GetUser(u.UserName).Email,
             Roles = u.UserName.GetRoles()
          };
    })
    .ToList();

We either get two Membership.GetUser(u.UserName) with such prefiltering or we will end with your original postfiltering. 我们要么得到两个Membership.GetUser(u.UserName)这样的预过滤,要么我们将以你原来的后过滤结束。

That's just shifting the complexity. 这只是改变了复杂性。 And it is difficult to say where the performance will be better. 而且很难说性能会更好。 It depends on whether the Membership.GetUser is fast and there are a lot of non-membership users - for my example. 这取决于Membership.GetUser是否很快,并且有很多非会员用户 - 就我的例子而言。 Or if Membership.GetUser is resource-consuming and there are few non-membership users your example with postfilter is better. 或者,如果Membership.GetUser消耗资源且非会员用户很少,那么使用postfilter的示例会更好。

As any performance based decision it should be thoroughly considered and checked. 作为任何基于绩效的决定,应该彻底考虑和检查。 In most cases the difference is minimal. 在大多数情况下,差异很小。

As it was already shown in the another post and pointed by Mr. 'Servy' it is possible to do so using one call of SelectMany SelectMany selecting either empty IEnumerable or 1-element array. 正如它已经在另一篇文章中展示并由'Servy'指出的那样,可以使用SelectMany SelectMany的一次调用来选择空IEnumerable或1元素数组。 But I still consider the first statement to be technically correct, because SelectMany returns collection of elements (it does not exactly add or doesn't add single element directly): 但我仍然认为第一个语句在技术上是正确的,因为SelectMany返回元素集合(它没有完全添加或不直接添加单个元素):

 model.Users = users
       .SelectMany(u =>
       {
           var membership = Membership.GetUser(u.UserName);

           if (membership == null)
               return Enumerable.Empty<UserBriefModel>();

           return new UserBriefModel[]
           {
               new UserBriefModel()
               {
                   Username = u.UserName,
                   Fullname = u.FullName,
                   Email = membership.Email,
                   Roles = u.UserName.GetRoles()
               }
           };
       })
        .ToList();

You can use a single method to accomplish this: 您可以使用单个方法来完成此任务:

private IEnumerable<UserBriefModel> SelectUserBriefModels(IEnumerable<User> users)
{
    foreach (var user in users)
    {
        var membershipUser = Membership.GetUser(user.UserName);
        if (membershipUser != null)
        {
            yield return new UserBriefModel
            {
                Username = user.UserName,
                Fullname = user.FullName,
                Email = membershipUser.Email,
                Roles = user.UserName.GetRoles()
            };
        }
    }
}

You would use it like this: 你会像这样使用它:

model.Users = SelectUserBriefModels(users);
model.Users = users
    .Where(u => u.Membership != null)
    .Select(u => new UserBriefModel
            {
                Username = u.UserName,
                Fullname = u.FullName,
                Email = u.Membership.Email,
                Roles = u.UserName.GetRoles()
            })
    .ToList();

First filter, then select. 首先过滤,然后选择。 For this solution you need to have a navigation property so you can do u.Membership.Email instead of the membershipUser.Email . 对于此解决方案,您需要具有导航属性,以便您可以使用u.Membership.Email而不是membershipUser.Email

My users look something like: 我的用户看起来像:

public class UserProfile
{
    // other properties

    public virtual Membership Membership { get; set; }
}

where Membership is the entity representing the membership table and is mapped via: 其中Membership是表示成员资格表的实体,并通过以下方式映射:

modelBuilder.Entity<Membership>()
   .HasRequired<UserProfile>(m => m.User)
   .WithOptional(u => u.Membership);

Then you can select everything with one query . 然后您可以使用一个查询选择所有内容。 Some other solutions here also work fine, but every call to Membership.GetUser(u.UserName) results in one additional DB call. 这里的一些其他解决方案也可以正常工作,但每次调用Membership.GetUser(u.UserName)导致一次额外的数据库调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM