0

「group by」文を使用する linq の結果であるオブジェクト IGrouping について疑問があります。

データベースに Products と Responses の 2 つのテーブルがあり、1 と * の関係があります。Responses テーブルには、製品のレートである FinalRate という列があります。製品には、n 個の応答または率を指定できます。

FinalRate の合計を完了したレートの数で割って Products の注文を取得したいと考えています。つまり、平均レートが高いものから低いものへと降順で並べます。

コードで(質問の最後で)読み取ることができるので、最初に応答を取得しようとします。すべての finalrate を合計し、それらをカウントで割るために、グループを使用します。

現在のコードが機能する場合でも、コードには 2 つの問題があります。

1.-単一のクエリで Products を取得しようとしましたが、グループで products テーブルを使用できず、"orderby" で Response テーブルを使用できないため、取得できません。もう1つ、LINQでは1つのテーブルをグループ化する可能性しかありません。「グループ化、応答」を行うことは不可能です。LINQ でこの sql 文を取得できませんでした:

select prod.ProductID,prod.Commercial_Product_Name,prod.Manufacturer_Name,
prod.ProductImageUrl  
from rev_product prod  
inner join rev_response res on res.AtProductid=prod.ProductID 
group by prod.ProductID,prod.Commercial_Product_Name,prod.Manufacturer_Name
,prod.ProductImageUrl  
order by (sum(res.FinalRate)/count(res.AtProductid))

私はこれを試しました:

var gruposproductos = (from prod in ctx.Products 
                       join res in ctx.Responses on prod.ProductID equals res.AtProductId  
                       group  prod by prod.ProductID into g    
                       orderby (g.Sum(ra =>ra.FinalRate)/g.Count())
                       descending select g).Take(2);

しかし、私が言うように、「orderby (g.Sum...」はエラーになります。これは、「into g」が応答テーブルではなく製品テーブルをグループ化するためです。

これが、最終的なコードで、同じ LINQ 文で製品を取得できない理由です。

2.-この事実を受け入れると、問題は IGrouping を取得することですが、コードで 2 つの foreach を実行しないと反復できる応答のリストを取得できません。「リスト」オブジェクトの場合と同様に、ループを 1 つだけにしたかったのです。

それは本当にクールな方法ではありませんが、うまくいきます。さらに、2 番目のループでは 1 回だけ追加されるように制御する必要があります。

より良いコードはありますか?

var groupproducts = (from res in ctx.Responses    
                     group  res by res.AtProductId into g                                                 
                     orderby (g.Sum(ra =>ra.FinalRate)/g.Count()) 
                     descending select g).Take(2).ToList();

 List<Product> theproducts = new List<Product>();

 foreach (var groupresponse in groupproducts)
 {

    foreach (var response in groupresponse)
    {

          var producttemp= (from prod in ctx.Products
                            where prod.ProductID == response.AtProductId
                            select prod).First();

          theproducts.Add(producttemp);

        }
     }
  }

最終的な解決策(@Danielに感謝します)

 var productsanonymtype = ctx.Products.Select(x => new
                {
                    Product = x,                   
                    AverageRating = x.Responses.Count() == 0 ? 0 : x.Responses.Select(r => (double)r.FinalRate).Sum() / x.Responses.Count()
                }).OrderByDescending(x => x.AverageRating);

                List<Product> products = new List<Product>();

                foreach (var prod in productsanonymtype)
                {
                    products.Add(prod.Product);
                }
4

2 に答える 2

1

これを試して:

products.Select(x => new
                     {
                         Product = x,
                         AverageRating = x.Responses.Sum(x => x.FinalRate) / 
                                         x.Responses.Count()
                     });

Sum私が使用しているオーバーロードは、すべてのプロバイダーで実装されているわけではありません。それが問題になる場合は、次の代替バージョンを使用できます。

products.Select(x => new
                     {
                         Product = x,
                         AverageRating = x.Responses.Select(x => x.FinalRate)
                                                    .Sum() / 
                                         x.Responses.Count()
                     });

製品からその応答へのナビゲーション プロパティがない場合は、まずそれを修正する必要があります。できない場合は、このバージョンを使用できます。

products.Join(responses, x => x.Id, x => x.ProductId,
              (p, r) => new { Product = p, Response = r })
        .GroupBy(x => x.Product)
        .Select(g => new { Product = g.Key,
                           AverageRating = g.Select(x => x.Response.FinalRate)
                                            .Sum() / 
                                           g.Count()
                         });

が であると仮定するFinalRateint、どちらのメソッドも整数で平均評価を計算します。つまり、4.5 評価はありません。また、四捨五入は行われません。つまり、実際の平均評価が 4.9 の場合、結果は 4 になります。これは、除算のオペランドの 1 つを にキャストすることで修正できますdouble

もう 1 つの問題は、これまでのところ評価がない場合です。この場合、上記のコードは例外になります。それが問題になる場合は、計算を次のように変更できます。

AverageRating = g.Count() == 0
                ? 0
                : g.Select(x => (double)x.Response.FinalRate).Sum() / g.Count()
于 2013-02-07T12:20:23.850 に答える
1
ctx.Products.GroupBy(x => new {
                                ProductId = x.ProductId, 
                                FinalRate = x.Responses.Sum(y => y.FinalRate),                  
                                CountProductId = x.Responses.Count
                              })
            .OrderBy(x => x.Key.FinalRate / x.Key.CountProductId);

そして、ここでプロジェクションで.....

ctx.Products.Select(x => new {  
                                ProductID = x.ProductID, 
                                Commercial_Product_Name = x.Commercial_Product_Name, 
                                Manufacturer_Name = x.Manufacturer_Name,
                                ProductImageUrl = x.ProductImageUrl,
                                FinalRate = x.Responses.Sum(y => y.FinalRate), 
                                CountProductId = x.Responses.Count
                             })
            .GroupBy(x => new {
                               ProductId = x.ProductId, 
                               FinalRate = x.FinalRate,                  
                               CountProductId = x.CountProductId
                              })
            .OrderBy(x => x.Key.FinalRate / x.Key.CountProductId);
于 2013-02-07T12:32:21.977 に答える