LINQ 如何在一对多连接中指定选择某些列

Posted

技术标签:

【中文标题】LINQ 如何在一对多连接中指定选择某些列【英文标题】:LINQ how do I specify select certain columns in a one to many joins 【发布时间】:2021-12-09 14:18:10 【问题描述】:

我有一个 LINQ 查询的情况。它有两个连接(一对多),但它带回了连接表中的所有列。我不确定如何创建 LINQ 查询以仅返回连接表中的几个字段。

var data = from mc in ctx.MembershipChapters
           where mc.PartitionKey == controllerStateManager.PartitionKey && mc.MembershipId == membershipId
           join prd in ctx.Products on mc.ProductId 
           equals prd.Id into prods
           from prd in prods.DefaultIfEmpty()

           join oli in ctx.OrderLineItems on mc.OrderLineItemId equals oli.OrderLineItemId into olis
           from oli in olis.DefaultIfEmpty()
           select new
           
             MembershipName = mc.Membership.Name,
             Products = prods.Select(p => new  
ProductName = p.Name, ProductId = p.Id ),

OrderLineItems = olis.Select(o => new  OrderLineItemName = o.Description, OrderLineItemId = o.OrderLineItemId )
;
controllerStateManager.Data = data.ToList();

这不起作用...我收到一个错误:“o”不在范围内。

基本上输出应该是这样的:

会员章节 ---> OrderLineItems ----------> 产品

我是 LINQ 的新手,我已经为此苦苦挣扎了太久。

【问题讨论】:

您好,您的别名重复 oli/prd 并且连接顺序对我不利,我会这样写:from mc in ctx.MembershipChapters where mc.PartitionKey == controllerStateManager. PartitionKey && mc.MembershipId ==membershipId 在 mc.ProductId 上的 ctx.Products 中加入 prd 等于 prd.Id 进入 prods 在 mc.OrderLineItemId 上的 ctx.OrderLineItems 中加入 oli 等于 oli.OrderLineItemId 在 prods 中从 prd2 进入 olis.DefaultIfEmpty() 从 oli2在 olis.DefaultIfEmpty() 【参考方案1】:

如果您有一对多的关系,并且您想要“一”项,每个项都有其零个或多个子项,例如具有零个或多个学生的学校;具有零个或多个订单的客户,或者,如您的情况:具有 OrderLineItems 的 MembershipChapters,考虑使用 Queryable.GroupJoin 的重载之一。

如果您从“多”方面开始,并且您希望每个项目都有其一个父项目,因此您希望学生拥有他就读的学校,或者订单拥有唯一下订单的客户,请使用Queryable.Join 的重载之一。

我几乎总是使用带有参数resultSelector 的重载,因此您可以准确定义结果中的内容。

要求:给定表 MembershipChapters、OrderLineItems 和 Products。 MembershipChapters 和 OrderLineItems 之间存在一对多的关系。每个 MembershipChapter 都有零个或多个 OrderLineItem,每个 OrderLineItem 恰好属于一个 MembershipChapter,即外键所指的 MembershipChapter。 OrderLineItems 和 Products 之间存在类似的一对多关系。给我所有(或部分)MembershipChapter,每个 MembershipChapter 及其零个或多个 OrderlineItem,以及每个 OrderLineItem 及其零个或多个 Products。

var result = dbContext.MemberShipChapters
    .Where(membershipChapter => ...)       // only if you don't want all MembershipChapters
    .GroupJoin(dbContext.OrderLineItems,

    membershipChapter => membershipChapter.Id, // from every membershipChapter get the primary key
    orderlineItem => orderLineItem.MembershipChapterId, // from every OrderlineItem get the foreign key

    // parameter resultSelector: from every MembershipChapter with its zero or more
    // OrderLineItems, make one new:
    (membershipChapter, orderLineItemsOfThisMembershipChapter) => new
    
        // Select only the membershipChapter properties that you plan to use
        Id = membershipChapter.Id,
        Name = membershipChapter.Name,
        ...

        // The zero or more OrderLineItems of this membershipChapter
        OrderLineItems = orderLineItemsOfThisMembershipChapter
            .Select(orderLineItem => new
            
                // Select only the OrderLineItems that you plan to use:
                Id = orderLineItem.Id,
                ...

                // not needed, you already know the value
                // MembershipChapterId = orderLineItem.MembershipChapterId,
            )
            .ToList(),
    );

这很简单。然而,如果你想 GroupJoin 三个表,那么这看起来很糟糕,虽然它是可行的。

另一种看起来更简单的方法:

    var result = dbContext.MemberShipChapters
    .Where(membershipChapter => ...)
    .Select((membershipChapter => new
    
        Id = membershipChapter.Id,
        Name = membershipChapter.Name,
        ...

        OrderLineItems = dbContext.OrderLineItems

            // keep only the OrderLineItems with a foreign key referring to this MembershipChapter
            .Where(orderLineItem => orderLineItem.MemberShipChapterId == membershipChapter.Id)
            .Select(orderLineItem => new
            
                Id = orderLineItem.Id,
                ...


                // do the same with the third table
                Products = dbContext.Products
                .Where(product => product.OrderLineItemId == orderLineItem.Id)
                .Select(product => new
                
                     Id = product.Id,
                     Price = product.Price,
                     ...
                )
                .ToList(),
            )
            .ToList(),
        );

【讨论】:

【参考方案2】:

这有点难读,但如果域链接正确,那么我想你只想得到这样的查询:

from ol in ctx.OrderLines where
    ol.MembershipChapter.PartitionKey == controllerStateManager.PartitionKey
    select new ol.Whatever, ol.Product.Whatever;

【讨论】:

我很欣赏这些建议......但我真正的问题是我有一个 MembershipChapter 记录和两个数据列表。我需要知道如何从每个列表中的每个元素中选择 3 个属性: 选择新 MembershipName = mc.Membership.Name, Products = prods.Select(p => new ProductName = p.Name, ProductId = p.Id ), OrderLineItems = olis.Select( o => 新 OrderLineItemName = o.Description, OrderLineItemId = o.OrderLineItemId ) ;

以上是关于LINQ 如何在一对多连接中指定选择某些列的主要内容,如果未能解决你的问题,请参考以下文章

嵌套的一对多关系 sqlalchemy 过滤

如何在pe:gcharts中指定列类型

如何获取多核,多cpu系统中指定cpu的序列号

如何在表中指定基于 SQL Server 中另一列的计算列?

如何在 API 项目中指定实体框架连接字符串

实体框架Linq查询:如何在多个导航属性上从何处选择并从第三个导航属性中选择