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

时间:2014-06-18 14:21:05

标签: c# linq

以下是我对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();

想知道是否有办法合并SELECTWHERE子句。

我试过了:

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

但是intellisense提示语法错误。这迫使我添加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();

那么编写这个SELECT语句的正确方法是什么,所以只有有效的记录被选入我的模型?

5 个答案:

答案 0 :(得分:7)

从概念上讲,你实际上有三个操作:

  1. 将用户名投射到成员资格用户
  2. 过滤掉空会员用户
  3. 将会员用户投射到模型
  4. 是您的查询应该如何查找。你的第一个查询已经尝试将第1步和第3步结合在一起,但是你正在努力,因为第二步真的应该在两者的中间,而你需要跳到的第二步到处都不漂亮。

    当您单独表示所有三个操作时,查询实际上变得更简单和可读(并成为惯用的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();
    

    这是一个查询,也可以在查询语法中更有效地编写:

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

    至于您是否可以将投影过滤组合到单个LINQ操作中的字面问题,肯定是可能的。这对问题来说是不合适的解决方案,但使用SelectMany可以让您同时进行过滤和投影。这可以通过将项目投影到包含要将其投影到的值的一个项目序列或基于谓词的空序列来完成。

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

    当然,每次使用此代码时,都会杀死一只小猫。不要杀小猫;请改用之前的查询。

答案 1 :(得分:1)

我不认为这是可能的,Select将根据我的知识对所有内容进行映射...如果您尝试过滤,则需要Where

编辑编辑: 我 不再 相信SelectMany可以做到(正如Servy所示)。

答案 2 :(得分:1)

我不知道任何Linq方法,它允许您任意添加或不将值添加到生成的IEnumerable中。

要做到这一点,lambda(选择器,谓词,过滤器......)应该能够控制这个添加。只有谓词(Where)才能做到。在您的情况下,您将必须执行谓词(Where)和Select。除了答案末尾描述的一种非直接方法外,没有一种组合方法可以同时为你做这两种方法。

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

我们要么得到两个Membership.GetUser(u.UserName)这样的预过滤,要么我们将以你原来的后过滤结束。

这只会改变复杂性。而且很难说性能会更好。 这取决于Membership.GetUser是否很快,并且有很多非会员用户 - 就我的例子而言。或者,如果Membership.GetUser耗费资源且非会员用户很少,那么使用postfilter的示例会更好。

任何基于绩效的决定都应该仔细考虑和检查。在大多数情况下,差异很小。

因为它已经在另一篇文章中显示并且由'Servy'先生指出,所以可以使用SelectMany SelectMany的一次调用来选择空的IEnumerable或1元素数组。但我仍然认为第一个语句在技术上是正确的,因为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();

答案 3 :(得分:0)

您可以使用一种方法来完成此任务:

    operationTypeButton.rx.tap
        .asObservable()
        .withLatestFrom(viewModel.availableOperationTypes)
        .subscribe(onNext: { [unowned self] (operationTypes) in
            self.presentPicker(forOperationTypes: operationTypes)
        })
        .addDisposableTo(disposeBag)

您可以这样使用它:

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

答案 4 :(得分:-1)

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

首先过滤,然后选择。对于此解决方案,您需要具有导航属性,这样您才能u.Membership.Email而不是membershipUser.Email

我的用户看起来像:

public class UserProfile
{
    // other properties

    public virtual Membership Membership { get; set; }
}

其中Membership是表示成员资格表的实体,并通过以下方式进行映射:

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

然后,您可以使用一个查询选择所有内容。此处的其他一些解决方案也可以正常工作,但每次调用Membership.GetUser(u.UserName)都会导致一次额外的数据库调用。