web-dev-qa-db-ja.com

匿名型とユーザー定義型のLINQ選択クエリ

匿名クラスはc#で読み取り専用のプロパティを持っています。これは、linq選択クエリで宣言してデータベースから特定の値を取得するためによく使用されます。私のコードには次のクエリがあります。newステートメントを使用して匿名クラスの新しいオブジェクトを選択するのを混乱させたもの。 StudentClerkshipsLogModelのモデルクラスがありました。モデル名を使用すると、クエリ結果で編集が可能になります。

var query = (from entity in _tblStudentClerkshipsLog.GetQueryable()
             where entity.StudentID == intStudentID                             
             select new StudentClerkshipsLogModel
             {
                 StudentClerkshipID = entity.StudentClerkshipID,
                 StudentID = entity.StudentID,
                 ClerkshipID = entity.ClerkshipID,
             }).ToList();

newステートメントでselectの後にタイプについて言及しなかった場合、終了できません。コンパイラはエラーを発生させます。匿名オブジェクトは読み取り専用です。

var query = (from entity in _tblStudentClerkshipsLog.GetQueryable()
             where entity.StudentID == intStudentID                             
             select new 
             {
                 StudentClerkshipID = entity.StudentClerkshipID,
                 StudentID = entity.StudentID,
                 ClerkshipID = entity.ClerkshipID,
             }).ToList()

私の質問は、linqが約2つのクエリを異なる方法でバインドする方法です。両方のクエリに動的バインディングがあるか、最初のクエリは静的です。

ありがとう

16
Muhammad Nasir

私が正しく理解しているとしたら、LINQプロバイダーは匿名オブジェクトのプロパティをどのように設定できるのでしょうか。それらは "true"の読み取り専用プロパティです(private setはなく、getのみ)。

IQueryable<T>Select拡張メソッドを呼び出すと、Expression<Func<T, TResult>タイプのを受け入れます。 Selectのスタブを作成する場合は、デバッガーを使用して、生成された式を調べることができます。

public static class MyExtensions
{
    public static void MySelect<T, TResult>(this IQueryable<T> query, Expression<Func<T, TResult>> projection)
    {
        System.Diagnostics.Debug.WriteLine(projection);
    }
}

違いは、コンパイラが名前付き型と匿名型のラムダ式を生成する方法にあります。名前付きタイプに対してSelectを呼び出すと、式は次のようになります。

{_ => new Person() {Id = _.Id, Name = _.Name}}

つまり、最初に新しいPersonオブジェクトが作成され、次にメンバーが初期化されます(MemberInit式)。

ただし、匿名型に対してSelectを呼び出すと、コンストラクター呼び出し(New expression)として式が作成されます。

{_ => new <>f__AnonymousType0`2(a = _.Id, b = _.Name)}

LINQプロバイダーは、クエリ結果を具体化するときに、これらのラムダをデリゲートにコンパイルし、最終的には匿名型のコンストラクターを呼び出すだけです。

11
Dennis

あなたが得ているエラーは本当にLINQとは何の関係もありません。 LINQをまったく使用しなくても同じことがわかります。

var anonymous = new { Name = "Fred" };
anonymous.Name = "Joe"; // Error, as properties of anonymous types are read-only

そのため、LINQクエリによってフェッチされたオブジェクトを変更する場合は、匿名型を使用しないでください。ただし、両方のLINQクエリは静的にバインドされます。匿名型はコンパイル時に完全に既知であり、コンパイラーはそれらに通常の型制限を適用します。例えば:

var anonymous = new { Name = "Fred" };
Console.WriteLine(anonymous.Foo); // Error: no property Foo
int bar = anonymous.Name; // Error: no conversion from string to int
17
Jon Skeet

LINQ結果の匿名型の結果との次の違いが見つかりました。

  1. 結果は編集可能ではありません。グリッドビューに値を割り当てると、読み取り専用になります。

  2. 匿名オブジェクトのスコープに問題があります。タイプを別のメソッドに渡すことができません。タイプvarのパラメーターを定義します。 varの後には必ず初期化式を続ける必要があります。

現在のコンテキストでのみ読み取り専用の結果が必要な場合は、匿名クエリを使用してください。他の関数で結果が必要な場合は、オブジェクトのタイプを定義する必要があります。 newの後のオブジェクトタイプは、結果定義から取得するプロパティを使用して作成され、中括弧{}。モデルクラスのすべての値を初期化する必要はありません。

2
Muhammad Nasir