C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト


■90113 / )  Re[5]: LINQでJOIN時のエラーについて
□投稿者/ WebSurfer (1752回)-(2019/02/09(Sat) 15:08:22)
No90111 (レイ さん) に返信

ご参考に検証に使ったコードをアップしておきます。

コメントの「// 質問者さんのコードのように DataTable 経由で取得してみる」以降を追加して
います。読んでもらえば上で説明したことが分かりやすいと思います。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
using System.Data;
using System.Data.SqlClient;

namespace ConsoleAppJoinByLinq
{
    class Program
    {
        static void Main(string[] args)
        {
            NORTHWINDEntities context = new NORTHWINDEntities();

            // これは Linq to Entities
            var delivery = from d in context.Order_Details
                           group d by d.ProductID into g
                           orderby g.Key
                           select new
                           {
                               ItemCode = g.Key,
                               Count = g.Sum(x => x.Quantity),
                               SumAmount = g.Sum(x => x.UnitPrice * x.Quantity)
                           };

            // これも Linq to Entities
            var test = from p in context.Products
                       join d in delivery
                       on p.ProductID equals d.ItemCode into dGroup
                       from item in dGroup.DefaultIfEmpty()
                       select new
                       {
                           ItemCode = p.ProductID,
                           Name = p.ProductName,
                           Count = item.Count,
                           SumAmount = item.SumAmount
                       };

            // delivery を含めた test のコード全体を Linq to Entities として SQL に変換することができ、
            // foreach で DB に SQL を投げることができ るので問題ない。
            foreach (var x in test)
            {
                Console.WriteLine($"ID: {x.ItemCode}, Name: {x.Name}, Count: {x.Count}, Sum: {x.SumAmount}");
            }

            // 質問者さんのコードのように DataTable 経由で取得してみる
            string connString = @"data source=lpc:(local)\sqlexpress;initial catalog=NORTHWIND;integrated security=True;";
            string selectquery = "SELECT [OrderID],[ProductID],[UnitPrice],[Quantity],[Discount] FROM [Order Details]";
            DataTable table = new DataTable();
            using (SqlConnection connection = new SqlConnection(connString))
            {
                using (SqlCommand command = new SqlCommand(selectquery, connection))
                {
                    SqlDataAdapter adapter = new SqlDataAdapter(command);
                    adapter.Fill(table);
                }
            }

            // これは Linq to Object
            var delivery2 = from d in table.AsEnumerable()
                            group d by d.Field<int>("ProductID") into g
                            orderby g.Key
                            select new
                            {
                                ItemCode = g.Key,
                                Count = g.Sum(x => x.Field<Int16>("Quantity")),
                                SumAmount = g.Sum(x => x.Field<decimal>("UnitPrice") * x.Field<Int16>("Quantity"))
                            };

            // delivery2 は Linq to Enitities ではない匿名型のオブジェクトのコレクション。
            // それを上のような Linq to Entities のクエリに組み込むと SQL に変換できないということで以下のエラーになる。
            // System.NotSupportedException: Unable to create a constant value of type 'Anonymous type'. 
            // Only primitive types or enumeration types are supported in this context.

            // 可決策は以下のように両方 Linq to Object とすること。
            var products = context.Products.ToList();

            var test2 = from p in products
                        join d in delivery2
                        on p.ProductID equals d.ItemCode into dGroup
                        from item in dGroup.DefaultIfEmpty()
                        select new
                        {
                            ItemCode = p.ProductID,
                            Name = p.ProductName,
                            Count = item.Count,
                            SumAmount = item.SumAmount
                        };

            foreach (var x in test2)
            {
                Console.WriteLine($"ID: {x.ItemCode}, Name: {x.Name}, Count: {x.Count}, Sum: {x.SumAmount}");
            }
        }
    }
}

結果はいずれも以下の通りとなります。

ID: 1, Name: Chai, Count: 828, Sum: 14277.6000
ID: 2, Name: Chang, Count: 1057, Sum: 18559.2000
ID: 3, Name: Aniseed Syrup, Count: 328, Sum: 3080.0000
・・・中略・・・
ID: 77, Name: Original Frankfurter grune Sose, Count: 791, Sum: 9685.0000

返信 編集キー/


管理者用

- Child Tree -