クエリーをライブビューとして使用するには、クエリー演算子の制限以外にも、クエリーが満たすべき条件があります。幸い、この条件はほとんどのケースで満たされます。これは、クエリーに特殊な式が含まれる場合にのみ考慮する必要があり、このような状況は比較的まれです(ただし、LiveLinq to Objects で使用されるクラスは、必ずプロパティ通知条件を満たす必要があります。これについては、「組み込みのコレクションクラス IndexedCollection の使用(LiveLinq to Objects)」で既に説明しました)。ただし、この条件は LiveLinq によって自動的には検証されません。したがって、この条件を満たすかどうかは、ユーザー自身の責任において確認する必要があります。この条件が満たされない場合、ビューはベースデータが変更されても反応しません。この条件については2つの説明を用意しています。1つめは基本的な理解のための簡単な説明、2つめは高度な用途に関する詳細な説明です。
簡単に言うと、基本的に、クラスがプロパティ通知を提供しなければならない場合があるということです。これを考慮すべきケースは2つあります。
(1) LiveLinq to Objects。
この場合、ビューの引数は独自のクラスのコレクションなので、そのクラスからプロパティ通知を提供する必要があります。そうしないと、ビューはプロパティの変更に反応しません(「組み込みのコレクションクラス IndexedCollection の使用(LiveLinq to Objects)」を参照)。
(2) ビューに対するビュー、ビューに対するインデックス(LiveLinq to DataSet、LiveLinq to XML、および LiveLinq to Objects に適用)。
次のビューがあるとします。
View<T> v;
このビュー v から別のビューを作成する(引数として v を使用する)場合、または v にインデックスを作成する場合は、T クラスからプロパティ通知が提供されることが必要です。そうでないと、v から定義されたビュー(またはインデックス)は、プロパティの変更に反応しません。
LiveLinq to DataSet および LiveLinq to XML のための注記:ビュー結果クラス T が DataRow(LiveLinq to DataSet の場合)または XNode(LiveLinq to XML の場合)であるか、その派生クラスである場合、プロパティ通知は必要なく(したがって、この場合は条件/制限なく機能します)、LiveLinq は標準のイベントから必要な通知を受け取ります。
また、独自のオブジェクトのプロパティが LiveLinq 自体によって自動的に変更される以外にコードによっては変更されない場合、つまりビューのソースコレクションの要素になるオブジェクトにプロパティを設定するコードがない場合も、これらの条件について考慮する必要がないと言えます。特に、独自のクラスに読み取り専用のプロパティが含まれ、それがすべて匿名クラスを含む場合が、LINQ ではよく使用されます。クラスや参照型ではなく値型である構造体を使用する場合も、値型は参照ではなく、コレクションの要素を変更することができないため、このケースに当たります。以上のような場合、変更通知の必要はありません。
最後に、回避する必要があるケースとして、たとえば、Customer プロパティを含む Order クラスでプロパティチェーンを使用すると、エラーの原因になります。
o => o.Customer.City
これは、Customer オブジェクトで City プロパティを変更しない場合は可能です。ただし、City プロパティを変更するコードがある場合、LiveLinq はその変更を反映しません。通常、Order は Customer オブジェクトの変更を LiveLinq に通知しないためです。通常は、プロパティチェーンを使用せずに、クエリーを変更することで同じ効果を得ることができます。たとえば、次のステートメントの代わりに
…Select(o => o.Customer.City)
次のステートメントを使用します。
…Select(o => o.Customer).Select(c => c.City)
監視可能な関数と監視不可能な関数
クラスがプロパティ通知をサポートするかどうかに基づいて、クエリー式で使用できる関数(結果セレクタ、キーセレクタなど)の選択肢が制限されます(「組み込みのコレクションクラス IndexedCollection の使用(LiveLinq to Objects)」を参照)。
ここで言及しているプロパティ通知は、ビューの結果のクラスではなく、ビューの引数の要素クラスに含まれるプロパティが対象です(ただし、ビューに基づいてビューを作成できるため、あるビューの結果が別のビューの引数になることも当然あります)。
ここでは、監視可能な関数と監視不可能な関数の2種類を区別します。監視可能な関数は使用でき、監視不可能な関数は使用を避ける必要があります。
監視可能な関数の例:
x => x.P1
x => x.P1 + x.P2 + x.M(c1)
(x, y) => new {x.P1, x.P2, M = y.M(), y.P3}
ここで、P1、P2、P3、M は変更通知を持つプロパティ/メソッドで、c1 は変更されることのない値です。
高度な用途のための注記:ここで言うプロパティ/メソッドの呼び出しは、この関数のパラメータの1つに直接適用されるものだけでなく、クエリー内のこの関数に先行する関数のパラメータに(オブジェクト初期化子内の単純な参照(のチェーン)を介して到達できる場合に)再帰的に適用されるものも含むと見なせます。次に例を示します。
....Select((x, y) => new { P1 = x, P2 = y }).Select(z => new { z.P2.A, z.P1.B })
これは許可されます(当然、プロパティ A および B が監視可能だとして)。
1a. 定数も監視可能です。
上の条件から、特に定数値が除外されていることに注意してください。すべての定数値/オブジェクトは関数で許可され、関数の監視可能性を損ないません。"定数値" とは、特定のどのパラメータ値に対しても同じオブジェクトのままであることを意味します。つまり、時間によって変わることはありません。最も一般的な定数の例は、恒等関数です。
x => x
このオブジェクトの状態は時間と共に変わることがありますが、オブジェクト自体とオブジェクトへの参照は同じままです(パラメータオブジェクトとして。もちろん、この場合は結果オブジェクトと同じ)。したがって、これは定数です。その他の定数の例を次に示します。
x => c1
(x, y) => new {x, y}
(x, y) => x + y
x => x == c1 ? c2 : c3
ここで、c1、c2、c3 は、何らかの定数値です。
同じ関数で定数値を監視可能な値と組み合わせることができ、結果の関数は監視可能なままです。たとえば、
x => new {x, x.P1, P = y.P2 + y.P3 }
ここで、P1 と P2 は変更通知を持つプロパティです。
1b. 引数に依存しないオブジェクトの初期化子およびコンストラクタは許可されます。
これまでの例には、匿名クラスの new しかありませんでしたが、実際には、ユーザー定義のクラスおよびコンストラクタの呼び出しも監視可能性を損なうことなく許可される場合があります。それは、コンストラクタで関数引数を使用せず、定数式だけを使用するか、何も使用しない(パラメータなしのコンストラクタまたはオブジェクト初期化子)場合です。したがって、以下の関数は監視可能です(ここで、c1 は定数値、P および Q は監視可能なプロパティです)。
(x, y) => new C { x.P, y.Q}
(x, y) => new C(c1) { X = x, P = y.P }
以下は監視不可能です。
(x, y) => new C(y) { x.P, y.Q}
(x, y) => new C(c1, x) { x, y.P }
名前が示すように、上記の条件を満たさない関数はどれも、監視不可能と見なされます。この監視可能性の条件は、関数自体だけでなく、その関数の引数のクラスにも依存します。そのクラスは、関数で使用される定数でないすべての項目に対して、プロパティ変更通知を持つ必要があります。そのため、x => x.P のような極めて単純な関数でさえ、P が監視不可能(クラスが P の変更通知を発行しない)であれば、監視不可能になる可能性があります。したがって、監視不可能な関数として最もよくある最初の例は、上と同じです。ただし、プロパティ P1、P2 の少なくとも一方が監視可能でない状況、つまりクラスがそのプロパティの変更通知を提供しない場合です。
x => x.P1
(x, y) => new {x.P1, y.P2 }
これは、通知を提供するコードを追加し忘れた場合などに起こり得ます(「組み込みのコレクションクラス IndexedCollection<T> の使用(LiveLinq to Objects)」を参照)。
別の原因として、次のように、計算値を返すプロパティがあります。
public class Customer
{
public List<Order> orders;
public int OrderCount { get {return orders.Count;} }
}
この場合は、orders.Count が変更されるたびに Customer クラスからプロパティ変更通知を発行するように、特別に対応する必要があります。
さらに、もう1つの原因として、たとえば、Customer プロパティを含む Order クラスでプロパティチェーンを使用する場合があります。
o => o.Customer.City
値が変更されるたびにプロパティ変更通知をトリガするように特別に対応を行えば、上の2つの例を含め、どのような式も監視可能にすることができます。ただし、その目的に特化して記述されたコードが必要です。たとえば、最後の関数の場合は、Customer クラスの City プロパティが変更されるたびに、Orders コレクションに対してプロパティ変更通知イベントをトリガします。