需要帮助创建复杂的 EF LINQ Select。

4

你好,我在创建一个复杂的 Linq 查询时遇到一些问题,需要进行多个连接和左连接操作。

我有四个表,如下所示。这是用于查看如何发送关于主题的新回复的电子邮件。因此,我获取加入了用户的所有帖子。同一用户在每个主题中可以有多个帖子。之后,我加入 UserEmailSetting,这是为了查看用户是否选择不接收电子邮件通知。最后,我需要知道是否已向用户发送了有关新回复的电子邮件(如果有大量回复,我不想垃圾邮件我的用户),因此,如果自上次访问网站以来已发送回复通知,则不想再发送另一封邮件。以下是我尝试过的代码,它能正常工作,但我想进行优化!

问题在于 UserEmailSetting 可能会有很多结果,因此当我实际上只返回 1 或 2 个结果时,我会得到很多结果。

以下是我的尝试:

var select = (from p in ForumPostRepository.Get()
              join u in UserRepository.Get() on p.UserId equals u.Id
              join ues in UsersEmailSettingRepository.Get() on u.Id equals ues.UserId

              join els in
                  (from _el in EmailLogRepository.Get()
                   where _el.Type == "ReplyToTopic" &&
                             _el.Values == topicId
                       orderby _el.Id descending 
                       select _el) on u.Id equals els.UserId 
                        into emailLogs

              from el in emailLogs.DefaultIfEmpty()

              where p.TopicId == forumTopic.Id &&
                    ues.ReplyToTopic //&& // We only want people who need notifications
                    //!u.Online // We only want people who are not online

              orderby p.Id descending, el.Id descending
              select new
              {
                  User = u,
                  EmailLog = el
              });

    var result = select.DistinctBy(x => x.User.Id).ToList();

这里是数据库类。
public class ForumPost
{
    public int? TopicId { get; set; }
    public int UserId { get; set; }
    ...
}

public class User
{
    public int Id { get; set; }
    public bool Online { get; set; }
    public DateTime LastLogin { get; set; }
    ...
}

public class UsersEmailSetting
{
    public int UserId { get; set; }
    public bool ReplyToTopic { get; set; }
}

public class EmailLog
{
    public int Id { get; set; }
    public int UserId { get; set; }
    public string Type { get; set; }
    public string Values { get; set; }
    public DateTime Created { get; set; }
}

更新:我希望更结构化地描述我想让Linq做什么,希望这能有所帮助。

  • 从ForumPostRepository获取所有主题ID为13的文章。
  • 在ForumPostRepository.UserId = UserRepository.Id的条件下与UserRepository进行连接。
  • 现在只想要不同的用户。
  • 在UserRepository.Id = UsersEmailSettingRepository.UserId的条件下与UsersEmailSettingRepository进行连接。
  • 在UserRepository.Id = EmailLogRepository.UserId并且EmailLogRepository.Type =“ReplyToTopic”以及EmailLogRepository.Values =“topicId”的条件下进行左连接。
  • -> 现在可以得到0至*个结果,但我只想要最新的一个!

有时候,直接编写SQL比尝试做这样的事情更好。 - Phill
你能展示一下你的类中有哪些导航属性吗? - Gert Arnold
我使用重复模式,因此无法访问导航属性。我将在2分钟内更新问题。 - Androme
我必须指出,你的EmailLog表是典型的数据库反模式中的消息队列。我将不得不搜索引用常见SQL问题的博客文章。但你会发现这个表很快增长,而很少使用旧数据。 - Aron
1
@DoomStone 存储库和导航属性并不是互斥的。割断自己使用导航属性就像把双手绑在背后,而你的前额却痒痒的一样。 - Gert Arnold
显示剩余2条评论
1个回答

2

不能保证这将是高性能的。

    var users = UserRepository.Get();
    var userEmailSettings = UsersEmailSettingRepository.Get();
    var emailLogs = EmailLogRepository.Get();
    var posts = ForumPostRepository.Get();
    var foo = 
        from user in users
        where posts
               .Any(post => post.UserId == u.Id && post.TopicId == topicId)
        where userEmailSettings
               .Any(ues => u.Id equals ues.UserId && ues.ReplyToTopic == true)
        where false == 
        (
                from el in emailLogs
                where el.Type == "ReplyToTopic"
                && el.Values == topicId
                && el.UserId == user.Id
                && el.Created > user.LastLogin
                select el
        ).Any()
        select user;

签名

Linq忍者

编辑:刚看到你没有导航属性。


感谢,我回家后会尝试它。 - Androme
我现在已经尝试过了,但是它不起作用 :( 我认为Linq本身是有效的,但我不认为EF知道如何翻译它! 代码:http://pastebin.com/XGJhJFdi 错误:http://i.imgur.com/asHjXJr.png - Androme
啊...明白了...叹气...典型...这应该解决问题。抱歉,我忘记闭合 IQueryable 而不是 Repository.Get() Func。这应该可以工作,但我要提醒你,一些 EF-Linq 提供程序可能无法正常工作,比如 MySQL。 - Aron
幸运的是,这个查询不会经常被触发,只有在发布新帖子时才会执行,但如果您认为直接执行SQL可以获得更好的结果,我也可以执行。 - Androme

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