简体   繁体   中英

How to union two complex queries querying complex objects?

I am quite new to entity framework so I am confused now. I have these two queries:

var usersQuery =
            from role in this.dbContext.Set<Model.ApplicationRole>()
            join ru in dbContext.Set<Model.ApplicationRoleUser>() on role.Id equals ru.ApplicationRoleId into rus
            from ru in rus.DefaultIfEmpty()
            join user in dbContext.Set<Model.User>() on ru.UserId equals user.Id into users
            from user in users.DefaultIfEmpty()
            where
                (ru != null && ru.ApplicationId == application.Id)
             && (roleId == null || ru.ApplicationRoleId == roleId.Value)
            group role by user into grps

            select
                new RoleActor
                {
                    Actor =
                        new Actor
                        {
                            AccountName = grps.Key.AccountName,
                            DisplayName =
                                (grps.Key.DisplayName ?? string.Empty) != string.Empty
                                    ? grps.Key.DisplayName
                                    : grps.Key.CommonName,
                            DomainName = grps.Key.DomainName,
                            Email = grps.Key.Email ?? string.Empty,
                            CompanyCode = grps.Key.CompanyCode,
                            AdGuid = grps.Key.AdGuid,
                            CommonName = grps.Key.CommonName
                        },
                    Type = Model.ActorType.User,
                    RoleNames = grps.Select(role => role.Name).ToList()
                };

        var groupsQuery =
            from role in this.dbContext.Set<Model.ApplicationRole>()
            join rg in dbContext.Set<Model.ApplicationRoleGroup>() on role.Id equals rg.ApplicationRoleId into rgs
            from rg in rgs.DefaultIfEmpty()
            join @group in dbContext.Set<Model.Group>() on rg.GroupId equals @group.Id into groups
            from @group in groups.DefaultIfEmpty()
            where
                (rg != null && rg.ApplicationId == application.Id)
             && (roleId == null || rg.ApplicationRoleId == roleId.Value)
            group role by @group into grps
            select
                new RoleActor
                {
                    Actor =
                        new Actor
                        {
                            AccountName = grps.Key.AccountName,
                            DisplayName =
                                (grps.Key.DisplayName ?? string.Empty) != string.Empty
                                    ? grps.Key.DisplayName
                                    : grps.Key.CommonName,
                            DomainName = grps.Key.DomainName,
                            Email = string.Empty,
                            CompanyCode = string.Empty,
                            AdGuid = grps.Key.AdGuid,
                            CommonName = grps.Key.CommonName
                        },
                    Type = Model.ActorType.Group,
                    RoleNames = grps.Select(role => role.Name).ToList()
                };

I need to union these two queries. But when I try to use extension method Union, I get exception:

The 'Distinct' operation cannot be applied to the collection ResultType of the specified argument. Parameter name: argument

I wanted to concat queries and then do something like:

queryConcatResult.GroupBy(x => x.Key).Select(x => x.FirstOrDefault())

However when I use method Concat, I get exception:

The nested query is not supported. Operation1='UnionAll' Operation2='MultiStreamNest'

I will be grateful for any hint.

It's not possible (at least with EF) to perform union or concat of queries that contain nested collection (for instance the result of group by or RoleNames as in your case).

The solution is to remove the grouping from the base queries, create uniform projection, concat and then do the grouping.

Something like this:

(1)

var usersQuery =
    from user in dbContext.Set<Model.User>()
    join ru in dbContext.Set<Model.ApplicationRoleUser>() on user.Id equals ru.UserId
    join role in this.dbContext.Set<Model.ApplicationRole>() on ru.ApplicationRoleId equals role.Id
    where ru.ApplicationId == application.Id
        && (roleId == null || ru.ApplicationRoleId == roleId.Value)
    select new
    {
        Actor = new Actor
        {
            AccountName = user.AccountName,
            DisplayName = (user.DisplayName ?? "") != "" ? user.DisplayName : user.CommonName,
            DomainName = user.DomainName,
            Email = user.Email ?? "",
            CompanyCode = user.CompanyCode,
            AdGuid = user.AdGuid,
            CommonName = user.CommonName
        },
        Type = Model.ActorType.User,
        Role = role,
    };

(2)

var groupsQuery =
    from @group in dbContext.Set<Model.Group>()
    join rg in dbContext.Set<Model.ApplicationRoleGroup>() on @group.Id equals rg.GroupId
    join role in this.dbContext.Set<Model.ApplicationRole>() on rg.ApplicationRoleId equals role.Id
    where rg.ApplicationId == application.Id
        && (roleId == null || rg.ApplicationRoleId == roleId.Value)
    select new
    {
        Actor = new Actor
        {
            AccountName = @group.AccountName,
            DisplayName = (@group.DisplayName ?? "") != "" ? @group.DisplayName : @group.CommonName,
            DomainName = @group.DomainName,
            Email = "",
            CompanyCode = "",
            AdGuid = @group.AdGuid,
            CommonName = @group.CommonName
        },
        Type = Model.ActorType.Group,
        Role = role,
    };

(3)

var actorsQuery =
    from a in usersQuery.Concat(groupsQuery)
    group a by new { a.Actor, a.Type } into g
    select new RoleActor
    {
        Actor = g.Key.Actor,
        Type = g.Key.Type,
        RoleNames = g.Select(a => a.Role.Name).ToList()
    };

Side note: Although using string.Empty is a good programming practice, you'd better avoid it in EF queries because EF does not recognize it as constant and generates additional SQL query parameters.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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