通过在Linq和EF中按条件分组表来获取详细信息

时间:2018-08-25 15:41:24

标签: c# sql-server entity-framework linq

我的SQL Server数据库中有以下3个表定义:

+----------------------------------+-----------------+------------------------------------------------
|             Product              | Rating                          |         Image                   | 
+----------------------------------+-----------------+-------------------------------------------------
| ProductId                        | Id                              | Id                              |
| ProdctName                       | Rating                          | Image                           |
|                                  | ProductId FK References(Product)| ProductId FK References(Product)|
+----------------------------------+-----------------+---------------+----------------------------------

这些表包含以下示例数据:

+----------------------------------+------------
|Product              | ProductId  |ProductName |
+----------------------------------+------------
|                     | 1          |Prodcuct 1  |
|                     | 2          |Prodcuct 2  |
|                     | 3          |Prodcuct 3  |
|                     | 4          |Prodcuct 4  |
+----------------------------------+------------+

+----------------------------------+----------------------+
|Rating              | Id          |RatingVal   |ProductId |
|+----------------------------------+-----------------------
|                     | 1          |3           |1         |
|                     | 2          |2           |2         | 
|                     | 3          |3           |2         |
|                     | 4          |5           |3         |
|                     | 5          |4           |3         |
+---------------------+------------+------------+----------+

+----------------------------------+----------------------+
|Image                | Id          |ImagePath  |ProductId
+----------------------------------+-----------------------
|                     | 1          |ABC           |1       |
|                     | 2          |XYZ           |2       | 
|                     | 3          |LMN           |3       |
|                     | 4          |PQR           |4       |
+---------------------+------------+------------+----------+

我需要在一个地方收集有关产品的信息,以便每个产品都包含有关产品的详细信息(来自产品表),相关的平均评分(来自评分表)m和产品的图像路径(来自图像表) )。换句话说,我需要以下输出:

+----------------------------------+--------------------------------+
|Output               | ProductId  |ProductName |Avg(Rating)|ImgPath|
+----------------------------------+--------------------------------+
|                     | 1          |Prodcuct 1  |3          |ABC    |
|                     | 2          |Prodcuct 2  |2.5        |XYZ    |
|                     | 3          |Prodcuct 3  |4.5        |LMN    |
|                     | 4          |Prodcuct 4  |0.0        |PQR    |
+----------------------------------+------------+-----------+-------+

我正在使用实体框架来获取此数据,并在我的代码中获取上下文类中的实体(如下所示)。

我的问题是:如何为所有产品产生所需的输出。 我下面的代码无法获取我想要的所有数据。问题是结果中未显示具有id4的产品,我认为这是因为产品4在评级表中没有条目。

var  trendingProducts = (from ratings in entities.Rating
                         group ratings by new { ratings.productId } into c
                         join products in entities.Products on c.FirstOrDefault().productId equals products.ProductID
                         join images in entities.Images on c.Key.productId equals images.ProductId                                       
                         select new ProductViewModel
                         {
                             ProductId = products.ProductId,
                             ProductName = products.ProductName,
                             RatingVal = c.Average(l => l.RatingVal) == null ? 0 : c.Average(l => l.Rating),
                             ImagePath = images.ImagePath,

                         }).ToList();

1 个答案:

答案 0 :(得分:0)

因此,您有一个Products表,一个Ratings表和一个Images表。

每个Product都具有零个或多个Ratings,每个Rating都使用外键Product恰好属于一个ProductId。同样,每个Product都有零个或多个Images,每个Image都使用外键Image恰好属于一个ProductId。只是标准的一对多关系。

可能每个Product都有零个或一个Image:在这种情况下,您有一个零个或一对一的关系。代码将相似。主要区别在于“产品”不会有Images的集合,而只会有一个虚拟的Image

如果您遵循entity framework code first conventions,您将拥有类似的课程:

class Product
{
    public int Id {get; set;}

    // every Product has zero or more Ratings:
    public virtual ICollection<Rating> Ratings {get; set;}

    // every product has zero or more Images:
    public virtual ICollection<Image> Images {get; set;}

    ... // other properties
}

class Rating
{
    public int Id {get; set;}

    // every Rating belongs to exactly one Product using foreign key:
    public int ProductId {get; set;}
    public virtual Product Product {get; set;} 

    ...
}

class Image
{
    public int Id {get; set;}

    // every Image belongs to exactly one Product using foreign key:
    public int ProductId {get; set;}
    public virtual Product Product {get; set;} 

    ...
}

这是实体框架检测到一对多关系所需了解的所有内容。它知道所需的表/列,并且知道表之间的关系。您可能希望为表或属性使用不同的标识符。在这种情况下,您将需要属性或流畅的API。但这超出了这个问题的范围。

请注意,在实体框架中,所有非虚拟属性都将成为表中的列。所有虚拟属性均表示表之间的关系。

  

我需要在一个地方收集有关产品的信息,以便   每个产品都包含有关产品的详细信息(来自产品   表),相关的平均评分(来自评分表)和图片   产品的路径(来自“图像”表)。

每当人们查询“带有子对象的对象”时,他们就会倾向于创建(组)联接。但是,如果您使用实体框架,则对这些查询使用ICollections会容易得多。如果使用ICollection,则实体框架将知道需要一个(组)联接。

var result = myDbContext.Products            // from the collection of Products
    .Where(product => ...)                   // select only those Products that ...
    .Select(product => new                   // from every remaining Product make one new
    {                                        // object with the following properties:
         // Select only the properties you actually plan to use!
         Id = product.Id,
         Name = product.ProductName,
         ...

         AverageRating = product.Ratings       // from all Ratings of this Product
             .Select(rating => rating.Rating)  // get the Rating value
             .Average(),                       // and Average it.

         Images = product.Images               // from all Images of this product
             .Select(image => image.ImagePath) // select the ImagePath
             .ToList(),

         // or if a Product has only zero or one Image:
         Image = product.Image?.ImagePath // if the product has an Image, get its ImagePath
                                          // or use null if there is no Image
    });

使用ICollections的好处是代码更简单,更自然,它看起来比(group)join更类似于您的需求文本。此外,如果涉及两个以上的表(组),则联接看起来很可怕。

我不会尝试使用GroupJoin给出解决方案。我相信其他答案也会显示出来。比较并亲自查看哪种解决方案似乎更容易理解。