【问题标题】:Left join using a different or not equal in Entity Framework在实体框架中使用不同或不相等的左连接
【发布时间】:2014-08-16 17:34:07
【问题描述】:

我必须构建一个查询来获取未收到的用户并提醒新帖子。

我的相关表结构如下:

帖子:postId int 用户:userId int、email varchar PostAlertUsers: postAlertUserId int, postId int, userId int

所有相关字段在表之间都有外键约束。

我在 SQL 中构建了这个查询,但找不到在 Entity Framework 中工作的方法:

SELECT u.email
FROM Users u
INNER JOIN Posts p ON p.userId != u.userId 
LEFT JOIN PostAlertUsers pu ON u.userId = pu.userId AND p.postId = pu.postId 
WHERE pu.postAlertUserId IS NULL

我编写了以下 EF 查询,但没有得到相同的结果:

from u in context.Users
join pu in context.PostAlertUsers on u.userId equals pu.userId into postAlerts
from pa in postAlerts.DefaultIfEmpty()
join p in context.Posts on pa.postId equals p.postId
where pa.userId != u.userId
select u.email;

如何使用 linq to entity 获得相同的结果。使用点语法(我不知道DbSet.Where(x => ...) 语法的正确术语)会更好。

编辑:

对于Posts 中不是来自同一用户的每个Post,我想获取在PostAlertUsers 上没有记录的所有用户。

编辑 2:

试图澄清一点:

对于每个帖子,我只想提醒用户一次其他用户的新帖子,我的例程将每小时运行一次以检查是否有人要发送消息。

我想获取尚未收到有关帖子警告的所有用户,这样它就不会在 PostAlertUsers 上记录此用户和帖子组合,但会记录来自其他用户的帖子。

示例数据:

Users
------------------------
userid | email
------------------------
1      | email1@test.com
2      | email2@test.com
3      | email3@test.com
------------------------

Posts (posts are created by users)
------------------------
postId | userId
------------------------
1      | 1
2      | 3
3      | 1
------------------------

PostAlertUsers (every time a user is notified about a new post, one record is added here)
------------------------
postId | userId
------------------------
1      | 2
1      | 3
2      | 1
------------------------

生成的查询将输出以下数据:

Result (using postId and userId to identify what user have to be notified for what post)
---------------------------------
postId | userId | email
---------------------------------
2      | 2      | email2@test.com
3      | 2      | email2@test.com
3      | 3      | email3@test.com
---------------------------------

编辑:

感谢 AD.Net,我提出了以下建议:

from u in context.Users
let posts = contexto.Posts.Where(p => p.userId != u.userId)
from p in posts
join pau in context.PostAlertUsers on u.userId equals pau.userId 
into alerts
from a in alerts.DefaultIfEmpty()
where a == null || a.postId != p.postId
orderby p.idPost
select new {
    p.postId,
    u.userId,
    u.email
}

【问题讨论】:

    标签: asp.net sql-server entity-framework linq-to-entities left-join


    【解决方案1】:

    编辑: 另一个角度的尝试,不确定性能,可能和cross-join一样糟糕

           from p in context.Posts
           let otherUsers = context.Users.Where(u => u.UserId != p.User.UserId)
           from u in otherUsers
           join pau in alerts on u.UserId equals pau.User.UserId into alert
           from a in alert.DefaultIfEmpty()
           where a == null || a.Post.PostId != p.PostId
           select new {p.PostId, u.Email};
    

    这是一个 LinqPad 尝试:

    void Main()
    {
         var users = new List<User>{new User{UserId = 1}, new User{UserId = 2}};
                var posts = new List<Post>
                {
                    new Post{PostId = 1, User = new User{UserId = 2}},
                    new Post{PostId = 2, User = new User{UserId = 1}},
                    new Post{PostId = 3, User = new User{UserId = 2}},
                };
                var alerts = new List<PostUserAlert>
                {
                    new PostUserAlert{Post = new Post{PostId = 1}, 
                                        User = new User{UserId = 1}}
                };
    
                var result = (from p in posts
                    let otherUsers = users.Where(u => u.UserId != p.User.UserId)
                    from u in otherUsers
                    join pau in alerts on u.UserId equals pau.User.UserId into alert
                    from a in alert.DefaultIfEmpty()
                    where a == null || a.Post.PostId != p.PostId
                    select new {p.PostId, u.UserId}).ToList();
    
                    result.Dump();
    }
    
    
     class User
        {
            public int UserId { get; set; }
            public string Email { get; set; }
        }
    
        class Post
        {
            public int PostId { get; set; }
            public User User { get; set; }
    
            public List<PostUserAlert>  PostUserAlerts { get; set; }
        }
    
        class PostUserAlert
        {
            public Post Post { get; set; }
            public User User { get; set; }
        }
    // Define other methods and classes here
    

    【讨论】:

    • 这是一对一的关系。您的第一个选项效果很好。它只是错过了我想获取尚未收到警报的用户,因此我将 where 子句更改为 where pa.Post.userId != u.userId &amp;&amp; pa = null 并且它按预期工作。谢谢。
    • 操作...我的错!我现在已经对其进行了测试,并且添加后它什么也没有返回。我可能遗漏了一些东西,但它不会返回我所期望的。我需要没有收到警报的用户,所以我必须得到在PostAlertUsers 上没有记录的users 中的每个Post 中的每个Posts 不是来自同一用户。跨度>
    • 不确定我是否正确地遵循了您的逻辑,但请检查更新。
    • 快到了。对于每个帖子,我只想警告用户一次来自其他用户的新帖子,我的例程将每小时运行一次。我必须让所有尚未收到有关帖子警告的用户,因此,它不会在PostAlertUsers 上为该用户和帖子记录,但会在Posts 上记录来自其他用户的记录。希望我让我更清楚,并感谢您的努力。
    • 是的。您的查询完成了这项工作。结果是预期的,生成的 SQL 查询接近我的原始查询。我正在做更多测试以确保它是正确的解决方案。
    猜你喜欢
    • 2013-05-15
    • 2013-03-15
    • 2011-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多