LINQ Union与SQL Union的区别

3

我有一个涉及到用户用户项目之间关系的技术问题。 UserProjects.UserId引用了Users.UserId

我想找到在UserProjects中的User,并且UserProject具有UserProjects.ProjectId == 4。然后将其余User与此表合并。

实际上,我想更改UserProject = 4的一些User属性。

在MSSQL中,只是为了测试,我有以下查询

Select U.UserId 
from Users U
Join UserProjects UP
On U.UserId = up.UserId
Where up.ProjectId = 4
Union
Select U.UserId  From Users U

我没有改变任何属性。无论如何,结果显示了所有用户,就像应该的那样。

但是现在使用相同的查询更改属性后,结果显示比我拥有的用户更多。

 var usrs = ((from users in context.Users
                             join userProj in context.UserProjects
                             on users.UserId equals userProj.UserId
                             where userProj.ProjectId == projectId
                             select new ProjectUsersDTO
                             {
                                 UserName = users.Name,
                                 Rate = users.RatePerHour,
                                 UserId = users.UserId,
                                 alreadyInProject = true
                             })
                             .Union(from users in context.Users
                                    select new ProjectUsersDTO
                                    {
                                        UserName = users.Name,
                                        Rate = users.RatePerHour,
                                        UserId = users.UserId,
                                        alreadyInProject = false
                                    }))
                               .ToList();
                    return usrs;

当UNION不允许重复时会发生什么情况?

非常感谢!


只要对象是可比较和相等的,你的LINQ Union应该会产生唯一的对象。你所涉及的对象是ProjectUsersDTO。我认为你需要在这个对象上实现一个IEquatable接口,以便Union按照你的意图工作。此外,由于“alreadyInProject”属性不相同,你的值集略有不同。如果你的IEquatable实现忽略了这个属性,那么这就无关紧要了。 - S. Brentson
1
你正在使用IEnumerable.Union,因为你在联接之前将其投影到DTO。如果你想让数据库执行去重操作,那么你需要推迟将DTO投影到联接之后。所以在两个查询中都投影到匿名类型,然后进行联接,然后添加AsEnumerable()调用,然后将其投影到DTO,最后执行ToList()。 - Maarten
@Maarten 我正想着要加上那个。 - S. Brentson
3个回答

2
LINQ Union与SQL Union等价。
但是,使用相同查询但更改属性会比我拥有的结果更多。
这并不是相同的查询。在第一个(SQL)查询中,您仅选择(包括)一个字段(UserId),而在第二个(LINQ)查询中,您包括了多个字段,其中一个字段肯定是不同的。由于Union使用所有包括的字段作为唯一性判断的条件,因此第二个查询返回更多的项目是正常的。
话虽如此,让我们看看如何解决具体问题。看起来您根本不需要Union。通常应该有导航属性,因此像这样的简单查询就可以完成任务(假设导航属性名为Projects):
var query =
    from user in context.Users
    select new ProjectUsersDTO
    {
        UserName = user.Name,
        Rate = user.RatePerHour,
        UserId = user.UserId,
        alreadyInProject = user.Projects.Any(userProj => userProj.ProjectId == projectId)
    };

var result = query.ToList();

2

SQL UNION(自动执行DISTINCT)和LINQ Union(需要显式使用Distinct())之间的行为差异在这里有解释。

因此,对于您的特定情况,只需将Distinct()应用于查询中即可。


1
您提供的链接根本没有解释它们之间的区别。它只谈到了内存中的linq,而不是ef或l2s。 - Maarten

0

唯一性/无重复基于完整的记录/ProjectUsersDTO实例。

项目4中的用户(或变量projectId的任何值)将在结果中出现两次,一次为alreadyInProject = true,另一次为alreadyInProject = false。 第一个select仅选择项目中的用户,第二个select选择所有用户(包括项目中的用户)。它们被视为不同的记录,因为alreadyInProject的值不同,因此两个版本都将出现在输出中。

我不确定您想要什么,但我假设您想要一个所有用户列表,并指示他们是否在具有给定projectId的项目中。 您可以使用此代码

var usrs = (from users in context.Users
            join userProj in context.UserProjects
            on users.UserId equals userProj.UserId
            select new ProjectUsersDTO
            {
                UserName = users.Name,
                Rate = users.RatePerHour,
                UserId = users.UserId,
                alreadyInProject = (userProj.ProjectId == projectId)
            })
            .ToList();
return usrs;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接