使用嵌套选择的Linq查询

时间:2018-03-23 06:29:43

标签: c# .net linq entity-framework-6

试图找出在linq语句中表达以下SQL并且无处可去的正确方法。

select messageid, comment 
from Message 
where isactive = 1 and messageid in 
(
    select messageid 
    from org.Notification 
    where NotificationTypeId = 7 and orguserid in 
    (
        select OrgUserId 
        from org.OrgUser
        where OrgId in 
        (
            select ChildOrgId from OrgRelation
            where ParentOrgId = 10001
        )
    )
    group by messageid
)

这在SQL Server中按原样运行,但我想在C#(E-F)代码中使用此查询,而我似乎在圈中运行。

4 个答案:

答案 0 :(得分:2)

显然Notification个对象有一个messageId。多个Notification个对象可能具有相同的MessageId值。

  

我希望Messages的Id等于其中一个   所有MessageId个对象的子选择的Notification值。

首先,我将向您展示一个解决您问题的LINQ查询,然后我将告诉您有关实体框架的一些信息,这将使这种LINQ查询更易于理解和维护。

直接解决方案

(1)选择应提取邮件的通知:

IQueryable<Notification> notificationsToUse = org.Notifications
    .Where(notification => notification.TypeId == 7
          && ....);

这是你的内心选择。我不确定NotificationsOrgUsersOrgRelations之间的关系。但这不在这个问题之内。

(2)提取所有使用过的MessageIds {/ 1>

Notifications

(3)使用“messageIdsUsedByNotificationsToUse

中的ID获取所有活动消息
IQueryable<int> messageIdsUsedByNotificationsToUse = notificationsToUse
    .Select(notification => notification.MessageId)
    // remove duplicates:
    .Distinct();

(4)您不想要完整的消息,只需要IQueryable<Message> fetchedActiveMessages = org.Messages .Where(message => message.IsActive && messageIdsUsedByNotificationsToUse.Contains(message.Id)); MessageId

Comment

TODO:如果需要:制作一个大的LINQ语句。

到目前为止,您还没有访问过该数据库。我只更改了IQueryable中的Expression。使它成为一个很大的LINQ语句不会提高性能,我怀疑它是否会提高可读性和可维护性。

使用实体框架的可能性的解决方案

似乎var result = fetchedActiveMessages.Select(message => new { MessageId = message.Id, Comment = message.Comment, }); Message之间存在一对多关系:每个Notification都有零个或多个Message,每个Notifications属于使用外键Notification只需一Message

如果您坚持entity framework code-first conventions,,那么您设计的课程类似于以下内容。 (强调一对多的关系):

MessageId

这是所有实体框架需要知道您计划class Message { public int Id {get; set;} // every Message has zero or more Notifications (one-to-many) public virtual ICollection<Notification> Notifications {get; set;} ... // other properties } class Notification { public int Id {get; set;} // every Notifications belongs to exactly one Message using foreign key public int MessageId {get; set;} public virtual Message Message {get; set;} ... // other properties } class MyDbContext : DbContext { public DbSet<Message> Messages {get; set;} public DbSet<Notification> Notifications {get; set;} } Messages之间的一对多关系。实体框架知道您打算将哪些属性作为主键和外键,并且它知道表之间的关系。

有时候有充分的理由偏离惯例。这需要使用属性或流畅的API来解决。

重要的是具有从NotificationsMessage的虚拟ICollection以及从Notification到它所属的Notification的虚拟参考的结构。

如果你设计了这样的课程,你的查询将是小菜一碟:

(1)选择您要使用的通知:

Message

(2)现在您可以直接选择属于这些通知的消息:

IQueryable<Notification> notificationsToUse = ... same as above

因为每个通知只属于一条消息,所以我确定没有重复。

继续:只有活跃消息的var result = notificationsToUse.Select(notification => notification.Message) MessageId

Comment

我不确定 .Where(message => message.IsActive) .Select(message => new { MessageId = message.Id, Comment = message.Comment, }); NotificationsOrgUsers之间的关系。如果你设计你的类使它们代表一个正确的一对多或多对多的关系,那么即使表达式(1)也会更加简单。

答案 1 :(得分:1)

您可以将查询细分为3个不同的部分,如下所示 -

  1. 首先过滤 OrgUserIds
  2. var filteredOrgUserIds = dc.OrgUsers.Where(u => dc.OrgRelations.Where(o 
        =>o.ParentOrgId == 10001).Select(d => 
        d.ChildOrgId).ToList().Contains(u.OrgId))
        .Select(uid => uid.OrgUserId)
        .Distinct()
        .ToList();
    
    1. 使用 filteredOrgUserIds 过滤掉通知。
    2. var filteredNotifications = dc.Notifications.Where(n => 
           n.NotificationTypeId == 7 && filteredOrgUserIds.Contains(n.OrgUserId))
           .Select(m => m.messageid)
           .Distinct()
           .ToList();
      
      1. 最后,通过查看 filteredNotifications 来过滤掉消息
      2.     m.isactive == 1 && filteredNotifications.Contains(m.messageid))
            .Select(m => new { message = m.messageid, comment = m.comment })
            .Distinct()
            .ToList();
        

        这应该可行。请从您的角度尝试一次,如果有帮助请告诉我们。

答案 2 :(得分:0)

从我的Recipe转换SQL到LINQ转换为SQL:

  1. 将子选择转换为单独声明的变量。
  2. 翻译LINQ子句中的每个子句,将monadic和aggregate运算符(DISTINCTTOPMINMAX等)转换为应用于整个LINQ查询的函数
  3. SELECT字段必须替换为select new { ... },以创建包含所有所需字段或表达式的匿名对象。
  4. IN翻译为.Contains(),将NOT IN翻译为! ... Contains()
  5. 注意到SQL查询在使用GROUP BY时使用DISTINCT,然后应用配方规则:

    var childOrgs = from or in OrgRelation where or.ParentOrgId == 1001 select or.ChildOrgId;
    var orgUsers = from ou in OrgUser where childOrgs.Contains(ou.OrgId) select ou.OrgUserId;
    var notificationMessages = (from n in Notification
                                where n.NotificationTypeId == 7 && orgUsers.Contains(n.orguserid)
                                select n.messageid).Distinct();
    var ans = from m in Message
              where m.isactive == 1 && notificationMessages.Contains(m.messageid)
              select new { m.messageid, m.comment };
    

答案 3 :(得分:-1)

如果您需要使用IN子句,则应使用Contains方法。