web-dev-qa-db-ja.com

Linqクエリ内でメソッドを呼び出す

テーブルの列から取得した値に基づいて文字列値を取得する「S」という名前の列をテーブルに挿入します。

例:for each ID (a.z)別のテーブルに保存されている文字列値を取得したい。文字列値は、Linqクエリを介して取得する別のメソッドから返されます。

  • Linqからメソッドを呼び出すことはできますか?
  • 同じクエリですべてを行う必要がありますか?

これは、取得する必要がある情報の構造です。

a.zはテーブル#1の最初の正方形のIDであり、このIDからテーブル#2の別のIDを取得し、それから列 'S'の下に表示する必要がある文字列値を取得できます。
enter image description here

_var q = (from a in v.A join b in v.B
    on a.i equals b.j
    where a.k == "aaa" && a.h == 0
    select new {T = a.i, S = someMethod(a.z).ToString()})
    return q;
_

S = someMethod(a.z).ToString()行により、次のエラーが発生します。

タイプ 'System.Data.Linq.SqlClient.SqlColumn'のオブジェクトをタイプ 'System.Data.Linq.SqlClient.SqlMethodCall'にキャストできません。

32
user990635

データベース側ではメソッド呼び出しが意味をなさないため、Linq-to-Objects_コンテキストでメソッド呼び出しを実行する必要があります-AsEnumerable()を使用してこれを行うことができます-基本的にクエリの残りは_Linq-to-Objects_を使用してメモリ内コレクションとして評価され、期待どおりにメソッド呼び出しを使用できます。

_var q = (from a in v.A join b in v.B
        on a.i equals b.j
        where a.k == "aaa" && a.h == 0
        select new {T = a.i, Z = a.z })
        .AsEnumerable()
        .Select(x => new { T = x.T, S = someMethod(x.Z).ToString() })
_
55
BrokenGlass

これを2つのステートメントに分割します。クエリの結果(データベースにヒットするもの)を返し、別のステップで2回目に結果を列挙して、翻訳を新しいオブジェクトリストに変換します。この2番目の「クエリ」はデータベースにヒットしないため、その内部でsomeMethod()を使用できます。

Linq-to-Entitiesは、C#からデータベースへのクエリへの移行を非常にシームレスにするため、少し奇妙なことです。その結果、「このC#をすべて実際にSQLとして実行できますか?」と自問する必要があります。できない場合-内部でsomeMethod()を呼び出している場合、クエリに問題が発生します。そして、通常の解決策はそれを分割することです。

.AsEnumerable()を使用した@BrokenGlassからの他の答えは、基本的にそれを行う別の方法です。)

11
Ken Smith

これは古い質問ですが、「ハック」に言及している人は誰もいません。これにより、選択中に反復せずにメソッドを呼び出すことができます。アイデアはコンストラクターを使用することで、コンストラクターで好きなものを呼び出すことができます(少なくとも、NHibernateを使用したLINQでは正常に動作しますが、LINQ2SQLまたはEFについてはわかりませんが、同じであると思います)。以下にベンチマークプログラムのソースコードがありますが、私の場合の反復アプローチはコンストラクターアプローチの約2倍遅いようです。不思議はないと思います-私のビジネスロジックは最小限でしたので、反復やメモリ割り当てなどが重要です。

また、私はこれがデータベース上で実行されることを試みるべきではない、と言うより良い方法があることを望みました、

// Here are the results of selecting sum of 1 million ints on my machine:
// Name    Iterations      Percent    
// reiterate       294     53.3575317604356%      
// constructor     551     100%

public class A
{
    public A()
    {            
    }

    public A(int b, int c)
    {
        Result = Sum(b, c);
    }

    public int Result { get; set; }

    public static int Sum(int source1, int source2)
    {
        return source1 + source2;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var range = Enumerable.Range(1, 1000000).ToList();

        BenchmarkIt.Benchmark.This("reiterate", () =>
            {
                var tst = range
                    .Select(x => new { b = x, c = x })
                    .AsEnumerable()
                    .Select(x => new A
                    {
                        Result = A.Sum(x.b, x.c)
                    })
                    .ToList();
            })
            .Against.This("constructor", () =>
            {
                var tst = range
                    .Select(x => new A(x, x))
                    .ToList();
            })
            .For(60)
            .Seconds()
            .PrintComparison();

        Console.ReadKey();
    }
}
0
Giedrius