答えは、エラーメッセージの解釈方法がわかれば、エラーメッセージにあります。
IEnumerable
次のように、LINQ to Entities (Entity Framework LINQ プロバイダー) 経由で作成しています。
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity
.Where(a => a.activityID == id)
.Select(s => new TeamAction(s.teamID, s.name, id, s.type));
呼び出しには、 4 つのパラメーターを受け取るコンストラクターをSelect
呼び出すラムダ式が含まれていることに注意してください。TeamAction
の最初の要素を要求するとすぐにIEnumerable
、LINQ はクエリを実行しようとします。その時点で、ラムダ式を解析し、実行可能な Entity Framework クエリに変換しようとします。しかし、例外メッセージが示すように:
Only parameterless constructors and initializers are supported
LINQ to Entities は実行方法を認識していないため、パラメーター化されたコンストラクターを LINQ クエリに含めることはできません。問題を解決するには、いくつかのオプションがあります。
オプション 1: IQueryable -> IEnumerable
これを回避する最も簡単な方法は、EF LINQ プロバイダーが問題のあるラムダを認識しないようにするIQueryable
ことIEnumerable
です。dbIncAct.ImcompleteActivity
はおそらく でありDbSet<>
、まだ LINQ 2 エンティティに依存しているをDbSet<>.Where
返します。IQueryable
その依存関係を断ち切るには、次のようにします。
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity
.Where(a => a.activityID == id)
.AsEnumerable()
.Select(s => new TeamAction(s.teamID, s.name, id, s.type));
これにより、EF クエリが強制的にパーツを実行し、エンティティWhere
の列挙可能なコレクションが返されます。IncompleteActivity
そのこと (内部で定義された のようなオブジェクト) は、EF とは完全に別のList
呼び出しに使用されます。Select
ここでの欠点は、おそらくデータベースにヒットする EF クエリをすぐに実行するように強制していることです。それが望ましくない場合は、他の 2 つのオプションのいずれかを使用して、パラメーター化されたコンストラクターを削除するしかありません。
オプション 2: オブジェクト初期化子
そのコンストラクターが何をしたかによって、簡単に修正できる場合とできない場合があります。コンストラクターが、新しく作成されたオブジェクトのプロパティを設定するためだけにある場合は、幸運です。C# は、まさにこの理由から、LINQ およびラムダと連携する新しいオブジェクト初期化子構文を導入しました。
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity
.Where(a => a.activityID == id)
.Select(s => new TeamAction
{
TeamId = s.teamID,
Name = s.name,
Id = id,
Type = s.type
});
オプション 3: リファクタリング
コンストラクターが実際の作業を行う場合は、リファクタリングを行う必要があります。できるだけ多くのロジックをデフォルトTeamAction()
コンストラクターに移動してみてください。ロジックの一部をプロパティ セッターに入れることもできますが、できる限り最小限に抑える必要があります。
オブジェクトが実際に複雑な初期化を必要とする場合、ライフサイクルの早い段階で呼び出される初期化メソッドを持つ典型的なパターンは次のとおりです。
var x = new X { ... };
x.InitializeMe();
これは、たとえば@for
ループ内で行うことも、クエリを作成した直後の別のステップとして行うこともできます。