Linq + MoreLinq如何将一个结果道具汇总到列表中?

时间:2019-04-25 02:24:35

标签: c# linq morelinq

我对查询有疑问,请看看。 我的目标是:

  • 我需要将所有Products和一个Image一起使用。
  • Products具有Values,这是有关产品的一些附加信息,例如规格等。
  • ImageValues可以是null
  • 要返回所有ProductsImageValues
  • 对于Values,我只需要Ids,因此可以获取List<int> of Values
  • ProductValuesImageObjects连接关系表-> Products可以有很多ProductValuesProducts可以有很多ImageObjects,但是可以有一个Image
  • DistinctBy来自more linq

问题,我不知道如何以正确的方式汇总Values来返回每个Values的{​​{1}}列表

PS 我也在使用更多linq

Product

答案 var q1 = (from p in Products join pv in ProductValues on p.ProductId equals pv.ProductId into ljpv from pv in ljpv.DefaultIfEmpty() select new { ProductId = p.ProductId, Description = p.Description, Name = p.Name, Price = p.Price, Quantity = p.Quantity, Type = p.Type, Values = (from v in ValueTypes where v.ValueId == pv.ValueId select new { ValueId = v.ValueId }).ToList(), ImageObjects = (from io in ImageObjects where io.ProductId == p.ProductId && io.IsDefault == true select new { Image = io.Image, IsDefault = io.IsDefault, ProductId = io.ProductId }) .ToList() }) .DistinctBy(x=>x.Name) .OrderBy(x=>x.Name); q1.Dump();

我知道这不是回答的地方,但是meaby可以对我的代码或meaby有所帮助,可以更清楚或更快速地完成。 很长一段时间以来,我一直在想如何执行此查询,但是当我写信给您时,我很眼花:乱:)


Values = (from tmp in ljpv select new { ValueId = tmp.ValueId}),回答之后-代码更快!

@Harald Coppoolse

1 个答案:

答案 0 :(得分:2)

因此,您有一个Products表和一个ProductValues表,它们之间具有一对多关系:每个Product的零个或多个ProductValues以及每个{ {1}}恰好属于一个ProductValue,即外键Product指向的Product

您想要所有ProductId(的几个属性),每个Products及其Product。之后,您ProductValuesDistinctBy,但这不是您的问题。

只要您想要“带有子项的项目”,例如“带有学生的学校”,“带有订单的客户”,“带有订单行的订单”,请考虑使用Enumerable.GroupJoin

GroupJoin实际上是一个左外部联接,后跟一个GroupBy。

OrderBy

在您的情况下,您不想GroupJoin两个序列,而是三个序列。您需要做一个额外的GroupJoin:

var productsWithTheirProductValues = products.GroupJoin(  // GroupJoin Products
    productValues,                                        // with ProductValues
    product => product.ProductId,           // from every Product take the ProductId
    productValue => productValue.ProductId, // from every ProductValue take the foreign key

    // ResultSelector: take the product with its zero or more matching ProductValues
    // to make a new object:
    (product, productValuesOfThisProduct) => new
    {
        // Select only the product properties you plan to use:
        Id = product.Id,
        Name = product.Name,
        Price = product.Price,
        ...

        ProductValues = productValuesOfThisProduct
            // only if you don't want all ProductValues of this product:
            .Where(productValue => ...)   

            .Select(productValue => new
            {
                // again select only the properties you plan to use
                Id = productValue.Id,
                ...

                // not needed: the foreign key, you already know the value
                // ProductId = productValue.ProductId,
            })
            .ToList(),
    });

这看起来太可怕了。因此,如果您必须更频繁地使用三个表进行GroupJoin,请考虑为三个表创建一个GroupJoin:

var result = products.GroupJoin(productValues,
    product => product.ProductId,
    productValue => productValue.ProductId,

    // ResultSelector: remember the product and all its productValues
    (product, productValuesOfThisProduct) => new
    {
        Product = product,
        ProductValues = productValuesOfThisProduct,
    })

    // now do the 2nd join:
    .GroupJoin(imageObjects,
        firstJoinResult => firstJoinResult.Product.ProductId,
        imageObject => imageObject.ProductId,

        // result selector:
        (firstJoinResult, imageObjectsOfThisProduct) => new
        {
            Product = firstJoinResult.Product,
            ProductValues = firstJoinResult.ProductValues,
            ImageObjects = imageObjectsOfThisProduct,
        })

        // take each element of this group join result and select the items that you want
        .Select(joinResult => new
        {
            ProductId = joinResult.Product.ProductId,
            Price = joinResult.Product.Price,
            ...

            ProductValues = joinResult.ProductValues.Select(productValue => new
            {
                 ...
            })
            .ToList(),

            ImageObjects = joinResult.ImageObjects.Select(imageObject => new
            {
                ...
            })
            .ToList(),
       });

用法:

static IEnumerable<TResult> GroupJoin<T1, T2, T3, TKey, TResult>(
   this IEnumerable<T1> source1, IEnumerable<T2> source2, IEnumerable<T3> source3,
   Func<T1, TKey> key1Selector,
   Func<T2, TKey> key2Selector,
   Func<T3, TKey> key3Selector,
   Func<T1, IEnumerable<T2>, IEnumerable<T3>, TResult> resultSelector)
{
     // put all source2 and source3 elements in lookuptables, using the keyselector
     var lookup2 = source2.ToLookup(item => key2Selector(item));
     var lookup3 = source3.ToLookup(item => key3Selector(item));

     // now for every source1 item, get all source2 and source3 items with the same key
     // and create the result:
     foreach (var item1 in source1)
     {
         TKey key1 = key1Selector(item1);
         IEnumerable<T2> items2 = lookup2[key1];
         IEnumerable<T3> items3 = lookup3[key1];
         // returns empty collection if no items with this key

         TResult result = resultSelector(item1, items2, items3);
         yield return result;
    }
}