【问题标题】:Converting SQL with LEFT JOIN to Linq (Method Syntax)将带有 LEFT JOIN 的 SQL 转换为 Linq(方法语法)
【发布时间】:2021-02-23 17:20:05
【问题描述】:

这是我在这里的第一篇文章,所以如果我有任何问题,请告诉我,我会修复它。

我正在努力将带有左连接的简单 SQL 语句转换为 LINQ 语句(方法语法)。我不能使用 Linquer,因为这是一个 .Net Core 5.0 MVC 项目。

假设我有两个表:

dbo.OrganisationChannel (Id, OrganisationId, ChannelId)
dbo.Channel (Id, ChannelName, ChannelUrl)

我想显示一个组织目前没有的所有频道。

这是正确的 SQL 查询

SELECT c.Id,  c.ChannelName, c.ChannelUrl
FROM dbo.Channel c
LEFT JOIN dbo.OrganisationChannel oc ON  c.Id = oc.ChannelId
WHERE oc.ChannelId IS NULL OR oc.OrganisationId <> 1

但是,对应的.GroupJoin.SelectMany 让我很困惑。我找不到合适的地方添加 WHERE 子句:

var groupItems = db.Channel
    .GroupJoin(
        db.OrganisationChannel,
        c => c.Id,
        oc => oc.ChannelId,
        (c, oc) => new { c, oc })
    .SelectMany(
        x => x.oc.DefaultIfEmpty(),
        (chan, orgChan) => new
        {
            Id = chan.c.Id,
            ChannelName = chan.c.ChannelName,
            ChannelUrl = chan.c.ChannelUrl,
            IsActive = chan.c.IsActive,
        }
        );

如果能得到任何帮助,我将不胜感激,谢谢!

【问题讨论】:

  • 尝试以下操作:var groupItems = db.Channel.Where(oc=> oc.ChannelId == DbNull.Value || oc.OrganisationId != 1)
  • 你在使用实体框架吗?你可以试试:var groupItems = db.Channel.Include(x=&gt;x.OrganisationChannels).Where(x=&gt;x.OrganisationChannels.ChannelId == null || x.OrganisationChannels.OrganisationId != 1
  • 嗨 Kamalpreet - 是的,我在 .net Core 5.0 MVC 项目中使用 EF Core。
  • 嗨 jdweng - OrganisationId 是 OrchanisationChannel 表(右侧表)的一部分,因此在 oc 上下文中不可用。另外,.Where 子句会出现在 GroupJoin 之前还是之后?
  • 试试我上面的例子。

标签: c# asp.net-mvc linq asp.net-core


【解决方案1】:

带有 LEFT JOIN 的方法语法是一场噩梦。如果您真的想要方法语法,请安装 Reshaper 并单击“转换为方法链”。但我不建议这样做 - 查询变得不可维护。

查询语法很简单

var query = 
   from c in db.Channel
   join oc in db.OrganisationChannel on c.Id equals oc.ChannelId into gj
   from oc in gj.DefaultIfempty()
   where (int?)oc.ChannelId == null || oc.OrganisationId != 1
   select new 
   {
       c.Id,
       c.ChannelName, 
       c.ChannelUrl
   };

【讨论】:

  • 感谢 Svyatoslav,这完美地返回了所需要的。我也感谢有关方法语法的建议!
【解决方案2】:

您可以使用LeftJoin 扩展方法:

public static IQueryable<TResult> LeftJoin<TResult, Ta, Tb, TKey>(this IQueryable<Ta> TableA, IEnumerable<Tb> TableB, Expression<Func<Ta, TKey>> outerKeySelector, Expression<Func<Tb, TKey>> innerKeySelector, Expression<Func<JoinIntermediate<Ta, Tb>, Tb, TResult>> resultSelector)
{
    return TableA.GroupJoin(TableB, outerKeySelector, innerKeySelector, (a, b) => new JoinIntermediate<Ta, Tb> { Value = a, ManyB = b }).SelectMany(intermediate => intermediate.ManyB.DefaultIfEmpty(), resultSelector);
}

public class JoinIntermediate<Ta, Tb>
{
    public Ta Value { get; set; }

    internal IEnumerable<Tb> ManyB { get; set; }
}

它的用法类似于Join 扩展方法,但将执行左连接而不是常规连接。然后,您可以在调用 LeftJoin 之后立即将调用添加到 Where 方法。

【讨论】:

  • 应该举个例子。 NotificationEvents .Where(w => w.ID == 123) .LeftJoin( Events, events => events.EventID, ev => ev.EventID, (events, ev) => new { NotificationEvents = events, Events = ev } );
  • 嗨 Nalka - 我喜欢 LeftJoin 的简化,但我一直坚持将这些添加到我的表名中(抱歉,可能需要在早上看看这个 - 这几天很长! )
【解决方案3】:

使用以下查询代替 lambda 表达式

            from left in lefts
            join right in rights on left equals right.Left into leftRights
            from leftRight in leftRights.DefaultIfEmpty()
            select new { }

【讨论】:

  • 谢谢 Venkatesh - 请你稍微详细说明一下,给定我的两个表名称(Channel,OrganisationChannel),并且还需要过滤出 OrganisationChannel.OrganisationId 的位置!= 1
【解决方案4】:

查看此网址https://dotnettutorials.net/lesson/left-outer-join-in-linq/

也是我的工作代码示例:

 UserApiKeys
            .Where(w => w.AppID == AppID && w.IsActive)
            .Join(
                    UserApiApplications,
                    keys => keys.AppID,
                    apps => apps.AppID,
                    (keys, apps) => new { UserApiKeys = keys, UserApiApplications = apps}
                )
                .OrderByDescending(d => (d.UserApiKeys.ExpirationDate ?? DateTime.MaxValue))
                .Select(s => new {
                    ApiKey = s.UserApiKeys.ApiKey, 
                    IsActive = s.UserApiKeys.IsActive, 
                    SystemName = s.UserApiKeys.SystemName,
                    ExpirationDate = (s.UserApiKeys.ExpirationDate == null)
                                                                ? "Newer Expires"
                                                                : s.UserApiKeys.ExpirationDate.ToString(),
                    s.UserApiApplications
                })
                .ToList()

另外,参考@nalka关于扩展方法使用的帖子:

NotificationEvents
.Where(w => w.ID == 123)
.LeftJoin(
        Events, 
        events => events.EventID, ev => ev.EventID, 
        (events, ev) => new { NotificationEvents = events, Events = ev }
);
    

【讨论】:

  • 感谢 power Mouse - 仍在努力找出将 Id 的 Where 子句放置在连接表上的位置。
  • 您可以在末尾添加另一个 Where 条件 w=>w.ev.XXX == "yyy" 不要忘记您有 2 个链接对象的列表。在第一个示例中检查 OrderByDescending.. 相同的条件...我建议购买 Linqpad - 它有帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-26
  • 2015-04-17
相关资源
最近更新 更多