我的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 |
+----------------------------------+------------+-----------+-------+
我正在使用Entity Framework来获取此数据,并在我的代码中获取上下文类中的实体(如下所示)。
我的问题是:如何为所有产品生成所需的输出。我下面的代码无法获取我想要的所有数据。问题是结果中没有显示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();
所以你有一张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
,Entity Framework将知道需要(组)连接。
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的好处在于代码更简单,更自然,它看起来更像您的需求文本而不是(组)连接。此外,如果涉及两个以上的表(组)连接往往看起来很可怕。
我不会尝试使用GroupJoin
提供解决方案。我相信其他答案会显示出来。比较并亲眼看看哪种解决方案似乎更容易理解。