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

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

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

Re[9]: LINQでJOIN時のエラーについて


(過去ログ 155 を表示中)

[トピック内 13 記事 (1 - 13 表示)]  << 0 >>

■90105 / inTopicNo.1)  LINQでJOIN時のエラーについて
  
□投稿者/ レイ (1回)-(2019/02/08(Fri) 23:33:10)

分類:[C#] 

2019/02/09(Sat) 00:24:35 編集(投稿者)
いつもお世話になっています。

DBから抽出したデータと、加工用にDataTableにセットしたデータを
JOINしたときに、エラーが出たので質問させてください。

まず、DBからマスタを取得し格納するクラスがあります。
このマスタクラスと、データをjoinして、
結果を取得したいのですが、思い通りになりません。

例えば
class Shohin
{
    public Shohin(string code, string name)
    {
        this.code = code;
        this.name = name;
    }

    public string code { get; set; }  // 商品コード
    public string name { get; set; }  // 名前
}

があり、このクラスを経由し、var shohinにデータを取得します。

また、出庫テーブルもDBにあり、これもDBより取得します。
class Delivery
{
    public Delivery(string no, string deliverydt, string itemcode)
    {
        this.no = no;
        this.deliverydt = deliverydt;
        this.itemcode = itemcode
    }

    public string no { get; set; }  // 出庫番号
    public string deliverydt { get; set; }  // 出庫日
    public decimal itemcode { get; set; }  // 商品コード
    public decimal deliveryCount { get; set; }  // 出庫数}
var dtdelivery

この後、出庫データに出庫日当時の価格を価格マスタから取得しながら、
DataTableに追加します。
    DataTable dtdelivery = new DataTable("dtdelivery");
    DataRow dr = dtdelivery.NewRow();
    foreach (var prop in propsdtdelivery)
    {
        if (prop.GetMethod.IsVirtual != true)
        {
            dr[prop.Name] = prop.GetValue(l) ?? DBNull.Value;  // 単価項目も追加します。また、出庫数*単価で金額項目も追加します。
        }
    }
    dtdelivery.Rows.Add(dr);
 
その後、出庫データテーブルの件数をカウントし、金額をサマリーします。
var deliverycontext = dtdelivery.AsEnumerable()
    .GroupBy(x => x.Field<string>("ItemCode")).Select(x => new
    {
        ItemCode = x.Key,
        Count = x.Sum(y => y.Field<decimal>("Count")),    // 出庫データ件数
        SumAmount = x.Sum(y => y.Field<decimal>("SumAmount"))     // 出庫金額合計
    });

deliverycontextの結果ビューには、値がセットされています。

ここで質問ですが、shohinとdeliverycontextをjoinしたいのですが、
エラーになってしまいます。

var test = from a in shohin
                  join b in deliverycontext on a.ItemCode equals b.ItemCode into A
                  from x in A.DefaultIfEmpty()
                  select new
                  {
                      ItemCode = x.ItemCode,
                      Count = (x != null) ? x.Count : 0,
                      SumAmount = (x != null) ? y.SumAmount : 0
                  };
エラー内容:
型 '匿名型' の定数値を作成できません。このコンテキストでサポートされるのはプリミティブ型または列挙型だけです。

何が原因なのか、ご教授の程よろしくお願い致します。

引用返信 編集キー/
■90106 / inTopicNo.2)  Re[1]: LINQでJOIN時のエラーについて
□投稿者/ WebSurfer (1748回)-(2019/02/08(Fri) 23:43:42)
No90105 (レイ さん) に返信

出来れば「図表モード」で書き直していただけませんか。コードがインデントされて見やすく
なりますので。

分類が ASP.NET となっていますがどういう関係があるのでしょうか? 単に C# の Linq の
話のように見えますけど。
引用返信 編集キー/
■90107 / inTopicNo.3)  Re[2]: LINQでJOIN時のエラーについて
□投稿者/ レイ (8回)-(2019/02/09(Sat) 00:27:01)
No90106 (WebSurfer さん) に返信

WebSurferさん

ご指摘ありがとうございます。

修正しました。

他にご指摘等ございましたら、
よろしくお願いします。
引用返信 編集キー/
■90108 / inTopicNo.4)  Re[3]: LINQでJOIN時のエラーについて
□投稿者/ WebSurfer (1749回)-(2019/02/09(Sat) 09:06:15)
No90107 (レイ さん) に返信

「図形モード」での書き換え有難うございました。

ところで、DB は何でしょう? SQL Server? Oracle? MySQL? その他?

開発環境(OS, ..NET, Entity Framework, Visual Studio のバージョンなど)も書いて
ください。

とりあえずエラーメッセージでググってみると、一番最初に以下の記事がヒットします。

LinQ to Entities のWhere句の書き方について
https://social.msdn.microsoft.com/Forums/aspnet/ja-JP/6a2f79c9-76c8-4840-9844-f36d30151314/linq-to-entities?forum=netfxgeneralja

それから想像すると、以下の記事の話が関係ありそうな気がしますが、いかがでしょう?

LINQにも色々 〜SQLに変換されるモノと変換されないモノ
https://codezine.jp/article/detail/8474
引用返信 編集キー/
■90109 / inTopicNo.5)  Re[3]: LINQでJOIN時のエラーについて
□投稿者/ WebSurfer (1750回)-(2019/02/09(Sat) 11:54:52)
No90107 (レイ さん) に返信

質問者さんのコードと似たような(あくまで似たような)サンプルを作って試してみましたが
エラーは再現できませんでした。

質問者さんのコードは理解できませんし、エラーは再現できないということで原因不明です。

ただ、No90108 の私のレスで書いた「LINQにも色々 〜SQLに変換されるモノと変換されないモ
ノ」は関係なさそうな感じです。

ご参考に、自分が作ったサンプルのコードをアップしておきます。

DB は Microsoft が提供する SQL Server サンプルデータベース Northwind で、それから
Visual Studio のウィザードを使って ADO.NET Entity Data Model を生成し、その中の
Order_Details, Products テーブルを使っています。

環境は、Windows 10 Pro 64-bit, .NET 4.6.1, Entity Framework 6.2.0, Visual Studio
Community 2015 Update 3, SQL Server 2008 Express です。

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

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

            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)
                           };

            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
                       };

            foreach (var x in test)
            {
                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

引用返信 編集キー/
■90111 / inTopicNo.6)  Re[4]: LINQでJOIN時のエラーについて
□投稿者/ レイ (9回)-(2019/02/09(Sat) 14:40:01)
No90109 (WebSurfer さん) に返信

WebSurferさん

コメントありがとうございます。

すいません、環境ですが
DB:PostgreSQL
開発環境
OS :Windows7 32bit
.NET Framework :4.5
Visual Studio :Express 2015
になります。

>>質問者さんのコードは理解できませんし
わかりにくくて申し訳ありません。
今回PGの修正を行っているのですが、
Linqを扱うのが初めてで、既存コードから流用したり
ネット検索でヒットしたコードを組み合わせて
いろいろ試したのみたですが、全く改善しないため、
困り果てて質問させていただきました。

やりたいこととしては
@DBから商品マスタを取得
ADBから出庫データを取得
 →出庫データには単価が入っていないため、C#の単価取得関数を呼び出す必要があり、
  出庫データをDataTableに格納し、for eachでループしながら
  DataTableに書き出し
BDataTableを商品コードごとに集計
C@とBをjoinして、最終結果を出力
となります。

WebSurferさんが載せていただいているURLも確認し、
Castさせてもみたんですが、結果は変わらず。

出庫データについては、単価を単価マスタから取得する必要があるため、
一度DataTableにセットし、取得関数を呼び出してセットしています。



引用返信 編集キー/
■90112 / inTopicNo.7)  Re[5]: LINQでJOIN時のエラーについて
□投稿者/ WebSurfer (1751回)-(2019/02/09(Sat) 14:40:40)
No90111 (レイ さん) に返信

エラーを再現できました。原因も分かりました。

私のレス No90109 のコードは、delivery を含めた test のコード全体を Linq to Entities と
して SQL に変換することができ、foreach (var x in test) で DB に SQL を投げることができ
るので問題なかったということのようです。

delivery に ToList() を適用したあと、それを test の Linq To Entities のコードに組み込
むと、その形では SQL に変換することができないので、質問者さんと同じエラーとなります。

質問者さんのコードの deliverycontext は Linq to Object で、そのコードを実行すると即時に
匿名型のコレクションを取得するはずです。即ち、私のコードの delivery に ToList() を適用
したのと同じことです。

質問者さんのコード var test = from a in shohin ... は Linq to Entities ですよね? だか
ら、匿名型のコレクションが混ざっていると SQL に変換することができなくてエラーになったの
だと思います。

No90108 の私のレスで書いた「LINQにも色々 〜SQLに変換されるモノと変換されないモノ」は
やっぱり関係があったようです。

解決策は、質問者さんのコードで言うと deliverycontex と test のところのクエリを、

(1) 両方とも Linq to Entities にする。それができなければ、

(2) 両方とも Linq to Object にする。

・・・ということでよさそうです。
引用返信 編集キー/
■90113 / inTopicNo.8)  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

引用返信 編集キー/
■90114 / inTopicNo.9)  Re[6]: LINQでJOIN時のエラーについて
□投稿者/ レイ (10回)-(2019/02/09(Sat) 20:52:37)
No90113 (WebSurfer さん) に返信

WebSurfer さん

コメントありがとうございます。

説明だけではなく、ご丁寧に検証で作られたソースも
アップしていただき、助かりました。

確かにソースを見ながら説明を読ませていただくと、
なるほど理解ができました。

私は単純に、2つのテーブルをjoinする際に、
equalsで結ぶ項目の型が違うから、と認識していましたが、
そもそものLinq自体の型?が違う、ということですね。
勉強になりました。

Linq to ObjectとLinq to Entitiesという言葉も
今回初めてお聞きした状態で、お恥ずかしい限りです。

次に試せるのが2月13日(水)になりますので、
その時に確認させていただきます。

本当にお世話になりました。

なお、念の為確認後に「解決済み」にさせていただきたいと思います。
引用返信 編集キー/
■90115 / inTopicNo.10)  Re[7]: LINQでJOIN時のエラーについて
□投稿者/ WebSurfer (1753回)-(2019/02/09(Sat) 22:48:45)
No90114 (レイ さん) に返信

> そもそものLinq自体の型?が違う、ということですね。
>
> Linq to ObjectとLinq to Entitiesという言葉も

紹介した記事、

LINQにも色々 〜SQLに変換されるモノと変換されないモノ
https://codezine.jp/article/detail/8474

の図を見ていただくと、一目瞭然と思いますので、是非見ていただければと・・・
引用返信 編集キー/
■90120 / inTopicNo.11)  Re[7]: LINQでJOIN時のエラーについて
□投稿者/ WebSurfer (1756回)-(2019/02/10(Sun) 10:20:25)
No90114 (レイ さん) に返信

【追伸】

上の私が書いたサンプルコードの DB は、上のレスにも書きましたが、全て SQL Server を使って
います。

質問者さんの DB は PostgreSQL だそうですが、当然ドライバやデザイナツールは SQL Server の
ものとは異なるはずですので、同じ結果になるかどうかは分かりません。その点ご注意ください。
引用返信 編集キー/
■90124 / inTopicNo.12)  Re[8]: LINQでJOIN時のエラーについて
□投稿者/ レイ (11回)-(2019/02/10(Sun) 19:30:42)
No90120 (WebSurfer さん) に返信

WebSurferさん

コメントありがとうございます。

ご指摘ありがとうございます。

確かに載ってますね。
勉強させていただきます。

DBの違いについても了解しました。

確認して、また結果はご報告させていただきます。


引用返信 編集キー/
■90194 / inTopicNo.13)  Re[9]: LINQでJOIN時のエラーについて
□投稿者/ レイ (12回)-(2019/02/15(Fri) 22:22:42)
No90120 (WebSurfer さん) に返信

WebSurferさん

遅くなり申し訳ありません。

ご教授いただいた方法で、うまく動作しました。

本当にありがとうございました。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -