【问题标题】:Complex GroupBy in C# LINQ lambda expressionsC# LINQ lambda 表达式中的复杂 GroupBy
【发布时间】:2018-03-24 18:56:44
【问题描述】:

对于长期使用 LINQ lambda 表达式的用户来说可能很容易,但我只是在这里碰壁......

我正在尝试获取具有特定 ID 的用户可以访问的所有区域对象。如果出于任何原因很重要,我正在使用实体框架。

context.AspNetUserRoles
    .Where(u => u.UserId == _currentUserId)
    .Select(ur => ur.AspNetRoles.RolePagePermissions.Select(pp => pp.Page.Area))
    .GroupBy(a => a.Select(g => g.Page.Select(gg => gg.Area)))
    .ToList()

我得到的是List<IGrouping<IEnumerable<IEnumerable<Area>>>,而不是List<Area>

有人知道如何轻松做到这一点吗?

由于许多页面可以在同一个区域中,并且角色与页面相关联,因此以下代码会返回许多重复区域(因为一个用户可以访问许多页面,因此每个页面返回一个区域):

context.AspNetUserRoles.Where(u => u.UserId == _currentUserId).SelectMany(ur => ur.AspNetRoles.RolePagePermissions.Select(pp => pp.Page.Area)).ToList()

这是用于测试的模型:

public partial class Area
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Area()
    {
        this.Page = new HashSet<Page>();
    }

    public System.DateTime CreationDate { get; set; }
    public int CreationUser { get; set; }
    public int Id { get; set; }
    public string Title { get; set; }
    public byte Activated { get; set; }
    public string SimpleLineIcon { get; set; }
    public string ControllerName { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Page> Page { get; set; }
}

public partial class AspNetRoles
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public AspNetRoles()
    {
        this.AspNetUserRoles = new HashSet<AspNetUserRoles>();
        this.RolePagePermissions = new HashSet<RolePagePermissions>();
    }

    public string Id { get; set; }
    public string Name { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<AspNetUserRoles> AspNetUserRoles { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<RolePagePermissions> RolePagePermissions { get; set; }
}

public partial class AspNetUserRoles
{
    public int Id { get; set; }
    public string UserId { get; set; }
    public string RoleId { get; set; }

    public virtual AspNetRoles AspNetRoles { get; set; }
    public virtual AspNetUsers AspNetUsers { get; set; }
}

public partial class AspNetUsers
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public AspNetUsers()
    {
        this.AspNetUserClaims = new HashSet<AspNetUserClaims>();
        this.AspNetUserLogins = new HashSet<AspNetUserLogins>();
        this.AspNetUserRoles = new HashSet<AspNetUserRoles>();
    }

    public string Id { get; set; }
    public string Email { get; set; }
    public bool EmailConfirmed { get; set; }
    public string PasswordHash { get; set; }
    public string SecurityStamp { get; set; }
    public string PhoneNumber { get; set; }
    public bool PhoneNumberConfirmed { get; set; }
    public bool TwoFactorEnabled { get; set; }
    public Nullable<System.DateTime> LockoutEndDateUtc { get; set; }
    public bool LockoutEnabled { get; set; }
    public int AccessFailedCount { get; set; }
    public string UserName { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<AspNetUserClaims> AspNetUserClaims { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<AspNetUserLogins> AspNetUserLogins { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<AspNetUserRoles> AspNetUserRoles { get; set; }
}

public partial class Page
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Page()
    {
        this.RolePagePermissions = new HashSet<RolePagePermissions>();
    }

    public System.DateTime CreationDate { get; set; }
    public int CreationUser { get; set; }
    public int Id { get; set; }
    public string Title { get; set; }
    public byte Activated { get; set; }
    public int AreaId { get; set; }
    public string SimpleLineIcon { get; set; }
    public string ActionName { get; set; }

    public virtual Area Area { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<RolePagePermissions> RolePagePermissions { get; set; }
}

public partial class RolePagePermissions
{
    public System.DateTime CreationDate { get; set; }
    public int CreationUser { get; set; }
    public int Id { get; set; }
    public string RoleId { get; set; }
    public int PageId { get; set; }
    public byte AccessAllowed { get; set; }
    public Nullable<System.DateTime> AccessAllowedByDate { get; set; }

    public virtual AspNetRoles AspNetRoles { get; set; }
    public virtual Page Page { get; set; }
}

【问题讨论】:

  • context.AspNetUserRoles.Where(u =&gt; u.UserId == _currentUserId).Select(ur =&gt; ur.AspNetRoles.RolePagePermissions.Select(pp =&gt; pp.Page.Area)).GroupBy(a =&gt; a.SelectMany(g =&gt; g.Page.Select(gg =&gt; gg.Area))).ToList()
  • 在第一次选择中使用SelectMany
  • @SoheilAlizadeh 得到了列表>>,所以少了一个 IEnumerable。任何想法如何仅获取 List
  • 你可以在你的问题中添加你的模型吗?我想测试一下。
  • 在问题中添加了模型

标签: c# entity-framework linq lambda


【解决方案1】:

我找到了返回所有可访问区域列表的解决方案之一:

context.AspNetUserRoles.Where(u => u.UserId == _currentUserId).SelectMany(ur => ur.AspNetRoles.RolePagePermissions.Select(pp => pp.Page.Area)).Distinct().ToList()

所以 .Distinct() 实现了这一切,感谢大家的帮助。

【讨论】:

    【解决方案2】:

    这应该是你想要的:

        context.AspNetUserRoles.Where(u => u.UserId == _currentUserId)
       .SelectMany(ur => ur.AspNetRoles.RolePagePermissions.Select(pp => pp.Page.Area));
    

    不确定你想通过 group by 完成什么,但这会给出区域列表。

    【讨论】:

    • 抱歉,已删除群组。你想按什么分组?
    • 许多页面可以属于同一个区域,所以我之前的代码返回了许多相同的区域,所以我想按区域分组。 Distinct 在这里提供了帮助并暂时解决了这个问题。
    【解决方案3】:

    在 LinqToEntities 中,Select 方法将返回您在表达式中选择的类型的 IQueryable(这与 LinqToObjects 的想法相同,但您有 Function Expression 作为 Select 方法的参数,它会返回一个 IEnumerable)。

    因此,如果您选择string 类型的属性,您将拥有IQueryable&lt;string&gt;,在您的情况下,您是在另一个列表中选择一个列表。你需要做的是使用SelectMany,它只是unwraps从你的Expression返回的集合,只给你一个包含所有项目的集合。

    你会得到这样的结果:

    List<Area> userAreas = context.AspNetUserRoles.Where(u => u.UserId == _currentUserId)
        .SelectMany(ur => ur.AspNetRoles.RolePagePermissions.Select(pp => pp.Page.Area)).ToList();
    

    由于您已经知道用户的 id,并且您的实体模型中存在用户与其区域之间的关系,因此没有必要使用GroupBy

    【讨论】:

    • 这是我之前尝试过的第一种方法,但我最终得到了这个错误:prntscr.com/ivuq8e任何想法为什么?
    • 第二个SelectMany应该是SelectArea不是一个集合
    • 谢谢@Camilo Terevinto 我已经修好了!
    猜你喜欢
    • 2013-03-08
    • 2013-03-29
    • 1970-01-01
    • 2011-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多