2017-08-21 56 views
0

我想用Linq做一個非常簡單的任務,並且已經卡住了。這真的很可怕,但我想,最好知道使用這個帖子。我有兩張桌子。一個是產品另一個是等級。以下是演示腳本:關於Linq查詢的問題

CREATE TABLE [dbo].[Products](
    [ProductID] [int] IDENTITY(1,1) PRIMARY KEY, 
    [ProductName] [varchar](40) NULL, 
    [Price] [float] NULL, 
    [Details] [varchar](max) NULL, 
    [CategoryID] [int] NULL 
) 

INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (1, N'Denim', 1200, NULL, 1) 
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (2, N'Denim 2', 220, NULL, 1) 
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (3, N'Pringles', 240, NULL, 2) 
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (4, N'Pringles 2', 260, NULL, 2) 
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (5, N'Pringles 3', 240, NULL, 2) 

CREATE TABLE [dbo].[Ratings](
    [AutoId] [int] IDENTITY(1,1) PRIMARY KEY, 
    [ProductId] [int] NOT NULL, 
    [UserRating] [float] NOT NULL 
) 

INSERT [dbo].[Ratings] ([AutoId], [ProductId], [UserRating]) VALUES (4, 2, 1.5) 
INSERT [dbo].[Ratings] ([AutoId], [ProductId], [UserRating]) VALUES (5, 4, 2.5) 
INSERT [dbo].[Ratings] ([AutoId], [ProductId], [UserRating]) VALUES (6, 1, 5) 
INSERT [dbo].[Ratings] ([AutoId], [ProductId], [UserRating]) VALUES (7, 2, 2.5) 

和輸出應該是以下幾點:

ProductId - ProductName - User Rating 
1 - Denim - 5 
2 - Denim 2 - 2 
3 - Pringles - 0 
4 - Pringles 2 - 2.5 
5 - Pringles 3 - 0 

這是一個項目的評級系統,我試圖讓使用Linq上面的輸出。用下面的Sql,我得到的結果是:

SELECT m.ProductId, m.ProductName, ISNULL(SUM(k.UserRating)/COUNT(k.ProductId), 0) AS 'User Rating' FROM Products m 
LEFT JOIN Ratings k ON k.ProductId = m.ProductID 
GROUP BY m.ProductID, m.ProductName 

不幸的是,以下Linq,我只得到,在評級存在表中的評級信息:

var con = (from c in db.Ratings 
      join d in db.Products on c.ProductId equals d.ProductID into ps 
      from rt in ps.DefaultIfEmpty() 
      group new { c, ps } by new { rt.ProductID, rt.ProductName } into g 
      select new ProductRating 
      { 
       ProductId = g.Key.ProductID, 
       ProductName = g.Key.ProductName, 
       Total = g.Sum(c => c.c.UserRating)/g.Count() 
      }).ToList(); 

注:我的目標是獲得評級表中不存在的評級詳細信息(產品評分總和)以及存在的評分詳情(如果沒有數據,則返回'0')。

更新1 - 類ProductRating

public class ProductRating { 
    public int ProductId { get; set; } 
    public string ProductName { get; set; } 
    public double Total { get; set; } //Property for the rating 
    } 

更新2 - 一流的產品和評級

public partial class Products 
{ 
    public int ProductID { get; set; } 
    public string ProductName { get; set; } 
    public Nullable<double> Price { get; set; } 
    public string Details { get; set; } 
    public Nullable<int> CategoryID { get; set; } 
    public ICollection<Ratings> Rating { get; set; } 
} 

public partial class Ratings 
{ 
    public int AutoId { get; set; } 
    public int ProductId { get; set; } 
    public double UserRating { get; set; } 
} 

使用了以下的Linq查詢並得到這個錯誤 - 指定LINQ to Entities不支持類型成員'Rating'。只有初始化,實體成員和實體導航屬性都支持

var con = db.Products.Select(c => new ProductRating 
      { 
       ProductId = c.ProductID, 
       ProductName = c.ProductName, 
       Total = c.Rating.Average(d => (double?)d.UserRating) ?? 0 
      }).ToList(); 

更新3:現在收到此錯誤 - 錯誤2016年爲條件指定的值不與成員的類型兼容,這是我試過的導航屬性:

Navigation Property
注:我修改Andrés Robinet的查詢和它的工作,雖然使用Join

+0

你已經搞亂了LINQ查詢中左外連接的左邊和右邊部分。你有沒有導航財產,像'公衆ICollection 評級{get;組; }'在'Product'類中?什麼是ORM - 例如LINQ to SQL,EF6,EF Core或? –

+0

我正在使用EF 6.0,它有一個名爲'Total'的屬性。在帖子中更新。 –

回答

1

你必須開始與Product。此外,您的Linq表達式中的連接順序與SQL中的順序相反。而且,group ... by ...只需再rt.UserRating值選擇

var con = (from d in db.Products 
      join c in db.Ratings on d.ProductId equals c.ProductId into ps 
      from rt in ps.DefaultIfEmpty() 
      group new { rt.UserRating } by new { d.ProductId, d.ProductName } into g 
      select new ProductRating 
      { 
       ProductId = g.Key.ProductId, 
       ProductName = g.Key.ProductName, 
       Total = g.Sum(r => r.UserRating)/g.Count() 
      }) 
      .ToList(); 

而且,如果g.Count()爲零,這將打破。但是,你有幾個選擇,反正:

  • 添加更復雜的查詢,並祈求EF能夠把它翻譯成SQL

    select new ProductRating 
    { 
        ProductName = g.Key.ProductName, 
        Total = g.Count() > 0 ? (g.Sum(r => r.UserRating)/g.Count()) : 0 
    } 
    
  • 重新定義ProductRating或使用一個輔助DTO,所以它需要SumCount屬性(計算平均按需,假設您仍可以擁有Total屬性)

    select new ProductRating 
    { 
        ProductName = g.Key.ProductName, 
        SumRating = g.Sum(r => r.UserRating), 
        CountRating = g.Count() 
    } 
    
+0

我試過你的解決方案@AndrésRobinet,它完美的工作。但是我只能得到「評級」表中存在的評級值。儘管試圖使其工作。謝謝。 –

1

你的LINQ語句有一個內部連接,而你的SQL有一個左連接。

+0

是的。在'Linq'中,我嘗試在ps.DefaultIfEmpty()**中用rt從'rt'使用''LEFT JOIN'。但沒有奏效。 –

2
from c in db.Ratings 
join d in db.Products on c.ProductId equals d.ProductID into ps 
from rt in ps.DefaultIfEmpty() 

是SQL Ratings LEFT OUTER JOIN Products的LINQ等效項,即與SQL查詢完全相反。

儘管可以修改LINQ查詢以匹配SQL查詢,但它沒有任何意義,因爲EF提供了一種更好的方法,它完全消除了在LINQ查詢中使用連接的需要 - 所謂的導航屬性(請參閱Don’t use Linq’s Join. Navigate!)。

var result = db.Products 
    .Select(p => new ProductRating 
    { 
     ProductId = p.ProductId, 
     ProductName = p.ProductName, 
     Total = p.Ratings.Average(r => (double?)r.UserRating) ?? 0 
    }).ToList(); 

通常情況下,Product類將使用這將是簡單的有一個集合導航屬性

public ICollection<Rating> Ratings { get; set; } 

代表之一ProductRating之間一對多的關係,以及相當於LINQ查詢更新:如果您沒有正確設置實體模型中的關係,則可以使用手動連接等效用group join更換導航屬性訪問上面的查詢:

var result = (
    from p in db.Products 
    join r in db.Ratings on p.ProductId equals r.ProductId into ratings 
    select new ProductRating 
    { 
     ProductId = p.ProductId, 
     ProductName = p.ProductName, 
     Total = ratings.Average(r => (double?)r.UserRating) ?? 0 
    }).ToList(); 
+0

我試過你的解決方案,它看起來很有前途@Ivan Stoev。現在,我收到了這個錯誤 - **指定的類型成員'Rating'在LINQ to Entities中不受支持。僅支持初始化器,實體成員和實體導航屬性。**我已經通過鏈接並手動更新了添加導航屬性的Product類。在帖子中更新。 –

+0

那麼,看看你的更新2,'公衆ICollection 評級{get;組; }'肯定是**實體導航屬性**,所以你不應該得到那個錯誤。除非在edmx(DataBase First)或流暢的API /數據註釋(Code First)中映射不正確。整個照片的某些部分不見了,你是唯一擁有它的人。 –

+0

我首先使用EF數據庫,希望沒有任何區別。雖然我在「產品」類中手動進行了更改,但它會有效還是需要做其他任何事情? –