.NET での XEP Event Persistence の使用法
XEP for .NET は、構造化データの非常に高速な格納および取得を可能にします。複雑なデータ構造を最適にマッピングするよう、スキーマ生成を制御できます。単純なデータ構造のスキーマは、多くの場合自動生成され、変更することなく使用できます。
この章で説明する項目は以下のとおりです。
-
Event Persistence の概要 — 永続イベントの概念および用語について紹介し、XEP を使用するコードの簡単な例を示します。
-
EventPersister の作成と接続 — EventPersister クラスのインスタンスを作成する方法、およびそれを使用してデータベース接続を開き、テストし、閉じる方法について説明します。
-
スキーマのインポート — .NET クラスを分析するため、および対応する永続イベントのスキーマを生成するために使用するメソッドおよび属性について説明します。
-
イベントの格納と変更 — 永続イベントの格納、変更、および削除に使用する Event クラスのメソッドについて説明します。
-
クエリの使用法 — クエリ結果セットを作成および処理する XEP クラスのメソッドについて説明します。
-
スキーマのカスタマイズとマッピング — .NET クラスがデータベース・スキーマにどのようにマップされるかについて詳しく説明するほか、最適なパフォーマンスを得るためのカスタマイズ・スキーマの生成方法についても詳細に説明します。
永続イベントという用語は元来、リアル・タイム・イベントから取得されたデータを指していました。XEP の現在の実装は、高速のイベント処理以上のことを実行できる高度なオブジェクト/リレーショナル・マッピング (ORM) フレームワークです。XEP の “永続イベント” は、きわめて複雑なデータ・オブジェクトにすることができ、使用可能なあらゆるデータ・ソースから永続化できます。
Event Persistence の概要
永続イベントは、.NET オブジェクトにデータ・フィールドの永続コピーを格納する InterSystems IRIS データベース・オブジェクトです。既定では、XEP は、各イベントを標準の %PersistentOpens in a new tab オブジェクトとして格納します。ストレージは、その他の手段 (オブジェクト、SQL、直接グローバル・アクセスなど) でデータが InterSystems IRIS にアクセスできるように自動的に構成されます。
永続イベントを作成して格納するには、事前に XEP が対応する .NET クラスを分析して、スキーマをインポートする必要があります。このスキーマは、.NET オブジェクトのデータ構造をデータベースの永続オブジェクトにどのように投影するのかを定義します。スキーマは、以下の 2 つのオブジェクト・プロジェクション・モデルのうちのいずれかを使用できます。
-
既定のモデルは、フラット・スキーマです。そのスキーマでは、参照オブジェクトがすべてシリアル化されて、インポートされたクラスの一部として格納され、スーパークラスから継承したフィールドはすべて、それらが、インポートされたクラスのネイティブ・フィールドであるかのように格納されます。これは、最も高速かつ効率的なモデルですが、元の .NET クラス構造に関する情報は何も保持されません。
-
構造情報を保持する必要がある場合は、フル・スキーマ・モデルを使用できます。このモデルでは、.NET ソース・クラスと InterSystems IRIS の投影されたクラスとの間に 1 対 1 のリレーションシップを作成することで、完全な .NET 継承構造が保持されますが、パフォーマンスがわずかに劣化することがあります。
両方のモデルの詳細は “スキーマ・インポート・モデル” を参照してください。各種のデータ型の投影方法の詳細は “スキーマ・マッピング・ルール” を参照してください。
XEP は、スキーマのインポート時に .NET クラスを分析して基本情報を取得します。XEP がインデックスを生成 (“IdKey の使用法” を参照) して、フィールドのインポートに関する既定のルールをオーバーライド (“属性の使用法” と “InterfaceResolver の実装” を参照) できるようにする追加情報を提供することができます。
永続イベントのフィールドは、単純な数値型およびそれらに関連する System 型、文字列、オブジェクト (組み込み/連続オブジェクトとして投影される)、列挙、およびコレクション・クラスから派生した型にすることができます。これらのデータ型を配列、入れ子になったコレクション、および配列のコレクションに含めることもできます。
スキーマがインポートされると、XEP を使用して非常に高いレートでデータの格納、照会、更新、および削除を行えます。格納されたイベントは、照会に対して、あるいは完全オブジェクトまたはグローバル・アクセスに対してすぐに使用できます。EventPersister、Event、および EventQuery<T> クラスは XEP のメイン機能を提供します。これらのクラスは、以下の順序で使用されます。
-
EventPersister クラスは、データベース接続を確立して制御するメソッドを提供します (“EventPersister の作成と接続” を参照してください)。
-
接続が確立されたら、他の EventPersister メソッドを使用して、スキーマをインポートできます (“スキーマのインポート” を参照してください)。
-
Event クラスは、イベントの格納、更新、または削除、クエリの作成、インデックス更新の制御を行うメソッドを提供します (“イベントの格納と変更” を参照してください)。
-
EventQuery<T> クラスは、データベースから一連のイベントを取得する単純な SQL クエリを実行するために使用されます。このクラスは、結果セットを繰り返し処理し、個別のイベントの更新および削除を行うためのメソッドを提供します (“クエリの使用法” を参照してください)。
次のセクションでは、これらの機能をすべてデモンストレーションする 2 つの非常に簡潔なアプリケーションについて説明します。
永続イベントを格納および照会する単純なアプリケーション
このセクションでは、XEP を使用して永続イベントを作成してアクセスする 2 つの非常に単純なアプリケーションについて説明します。
-
StoreEvents プログラム — サーバ上のネームスペースへの接続を開き、イベントを格納するためのスキーマを作成し、Event のインスタンスを使用してオブジェクトの配列を永続イベントとして格納し、接続を閉じて終了します。
-
QueryEvents プログラム — StoreEvents と同じネームスペースにアクセスする新しい接続を開き、EventQuery<T> のインスタンスを作成して、以前に格納されたイベントを読み取りおよび削除し、接続を閉じて終了します。
これらのアプリケーションは、システムを排他的に使用し、2 つの連続したプロセスで実行されることが前提となっています。
どちらのプログラムも、XEP サンプル・アプリケーションで定義されているクラスの 1 つである xep.samples.SingleStringSample のインスタンスを使用します (詳細は、“XEP のサンプル・アプリケーション” を参照してください)。
StoreEvents プログラム
StoreEvents では、EventPersister の新しいインスタンスが作成され、特定のサーバ・ネームスペースに接続されます。SingleStringSample クラス用にスキーマがインポートされ、テスト・データベースが、そのクラスのエクステントから既存のイベントをすべて削除することで初期化されます。Event のインスタンスが作成され、SingleStringSample オブジェクトの配列を永続イベントとして格納するために使用されます。その後、接続が終了されます。その新しいイベントはデータベース内で永続化され、QueryEvents プログラムによってアクセスされるようになります (次のセクションで説明します)。
using System;
using InterSystems.XEP;
using xep.samples; // compiled XEPTest.csproj
public class StoreEvents {
private static String className = "xep.samples.SingleStringSample";
private static SingleStringSample[] eventItems = SingleStringSample.generateSampleData(12);
public static void Main(String[] args) {
for (int i=0; i < eventItems.Length; i++) {
eventItems[i].name = "String event " + i;
}
try {
Console.WriteLine("Connecting and importing schema for " + className);
EventPersister myPersister = PersisterFactory.CreatePersister();
myPersister.Connect("127.0.0.1",1972,"User", "_SYSTEM", "SYS");
try { // delete any existing SingleStringSample events, then import new ones
myPersister.DeleteExtent(className);
myPersister.ImportSchema(className);
}
catch (XEPException e) { Console.WriteLine("import failed:\n" + e); }
Event newEvent = myPersister.GetEvent(className);
newEvent.Store(eventItems); // store array of events
//Print each item in the resultset
SingleStringSample mySample = xepQuery.GetNext();
while (mySample != null){
Console.WriteLine(mySample.name);
mySample = xepQuery.GetNext();
}
newEvent.Close();
myPersister.Close();
}
catch (XEPException e) { Console.WriteLine("Event storage failed:\n" + e); }
} // end Main()
} // end class StoreEvents
StoreEvents.Main() が呼び出される前に、xep.samples.SingleStringSample.generateSampleData() メソッドが呼び出されて、サンプル・データ配列 eventItems が生成されます (サンプル・クラスの詳細は “XEP のサンプル・アプリケーション” を参照してください)。
この例では、XEP メソッドは以下のアクションを実行します。
-
PersisterFactory.CreatePersister() は、EventPersister の新しいインスタンスである myPersister を作成します。
-
EventPersister.Connect() は、User ネームスペースへの接続を確立します。
-
EventPersister.ImportSchema() は、SingleStringSample クラスを分析し、そのスキーマをインポートします。
-
EventPersister.DeleteExtent() を呼び出すと、SingleStringSample エクステントから既存のテスト・データを削除することで、データベースがクリーンアップされます。
-
EventPersister.GetEvent() は、SingleStringSample イベントの処理に使用される Event の新しいインスタンスである newEvent を作成します。
-
Event.Store() は、入力として eventItems 配列を受け入れ、その配列内のオブジェクトごとに新しい永続イベントを作成します(代わりに、コードで eventItems 配列をループして個々のオブジェクトごとに Store() を呼び出すこともできますが、この例ではそのようにする必要はありません)。
-
Event.Close() および EventPersister.Close() は、イベントが格納された後に newEvent および myPersister に対して呼び出されます。
これらのメソッドについては、すべてこの章で後で詳しく説明します。接続を開き、テストし、閉じることに関する詳細は、“EventPersister の作成と接続” を参照してください。スキーマの作成の詳細は、“スキーマのインポート” を参照してください。Event クラスの使用とエクステントの削除の詳細は、“イベントの格納と変更” を参照してください。
QueryEvents プログラム
この例では、QueryEvents は StoreEvents プロセスの終了直後に実行されるものと想定しています (“StoreEvents プログラム” を参照してください)。QueryEvents は、StoreEvents と同じネームスペースにアクセスする新しいデータベース接続を確立します。EventQuery<T> のインスタンスが作成され、前に格納されたイベントに繰り返し処理を行い、それらのデータを出力し、それらを削除します。
using System;
using InterSystems.XEP;
using SingleStringSample = xep.samples.SingleStringSample; // compiled XEPTest.csproj
public class QueryEvents {
public static void Main(String[] args) {
EventPersister myPersister = null;
EventQuery<SingleStringSample> myQuery = null;
try {
// Open a connection, then set up and execute an SQL query
Console.WriteLine("Connecting to query SingleStringSample events");
myPersister = PersisterFactory.CreatePersister();
myPersister.Connect("127.0.0.1",1972,"User","_SYSTEM","SYS");
try {
Event newEvent = myPersister.GetEvent("xep.samples.SingleStringSample");
String sql = "SELECT * FROM xep_samples.SingleStringSample WHERE %ID BETWEEN 3 AND ?";
myQuery = newEvent.CreateQuery<SingleStringSample>(sql);
newEvent.Close();
myQuery.AddParameter(12); // assign value 12 to SQL parameter
myQuery.Execute();
}
catch (XEPException e) {Console.WriteLine("createQuery failed:\n" + e);}
// Iterate through the returned data set, printing and deleting each event
SingleStringSample currentEvent;
currentEvent = myQuery.GetNext(); // get first item
while (currentEvent != null) {
Console.WriteLine("Retrieved " + currentEvent.name);
myQuery.DeleteCurrent();
currentEvent = myQuery.GetNext(); // get next item
}
myQuery.Close();
myPersister.Close();
}
catch (XEPException e) {Console.WriteLine("QueryEvents failed:\n" + e);}
} // end Main()
} // end class QueryEvents
この例では、XEP メソッドは以下のアクションを実行します。
-
EventPersister.CreatePersister() および EventPersister.Connect() が再び呼び出され (StoreEvents の場合と同様)、User ネームスペースへの新しい接続が確立されます。
-
EventPersister.GetEvent() は、SingleStringSample エクステントに対するクエリを作成するために使用される Event のインスタンスである newEvent を作成します。クエリが作成された後、newEvent は、その Close() メソッドを呼び出すことで破棄されます。
-
Event.CreateQuery() は、EventQuery<T> のインスタンスである myQuery を作成します。T は、ターゲット・クラス SingleStringSample です。SQL 文は、3 と変数パラメータ値の間のオブジェクト ID を持つ永続 SingleStringSample イベントすべてを取得するクエリを定義します。
-
EventQuery<T>.AddParameter() は、値 12 を SQL パラメータに割り当てます。
-
EventQuery<T>.Execute() は、クエリを実行します。クエリが正常に実行された場合、myQuery に、そのクエリに一致する SingleStringSample イベントすべてのオブジェクト ID をリストする結果セットが含まれるようになります。
-
EventQuery<T>.GetNext() が呼び出され、結果セットの最初の項目がフェッチされ、それが変数 currentEvent に割り当てられます。
-
while ループ内で以下のように実行されます。
-
currentEvent の name フィールドが出力されます。
-
EventQuery<T>.DeleteCurrent() がデータベースから最後にフェッチされたイベントを削除します。
-
EventQuery<T>.GetNext() が再び呼び出され、次のイベントがフェッチされ、それが変数 currentEvent に割り当てられます。
これ以上項目がない場合、GetNext() が null を返し、ループが終了します。
-
-
EventQuery<T>.Close() および EventPersister.Close() は、すべてのイベントが出力および削除された後に myQuery および myPersister に対して呼び出されます。
これらのメソッドについては、すべてこの章で後で詳しく説明します。接続を開き、テストし、閉じることに関する詳細は、“EventPersister の作成と接続” を参照してください。EventQuery<T> のインスタンスの作成および使用の詳細は、“クエリの使用法” を参照してください。
EventPersister の作成と接続
EventPersister クラスは XEP の主なエントリ・ポイントです。API では、データベース接続メソッド、スキーマをインポートするメソッド、トランザクション処理メソッド、および Event のインスタンスの作成を行ってデータベースのイベントにアクセスするメソッドを提供しています。
EventPersister のインスタンスは、次のメソッドによって作成され、破棄されます。
-
PersisterFactory.CreatePersister() — EventPersister の新しいインスタンスを返します。
-
EventPersister.Close() — この EventPersister インスタンスを閉じ、関連するリソースを解放します。
以下のメソッドを使用して、接続を作成します。
-
EventPersister.Connect() — namespace、username、password の String 引数を取り、指定されたネームスペースへの接続を確立します。
以下の例では、接続を確立します。
// Open a connection
string host = "127.0.0.1";
int port = 1972;
string namespace = "USER";
string username = "_SYSTEM";
string password = "SYS";
EventPersister myPersister = PersisterFactory.CreatePersister();
myPersister.Connect(host, port, namespace,username,password);
// perform event processing here . . .
myPersister.Close();
EventPersister.Connect() メソッドは、ホスト・マシンの指定されたポートとネームスペースへの接続を確立します。現在のプロセス内に接続が存在しない場合は、新しい接続が作成されます。接続が既に存在している場合は、そのメソッドによって既存の接続オブジェクトへの参照が返されます。
接続に関連するすべてのロック、ライセンス、およびその他のリソースを確実に解放するために、範囲外になる前に必ず EventPersister のインスタンスで Close() を呼び出します。
スキーマのインポート
.NET クラスのインスタンスを永続イベントとして格納するには、その前にそのクラスのスキーマをインポートする必要があります。スキーマによって、イベントが格納されるデータベース構造が定義されます。XEP は、フラット・スキーマとフル・スキーマの 2 つの異なるスキーマ・インポート・モデルを提供しています。これらのモデル間の主な相違は、.NET オブジェクトが ObjectScript オブジェクトとして投影される方法です。パフォーマンスが重要であり、イベント・スキーマが比較的単純である場合、フラット・スキーマが最適な選択になります。フル・スキーマは、より複雑なスキーマ用のより豊富な機能を提供しますが、パフォーマンスに影響を与える可能性があります。スキーマ・モデルと関連テーマの詳細は、“スキーマのカスタマイズとマッピング” を参照してください。
以下のメソッドを使用して、.NET クラスを分析し、目的のタイプのスキーマをインポートします。
-
EventPersister.ImportSchema() — フラット・スキーマをインポートします。.dll ファイル名、完全修飾クラス名、またはクラス名の配列を指定する引数を取り、指定場所で検出されたすべての依存性とすべてのクラスをインポートします。正常にインポートされたすべてのクラスの名前を含む String 配列を返します。
-
EventPersister.ImportSchemaFull() — フル・スキーマをインポートします。ImportSchema() と同じ引数を取り、同じクラス・リストを返します。このメソッドによってインポートされるクラスは、ユーザ生成 IdKey を宣言する必要があります (“IdKey の使用法” を参照してください)。
-
Event.IsEvent() — 静的 Event メソッド。引数としてすべての型の .NET オブジェクト名またはクラス名を受け取り、指定されたオブジェクトを有効な XEP イベント (“インポートされるクラスの要件” を参照) として投影可能かどうかを調べるためのテストを行い、それが有効でない場合は適切なエラーをスローします。
インポート・メソッドは、使用されるスキーマ・モデルを除いて同一です。以下の例は、単純なテスト・クラスおよびその依存クラスをインポートします。
ネームスペース test の以下のクラスがインポートされます。
namespace test {
public class MainClass {
public MainClass() {}
public String myString;
public test.Address myAddress;
}
public class Address {
public String street;
public String city;
public String state;
}
}
以下のコードは、IsEvent() を呼び出してメイン・クラス test.MainClass が投影可能であることを確認してから、ImportSchema() を使用してそのメイン・クラスをインポートします。test.MainClass がインポートされるときに、依存クラス test.Address も自動的にインポートされます。
try {
Event.IsEvent("test.MainClass"); // throw an exception if class is not projectable
myPersister.ImportSchema("test.MainClass");
}
catch (XEPException e) {Console.WriteLine("Import failed:\n" + e);}
この例では、依存クラス test.Address のインスタンスは、シリアル化されて test.MainClass の他のフィールドと同じ ObjectScript オブジェクトに埋め込まれます。代わりに ImportSchemaFull() が使用された場合、格納された test.MainClass のインスタンスには、別のデータベース・クラス・エクステントに格納されている test.Address のインスタンスへの参照が含まれます。
イベントの格納と変更
クラスのスキーマがインポートされると (“スキーマのインポート” を参照)、Event のインスタンスを作成して、そのクラスのイベントを格納し、それらにアクセスできます。Event クラスは、永続イベントの格納、更新、または削除、そのクラス・エクステントに対するクエリの作成、インデックス更新の制御を行うメソッドを提供します。このセクションでは、以下の項目について説明します。
-
イベントの作成と格納 — Event のインスタンスの作成方法およびそれを使用して指定クラスの永続イベントを格納する方法について説明します。
-
格納されたイベントへのアクセス — 指定したクラスの永続イベントをフェッチ、変更、および削除する Event メソッドについて説明します。
-
インデックス更新の制御 — インデックス・エントリを更新するタイミングを制御することで処理の効率性を高めることができる Event メソッドについて説明します。
イベントの作成と格納
Event クラスのインスタンスは、以下のメソッドによって作成され、破棄されます。
-
EventPersister.GetEvent() — className String 引数を取り、指定したクラスのイベントを格納し、それにアクセスできる Event のインスタンスを返します。オプションで、インデックス・エントリを更新する既定の方法を指定する indexMode 引数を取ります (詳細は、“インデックス更新の制御” を参照してください)。
Note:ターゲット・クラスEvent のインスタンスは、GetEvent() の呼び出しで className 引数によって指定されたクラスのイベントのみを格納、アクセス、または照会できます。この章では、クラス className をターゲット・クラスと呼びます。各例では、ターゲット・クラスは常に SingleStringSample です。
-
Event.Close() — この Event インスタンスを閉じ、それに関連するリソースを解放します。
以下の Event メソッドは、ターゲット・クラスの .NET オブジェクトを永続イベントとして格納します。
-
Store() — ターゲット・クラスの 1 つ以上のインスタンスをデータベースに追加します。引数として 1 つのイベント、または複数のイベントからなる 1 つの配列を取り、格納されているイベントごとに long データベース ID (データベース ID を返すことができない場合は 0) を返します。
Important:イベントが格納されるときは、どのようなテストも行われず、既存のデータが変更されたり、上書きされることはありません。各イベントは、可能な限り速い速度でエクステントに追加されるか、指定されたキーを持つイベントが既にデータベースに存在する場合は暗黙的に無視されます。
以下の例では、ターゲット・クラスとして SingleStringSample を指定して Event のインスタンスを作成し、それを使用して .NET SingleStringSample オブジェクトの配列を永続イベントとして投影します。この例では、myPersister が既に作成されて接続されており、SingleStringSample クラスのスキーマがインポートされていることを前提としています。この実行方法の例は、“永続イベントを格納および照会する単純なアプリケーション” を参照してください。SingleStringSample クラスと、このクラスを定義および使用するサンプル・プログラムの詳細は、“XEP のサンプル・アプリケーション” を参照してください。
SingleStringSample[] eventItems = SingleStringSample.generateSampleData(12);
try {
Event newEvent = myPersister.GetEvent("xep.samples.SingleStringSample");
long[] itemIdList = newEvent.Store(eventItems); // store all events
int itemCount = 0;
for (int i=0; i < itemIdList.Length; i++) {
if (itemIdList[i]>0) itemCount++;
}
Console.WriteLine("Stored " + itemCount + " of " + eventItems.Length + " events");
newEvent.Close();
}
catch (XEPException e) { Console.WriteLine("Event storage failed:\n" + e); }
-
SingleStringSample の generateSampleData() メソッドは、12 個の SingleStringSample オブジェクトを生成し、それらを eventItems という名前の配列に格納します。
-
EventPersister.GetEvent() メソッドは、ターゲット・クラスとして SingleStringSample を指定して newEvent という名前の Event インスタンスを作成します。
-
Event.Store() メソッドが呼び出され、eventItems 配列内の各オブジェクトをデータベース内の永続イベントとして投影します。
このメソッドは、itemIdList という名前の配列を返します。この配列には、正常に格納された各イベントの場合は long オブジェクト ID が、格納できなかったオブジェクトの場合は 0 が含まれます。変数 itemCount は、itemIdList の 0 より大きい各 ID に対して 1 回インクリメントされ、その合計が出力されます。
-
ループが終了すると、Event.Close() メソッドが呼び出されて、関連するリソースが解放されます。
接続に関連するすべてのロック、ライセンス、およびその他のリソースを確実に解放するために、範囲外になる前に必ず Event のインスタンスで Close() を呼び出します。
格納されたイベントへのアクセス
永続イベントが格納されると、そのターゲット・クラスの Event インスタンスが、イベントを読み取り、更新、および削除するための以下のメソッドを提供します。
-
DeleteObject() — 引数としてデータベース・オブジェクト ID または IdKey を取り、データベースから指定したイベントを削除します。
-
GetObject() — 引数としてデータベース・オブジェクト ID または IdKey を取り、指定したイベントを返します。
-
UpdateObject() — 引数としてデータベース・オブジェクト ID または IdKey およびターゲット・クラスの Object を取り、指定したイベントを更新します。
ターゲット・クラスが標準オブジェクト ID を使用する場合、それは long 値として指定されます (前のセクションで説明した Store() メソッドで返されるとおり)。ターゲット・クラスが IdKey を使用する場合、それは Object の配列として指定され、その配列の各項目はその IdKey を構成するフィールドの 1 つの値です (“IdKey の使用法” を参照してください)。
以下の例では、配列 itemIdList には、前に格納された SingleStringSample イベントのオブジェクト ID 値のリストが含まれています。例では、リストの最初の 6 個の永続イベントを適宜更新し、残りを削除します。
itemIdList 配列を作成した例は、“イベントの作成と格納” を参照してください。この例では、myPersister という名前の EventPersister インスタンスが既に作成され、データベースに接続されていることも前提としています。
// itemIdList is a previously created array of SingleStringSample event IDs
try {
Event newEvent = myPersister.GetEvent("xep.samples.SingleStringSample");
int itemCount = 0;
for (int i=0; i < itemIdList.Length; i++) {
try { // arbitrarily update events for first 6 Ids and delete the rest
SingleStringSample eventObject = (SingleStringSample)newEvent.GetObject(itemIdList[i]);
if (i<6) {
eventObject.name = eventObject.name + " (id=" + itemIdList[i] + ")" + " updated!";
newEvent.UpdateObject(itemIdList[i], eventObject);
itemCount++;
} else {
newEvent.DeleteObject(itemIdList[i]);
}
}
catch (XEPException e) {Console.WriteLine("Failed to process event:\n" + e);}
}
Console.WriteLine("Updated " + itemCount + " of " + itemIdList.Length + " events");
newEvent.Close();
}
catch (XEPException e) {Console.WriteLine("Event processing failed:\n" + e);}
-
EventPersister.GetEvent() メソッドは、ターゲット・クラスとして SingleStringSample を指定して newEvent という名前の Event インスタンスを作成します。
-
配列 itemIdList には、前に格納された SingleStringSample イベントのオブジェクト ID 値のリストが含まれています (itemIdList を作成した例は、“イベントの作成と格納” を参照してください)。
ループでは、itemIdList の各項目が処理されます。最初の 6 つの項目は変更および更新され、残りの項目は削除されます。以下の処理が実行されます。
-
Event.GetObject() メソッドが、itemIdList[i] に指定されたオブジェクト ID を持つ SingleStringSample オブジェクトをフェッチして、これを変数 eventObject に割り当てます。
-
eventObject name フィールドの値が変更されます。
-
eventObject がリストの最初の 6 項目の 1 つである場合は、Event.UpdateObject() が呼び出されてデータベース内でこれが更新されます。そうでない場合は、Event.DeleteObject() が呼び出されて、データベースからオブジェクトが削除されます。
-
-
itemIdList のすべての ID が処理されると、ループが終了し、更新されたイベントの数を示すメッセージが表示されます。
-
Event.Close() メソッドが呼び出されて、関連するリソースが解放されます。
SingleStringSample クラスを定義および使用するサンプル・プログラムの詳細は、“XEP のサンプル・アプリケーション” を参照してください。
簡単な SQL クエリによってフェッチされた永続イベントへのアクセス方法、およびその変更方法の詳細は、“クエリの使用法” を参照してください。
テスト・データベースを初期化する際は、多くの場合、クラス全体を削除するか、指定されたエクステントのすべてのイベントを削除すると便利です。以下の EventPersister メソッドは、クラスおよびエクステントをデータベースから削除します。
-
DeleteClass() — 引数として className 文字列を取り、指定した ObjectScript クラスを削除します。
-
DeleteExtent() — 引数として className 文字列を取り、指定したクラスのエクステントのすべてのイベントを削除します。
これらのメソッドは主としてテスト用に提供されているので、実際に使用するコードでは使用しないようにしてください。これらの用語の詳しい定義については、"サーバ側プログラミングの入門ガイド" の “クラスとエクステント” を参照してください。
インデックス更新の制御
既定では、データベース内のイベントに対して動作する Event メソッドの 1 つの呼び出しを実行するときに、インデックスは更新されません (“格納されたイベントへのアクセス” を参照してください)。インデックスは非同期に更新され、更新は、すべてのトランザクションが完了し、Event インスタンスが閉じられた後にのみ実行されます。一意のインデックスに対して一意性のチェックは実行されません。
このセクションは、標準オブジェクト ID または生成された IdKey を使用するクラスにのみ適用されます (“IdKey の使用法” を参照してください)。ユーザ割り当て IdKey を持つクラスは、同期でのみ更新できます。
この既定のインデックス作成動作を変更する方法はいくつかあります。Event インスタンスが EventPersister.GetEvent() によって作成される場合 (“イベントの作成と格納” を参照)、オプションの indexMode パラメータを設定して既定のインデックス作成動作を指定できます。以下のオプションを使用できます。
-
Event.INDEX_MODE_ASYNC_ON — 非同期のインデックス作成を可能にします。これは、indexMode パラメータが指定されていない場合の既定の設定です。
-
Event.INDEX_MODE_ASYNC_OFF — StartIndexing() メソッドが呼び出されない限り、インデックス作成は実行されません。
-
Event.INDEX_MODE_SYNC — インデックス作成は、エクステントが変更されるたびに実行されます。これは多数のトランザクションの場合は非効率的になる可能性があります。クラスにユーザ割り当て IdKey がある場合は、このインデックス・モードを指定する必要があります。
以下の Event メソッドを使用すると、ターゲット・クラスのエクステントに対する非同期インデックス更新を制御できます。
-
StartIndexing() — ターゲット・クラスのエクステントに対して非同期インデックス作成を開始します。インデックス・モードが Event.INDEX_MODE_SYNC である場合は、例外をスローします。
-
StopIndexing() — エクステントに対する非同期インデックス作成を停止します。Event インスタンスを閉じるときにインデックスを更新しないようにする場合は、Event.Close() を呼び出す前にこのメソッドを呼び出します。
-
WaitForIndexing() — int timeout 値を引数として取り、非同期インデックス作成が完了するまで待機します。timeout 値は、待機する秒数を指定します (-1 の場合は永久に待機し、0 の場合は直ちに返します)。インデックス作成が完了した場合は true を返し、インデックス作成が完了する前に待機がタイムアウトした場合は false を返します。インデックス・モードが Event.INDEX_MODE_SYNC である場合は、例外をスローします。
クエリの使用法
Event クラスは、ターゲット・クラスのエクステントに対して制限付き SQL クエリを実行できる EventQuery<T> のインスタンスを作成する方法を提供します。EventQuery<T> メソッドは、SQL クエリの実行、クエリ結果セット内の個別の項目の取得、更新、または削除を行うために使用します。
以下の項目について説明します。
-
クエリの作成と実行 — EventQuery<T> クラスのメソッドを使用してクエリを実行する方法について説明します。
-
クエリ・データの処理 — EventQuery<T> 結果セットの項目にアクセスして変更する方法について説明します。
-
フェッチ・レベルの定義 — クエリによって返されるデータの量の制御方法について説明します。
このセクションの例では、EventPersister オブジェクトの myPersister が既に作成されて接続されており、SingleStringSample クラスのスキーマがインポートされていることを前提としています。この実行方法の例は、“永続イベントを格納および照会する単純なアプリケーション” を参照してください。
クエリの作成と実行
以下のメソッドは、EventQuery<T> のインスタンスを作成および破棄します。
-
Event.CreateQuery() — SQL クエリのテキストを含む String 引数を取り、EventQuery<T> のインスタンスを返します。パラメータ T は、親 Event のターゲット・クラスです。
-
EventQuery<T>.Close() — この EventQuery<T> インスタンスを閉じ、それに関連するリソースを解放します。
EventQuery<T> のインスタンスによって実行されるクエリは、指定した汎用タイプ T (クエリ・オブジェクトを作成した Event インスタンスのターゲット・クラス) の .NET オブジェクトを返します。EventQuery<T> クラスによってサポートされるクエリは、以下に示すとおり、SQL の SELECT 文のサブセットです。
-
クエリは、SELECT 節、FROM 節、および (オプションで) WHERE、ORDER BY などの標準 SQL 節から構成される必要があります。
-
SELECT 節と FROM 節は構文的に適切にする必要がありますが、実際はクエリ実行中に無視されます。マップされているすべてのフィールドは、常に、ターゲット・クラス T のエクステントからフェッチされます。
-
SQL 式は、任意の型の配列、組み込みオブジェクト、または組み込みオブジェクトのフィールドのいずれも参照できません。
-
システム生成オブジェクト ID は、%ID と呼ばれることがあります。先頭に % があるため、フィールドで呼び出した id と .NET クラスで競合しません。
以下の EventQuery<T> メソッドは、クエリを定義および実行します。
-
AddParameter() — この EventQuery<T> と関連付けられた SQL クエリのパラメータを結合します。パラメータに結合する値を指定する引数として Object value を取ります。
-
Execute() — この EventQuery<T> と関連付けられた SQL クエリを実行します。クエリが正常に実行された場合、この EventQuery<T> には、後で説明するメソッドでアクセスできる結果セットが含まれます (“クエリ・データの処理” を参照してください)。
以下の例では、xep.samples.SingleStringSample エクステント内のイベントに対して単純なクエリを実行します (SingleStringSample クラスを定義および使用するサンプル・プログラムの詳細は、“XEP のサンプル・アプリケーション” を参照してください)。
Event newEvent = myPersister.GetEvent("xep.samples.SingleStringSample");
EventQuery<SingleStringSample> myQuery = null;
String sql =
"SELECT * FROM xep_samples.SingleStringSample WHERE %ID BETWEEN ? AND ?";
myQuery = newEvent.CreateQuery<SingleStringSample>(sql);
myQuery.AddParameter(3); // assign value 3 to first SQL parameter
myQuery.AddParameter(12); // assign value 12 to second SQL parameter
myQuery.Execute(); // get resultset for IDs between 3 and 12
EventPersister.GetEvent() メソッドは、ターゲット・クラスとして SingleStringSample を指定して newEvent という名前の Event インスタンスを作成します。
Event.CreateQuery() メソッドは、SQL クエリを実行してその結果セットを保持する myQuery という名前の EventQuery<T> のインスタンスを作成します。sql 変数には、2 つのパラメータ値の間の ID を持つターゲット・クラス内のすべてのイベントを選択する SQL 文が含まれています。
EventQuery<T>.AddParameter() メソッドが 2 回呼び出されて、2 つのパラメータに値を割り当てます。
EventQuery<T>.Execute() メソッドが呼び出されると、ターゲット・クラスのエクステントに対して指定されたクエリが実行され、結果セットが myQuery に格納されます。
既定では、結果セットの各オブジェクトのすべてのデータがフェッチされ、各オブジェクトが完全に初期化されます。各オブジェクトでフェッチされるデータの量および型を制限するオプションについては、“フェッチ・レベルの定義” を参照してください。
クエリ・データの処理
クエリが実行された後、以下の EventQuery<T> メソッドを使用して、クエリ結果セット内の項目にアクセスし、データベース内の対応する永続イベントを更新または削除できます。
-
GetNext() — ターゲット・クラスの次のオブジェクトを結果セットから返します。結果セットにそれ以上項目がない場合は、null を返します。
-
UpdateCurrent() — 引数としてターゲット・クラスのオブジェクトを取り、それを使用して GetNext() によって最後に返された永続イベントを更新します。
-
DeleteCurrent() — GetNext() によって最後に返された永続イベントをデータベースから削除します。
-
GetAll() — GetNext() を使用して結果セットからすべての項目を取得し、それらの項目を List で返します。更新または削除には使用できません。GetAll() および GetNext() は同じ結果セットにアクセスできません。一方のメソッドが呼び出されると、他方のメソッドは Execute() が再び呼び出されるまで使用できません。
Id または IdKey によって特定された永続イベントへのアクセス方法、およびその変更方法の詳細は、“格納されたイベントへのアクセス” を参照してください。
myQuery.Execute(); // get resultset
SingleStringSample currentEvent = myQuery.GetNext();
while (currentEvent != null) {
if (currentEvent.name.StartsWith("finished")) {
myQuery.DeleteCurrent(); // Delete if already processed
} else {
currentEvent.name = "in process: " + currentEvent.name;
myQuery.UpdateCurrent(currentEvent); // Update if unprocessed
}
currentEvent = myQuery.GetNext();
}
myQuery.Close();
この例で、EventQuery<T>.Execute() の呼び出しでは、前の例で説明したクエリが実行されることが前提です (“クエリの作成と実行” を参照)。また、結果セットは myQuery に格納されます。結果セットの各項目は SingleStringSample オブジェクトです。
GetNext() の最初の呼び出しによって、結果セットから最初の項目が取得され、それが currentEvent に割り当てられます。
while ループで、以下の処理が結果セットの各項目に適用されます。
-
currentEvent.name が文字列 "finished" で始まる場合、DeleteCurrent() は対応する永続イベントをデータベースから削除します。
-
それ以外の場合、currentEvent.name の値が変更され、UpdateCurrent() が呼び出されます。これは currentEvent を引数として取り、これを使用してデータベースの永続イベントを更新します。
-
GetNext() の呼び出しによって、結果セットから次の SingleStringSample オブジェクトが返され、それが currentEvent に割り当てられます。
ループが終了した後、Close() が呼び出され、myQuery に関連付けられたリソースを解放します。
接続に関連するすべてのロック、ライセンス、およびその他のリソースを確実に解放するために、範囲外になる前に必ず EventQuery<T> のインスタンスで Close() を呼び出します。
フェッチ・レベルの定義
フェッチ・レベルは、クエリを実行するときに返されるデータの量を制御するために使用できる Event プロパティです。基礎となるイベントが複雑でイベント・データの小さいサブセットのみが必要な場合、これは特に便利です。
以下の EventQuery<T> メソッドは、現在のフェッチ・レベルを設定し、返します。
-
GetFetchLevel() — Event の現在のフェッチ・レベルを示す int を返します。
-
SetFetchLevel() — 引数として Event フェッチ・レベル列挙の値の 1 つを取り、そのフェッチ・レベルを Event に対して設定します。
以下のフェッチ・レベル値がサポートされます。
-
Event.OPTION_FETCH_LEVEL_ALL — これが既定です。すべてのデータがフェッチされ、返されるイベントは完全に初期化されます。
-
Event.OPTION_FETCH_LEVEL_DATATYPES_ONLY — データ型フィールドのみがフェッチされます。これには、すべての単純な数値型とそれらに関連付けられた System 型、文字列、および列挙が含まれます。その他のフィールドはすべて null に設定されます。
-
Event.OPTION_FETCH_LEVEL_NO_ARRAY_TYPES — 配列を除くすべてのタイプがフェッチされます。ディメンジョンに関係なく、配列タイプのすべてのフィールドは null に設定されます。すべてのデータ型、オブジェクト・タイプ (シリアル化タイプを含む) およびコレクションがフェッチされます。
-
Event.OPTION_FETCH_LEVEL_NO_COLLECTIONS — コレクション・クラスの実装を除くすべてのタイプがフェッチされます。
-
Event.OPTION_FETCH_LEVEL_NO_OBJECT_TYPES — オブジェクト・タイプを除くすべてのタイプがフェッチされます。シリアル化タイプもオブジェクト・タイプと見なされ、フェッチされません。すべてのデータ型、配列タイプ、およびコレクションがフェッチされます。
スキーマのカスタマイズとマッピング
このセクションでは、.NET クラスが InterSystems IRIS イベント・スキーマにどのようにマップされるか、また最適なパフォーマンスを得るためにスキーマをどのようにカスタマイズできるかについて詳細に説明します。多くの場合、メタ情報を提供することなく、単純なクラスのスキーマをインポートできます。しかし、状況によって、スキーマのインポート方法のカスタマイズが必要となったり要望されたりすることがあります。以下のセクションには、カスタマイズ・スキーマおよびその生成方法に関する情報が掲載されています。
-
スキーマ・インポート・モデル — XEP によってサポートされる 2 つのスキーマ・インポート・モデルについて説明します。
-
属性の使用法 — XEP 属性を .NET クラスに追加して、作成する必要があるインデックスを指定できます。また、アノテーションを追加して、インポートしないフィールドまたはシリアル化する必要があるフィールドを指定してパフォーマンスを最適化することもできます。
-
IdKey の使用法 — アノテーションを使用して、IdKey (既定のオブジェクト ID の代わりに使用するインデックス値) を指定できます。IdKey はフル・スキーマをインポートする場合に必要です。
-
InterfaceResolver の実装 — 既定では、フラット・スキーマは、インタフェースとして宣言されたフィールドをインポートしません。スキーマのインポート時に、InterfaceResolver インタフェースの実装を使用して、インタフェースとして宣言されたフィールドの実際のクラスを指定することができます。
-
スキーマ・マッピング・ルール — .NET クラスが InterSystems IRIS イベント・スキーマにどのようにマップされるかについて詳しく説明します。
スキーマ・インポート・モデル
XEP は、フラット・スキーマとフル・スキーマの 2 つの異なるスキーマ・インポート・モデルを提供しています。これらのモデル間の主な相違は、.NET オブジェクトが ObjectScript オブジェクトに投影される方法です。
-
埋め込みオブジェクト・プロジェクション・モデル (フラット・スキーマ) — フラット・スキーマ をインポートします。そのスキーマでは、インポートされたクラスによって参照されるオブジェクトがすべてシリアル化されて埋め込まれ、すべての上位クラスで宣言されたフィールドはすべて、それらが、インポートされたクラス自体で宣言されているかのように収集され、投影されます。そのクラスのインスタンスのすべてのデータは、単一の %Library.PersistentOpens in a new tab オブジェクトとして格納され、元の .NET クラス構造に関する情報は保持されません。
-
フル・オブジェクト・プロジェクション・モデル (フル・スキーマ) — フル・スキーマ をインポートします。そのスキーマでは、インポートされたクラスによって参照されるオブジェクトがすべて別の %PersistentOpens in a new tab オブジェクトとして投影されます。継承されたフィールドは、同様に %PersistentOpens in a new tab クラスにインポートされている上位クラスのフィールドへの参照として投影されます。.NET ソース・クラスと ObjectScript の投影されたクラスの間には 1 対 1 の対応があるため、.NET クラス継承構造が保持されます。
フル・オブジェクト・プロジェクションでは、元の .NET クラスの継承構造が保持されますが、パフォーマンスに影響する場合があります。パフォーマンスが重要であり、イベント・スキーマが比較的単純である場合、フラット・オブジェクト・プロジェクションが最適な選択になります。パフォーマンスの低下を許容できる場合、より豊富な機能および複雑なスキーマのために、フル・オブジェクト・プロジェクションを使用できます。
埋め込みオブジェクト・プロジェクション・モデル (フラット・スキーマ)
既定では、XEP は、平坦化することで参照オブジェクトを投影するスキーマをインポートします。つまり、インポートされたクラスによって参照されるオブジェクトがすべてシリアル化されて埋め込まれ、すべての上位クラスで宣言されたフィールドはすべて、それらが、インポートされたクラス自体で宣言されているかのように収集され、投影されます。対応する ObjectScript オブジェクトは、%Library.PersistentOpens in a new tab を拡張し、シリアル化されて埋め込まれたオブジェクト (元の .NET オブジェクトには外部オブジェクトへの参照が含まれていた) を含んでいます。
つまり、フラット・スキーマでは、厳密な意味での継承は InterSystems IRIS 側に保持されません。例えば、以下の 3 つの .NET クラスを考えてみます。
class A {
String a;
}
class B : class A {
String b;
}
class C : class B {
String c;
}
クラス C をインポートすると、以下の ObjectScript クラスができます。
Class C : %Persistent ... {
Property a As %String;
Property b As %String;
Property c As %String;
}
対応する ObjectScript オブジェクトは、特別にインポートしない限り、A と B のいずれのクラスに対しても生成されません。ObjectScript オブジェクト C は、A と B のいずれも拡張しません。インポートされると A および B は、以下の構造になります。
Class A : %Persistent ... {
Property a As %String;
}
Class B : %Persistent ... {
Property a As %String;
Property b As %String;
}
すべての処理は、対応する ObjectScript オブジェクトに対してのみ実行されます。例えば、タイプ C のオブジェクトに対して Store() を呼び出すと、対応する C ObjectScript オブジェクトのみが格納されます。
.NET の子クラスが、そのスーパークラスでも宣言される同じ名前のフィールドを非表示にすると、XEP エンジンは常にその子フィールドの値を使用します。
フル・オブジェクト・プロジェクション・モデル (フル・スキーマ)
フル・オブジェクト・モデルでは、一致する継承構造をデータベース・クラスに作成することで、.NET 継承モデルを保持するスキーマをインポートします。すべてのオブジェクト・フィールドをシリアル化してすべてのデータを単一の ObjectScript オブジェクトに格納するのではなく、スキーマによって .NET ソース・クラスと投影された ObjectScript クラスとの間に 1 対 1 のリレーションシップが確立されます。フル・オブジェクト・プロジェクション・モデルでは、参照されるクラスがそれぞれ別々に格納され、指定されているクラスのフィールドが、対応する ObjectScript クラスのオブジェクトへの参照として投影されます。
参照されるクラスには、ユーザ定義 IdKey ([Id] または [Index] — “IdKey の使用法” を参照) を作成する属性が含まれている必要があります。オブジェクトが格納されるときは、参照されるすべてのオブジェクトが最初に格納され、結果の IdKey がその親オブジェクトに格納されます。残りの XEP と同様に、一意性チェックは実行されず、既存のデータの変更または上書きは試みられません。データは単に、可能な限り速い速度で追加されます。IdKey 値が、既に存在しているイベントを参照している場合、それは単にスキップされ、その既存のイベントの上書きが試みられることはありません。
[Embedded] クラス・レベル属性を使用すると、マークされたクラスのインスタンスを別々に格納するのではなく、シリアル化されたオブジェクトとして埋め込むことで、フル・スキーマを最適化できます。
フル・オブジェクト・モデルの使用法の例は、FlightLog サンプル・プログラム (“XEP のサンプル・アプリケーション” にリストされている) を参照してください。
属性の使用法
XEP エンジンは、.NET クラスを調べることで、XEP イベント・メタデータを推測します。追加情報は、属性を使用して .NET クラスで指定できます。属性は Intersystems.XEP.attributes ネームスペースにあります。.NET クラスは、XEP 永続イベントの定義に準拠している限り (“インポートされるクラスの要件” を参照してください)、ObjectScript クラスとして投影され、カスタマイズは必要ありません。
属性には、投影されるクラス内の個別のフィールドに適用されるものと、クラス全体に適用されるものがあります。
-
フィールド属性 — インポートされるクラスのフィールドに適用されます。
-
[Id] — そのフィールドが IdKey として機能するようになることを指定します。
-
[Serialized] — フィールドをシリアル化形式で格納および取得する必要があることを示します。
-
[Transient] — フィールドをインポートから除外する必要があることを示します。
-
-
クラス属性 — インポートされるクラス全体に適用されます。
-
[Embedded] — フル・スキーマでこのクラスのフィールドを、参照ではなく (フラット・スキーマと同様に) 埋め込みとする必要があることを示します。
-
[Index] — クラスのインデックスを宣言します。
-
[Id] でマークされたフィールドの値は、標準のオブジェクト ID を置換する IdKey として使用されます (“IdKey の使用法” を参照してください)。この属性は、クラスごとに 1 つのフィールドでのみ使用でき、そのフィールドは String、int、または long である必要があります (double は許容されますが推奨されません)。複合 IdKey を作成するには、代わりに [Index] 属性を使用します。[Id] でマークされたクラスは、[Index] を使用して複合プライマリ・キーも宣言することはできません。両方の属性が同じクラスに対して使用されている場合、例外がスローされます。
以下のパラメータを指定する必要があります。
-
generated — XEP がキー値を生成する必要があるかどうかを指定する bool。
-
generated = true — (既定の設定) キー値はサーバによって生成され、[Id] でマークされたフィールドは Int64 である必要があります。このフィールドは、挿入または保存の前は null であることが前提となっており、そのような操作の完了時に XEP によって自動的に入力されます。
-
generated=false — マークされたフィールドのユーザ割り当て値が IdKey 値として使用されます。フィールドは、String、int、Int32、long、または Int64 にできます。
-
次の例では、ssn フィールドのユーザ割り当て値がオブジェクト ID として使用されます。
using Id = InterSystems.XEP.Attributes.Id;
public class Person {
[Id(generated=false)]
Public String ssn;
public String name;
Public String dob;
}
[Serialized] 属性は、フィールドをシリアル化形式で格納および取得する必要があることを示します。
この属性は、シリアル化可能フィールド (配列を含みます。これは暗黙的にシリアル化可能です) の格納を最適化します。XEP エンジンは、データの格納または取得のための既定のメカニズムを使用するのではなく、シリアル・オブジェクト用の関連する読み取りまたは書き込みメソッドを呼び出します。マークされたフィールドがシリアル化可能でない場合は、例外がスローされます。
例 :
using Serialized = InterSystems.XEP.Attributes.Serialized;
public class MyClass {
[Serialized]
public xep.samples.Serialized serialized;
[Serialized]
public int[,,,] quadIntArray;
[Serialized]
public String[,] doubleStringArray;
}
// xep.samples.Serialized:
[Serializable]
public class Serialized {
public String name;
public int value;
}
[Transient] 属性は、フィールドをインポートから除外する必要があることを示します。マークされたフィールドは、ObjectScript クラスに投影されず、オブジェクトが格納またはロードされるときに無視されます。
例 :
using Transient = InterSystems.XEP.Attributes.Transient;
public class MyClass {
// this field will NOT be projected:
[Transient]
public String transientField;
// this field WILL be projected:
public String projectedField;
}
[Embedded] 属性は、フル・スキーマが生成される場合に使用できます (“スキーマ・インポート・モデル” を参照してください)。ObjectScript クラスに投影するときに、このクラスのフィールドを、参照ではなく (フラット・スキーマと同様に) シリアル化して埋め込む必要があることを示します。
例 :
using Embedded = InterSystems.XEP.Attributes.Embedded;
[Embedded]
public class Address {
String street;
String city;
String zip;
String state;
}
[Index] 属性を使用すると、1 つ以上の複合インデックスを宣言できます。
以下のパラメータに引数を指定する必要があります。
-
name — 複合インデックスの名前を含む String。
-
fields — 複合インデックスを構成するフィールドの名前を含む String の配列。
-
type — インデックス・タイプ。xep.attributes.IndexType 列挙には、以下の使用可能なタイプがあります。
-
IndexType.none — 既定値。インデックスがないことを示します。
-
IndexType.bitmap — ビットマップ・インデックス ("InterSystems SQL の使用法" の “ビットマップ・インデックス” を参照してください)。
-
IndexType.bitslice — ビットスライス・インデックス ("InterSystems SQL の使用法" の “概要” を参照してください)。
-
IndexType.simple — 1 つ以上のフィールドに対する標準インデックス。
-
IndexType.idkey — 標準 ID の代わりに使用されるインデックス (“IdKey の使用法” を参照してください)。
-
例 :
using Index = InterSystems.XEP.Attributes.Index;
using IndexType = InterSystems.XEP.Attributes.IndexType;
[Index(name="indexOne",fields=new string[]{"ssn","dob"},type=IndexType.idkey)]
public class Person {
public String name;
public String dob;
public String ssn;
}
IdKey の使用法
IdKey は、既定のオブジェクト ID の代わりに使用されるインデックス値です。単純 IdKey と複合 IdKey のどちらも XEP によってサポートされており、フル・スキーマでインポートされる .NET クラスにはユーザ生成 IdKey が必要です (“スキーマのインポート” を参照してください)。単一フィールドに対する IdKey は [Id] 属性で作成できます。複合 IdKey を作成するには、IndexType idkey と共に [Index] 属性を追加します。例えば、以下のクラスを指定したとします。
class Person {
String name;
int id;
String dob;
}
既定のストレージ構造では、添え字として標準オブジェクト ID を使用します。
^PersonD(1)=$LB("John",12,"1976-11-11")
以下の属性は、name および id フィールドを使用して、標準オブジェクト ID を置き換える newIdKey という名前の複合 IdKey を作成します。
[Index(name="newIdKey", fields=new String[]{"name","id"},type=IndexType.idkey)]
これは、以下のグローバル構造になります。
^PersonD("John",12)=$LB("1976-11-11")
XEP では、SQL コマンドなど他の方法で追加された IdKey も処理されます ("InterSystems SQL の使用法" の “インデックスでの Unique、PrimaryKey、IDKey キーワードの使用” を参照してください)。XEP エンジンは、基本クラスに IdKey が含まれているかどうかを自動的に判別し、適切なグローバル構造を生成します。
IdKey の使用法にはいくつかの制限があります。
-
IdKey 値は一意である必要があります。IdKey がユーザ生成である場合、一意性は呼び出し元のアプリケーションで確保する必要があり、XEP によって強制されることはありません。アプリケーションで、データベースに既に存在しているキー値を持つイベントの追加が試みられると、その試みは暗黙的に無視され、既存のイベントは変更されません。
-
IdKey を宣言するクラスには、それが他のインデックスも宣言している場合、非同期にインデックスを付けることはできません。
-
複合 IdKey ではフィールドの数に制限はありませんが、フィールドが String、int、または long であるか、それらに対応する System 型であることが必要です。double も使用できますが、非推奨です。
-
非常に高い継続的な挿入レートを必要とするまれな特定の状況では、パフォーマンスが低下することがあります。
IdKey に基づくイベントの取得、更新、および削除を可能にする Event メソッドの説明は、“格納されたイベントへのアクセス” を参照してください。
IdKey および標準のインターシステムズ・ストレージ・モデルの詳細は、"グローバルの使用法" の “多次元ストレージの SQL およびオブジェクトの使用法” を参照してください。SQL での IdKey の詳細は、"InterSystems SQL の使用法" の “インデックスの定義と作成” を参照してください。
サンプル・プログラム IdKeyTest および FlightLog は、IdKey の使用例を示しています (サンプル・プログラムの詳細は、“XEP のサンプル・アプリケーション” を参照してください)。
InterfaceResolver の実装
フラット・スキーマがインポートされるとき、継承階層に関する情報は保持されません (“スキーマ・インポート・モデル” を参照してください)。これにより、インタフェースとして宣言されているタイプを持つフィールドを処理するときに問題が発生します。これは、XEP エンジンは、フィールドの実際のクラスを把握する必要があるためです。既定では、そのようなフィールドはフラット・スキーマにインポートされません。この動作は、Intersystems.XEP.InterfaceResolver の実装を作成し、処理中に特定のインタフェース・タイプを解決することで、変更できます。
InterfaceResolver は、フラット・スキーマ・インポート・モデルにのみ関連しており、.NET クラス継承構造は保持されません。フル・スキーマ・インポート・モデルでは、.NET と ObjectScript のクラスの間に 1 対 1 のリレーションシップが確立されるため、インタフェースの解決に必要な情報が保持されます。
InterfaceResolver の実装は、フラット・スキーマ・インポート・メソッド ImportSchema() を呼び出す前に EventPersister に渡されます (“スキーマのインポート” を参照してください)。これにより、XEP エンジンに、処理中にインタフェース・タイプを解決する方法が提供されます。以下の EventPersister メソッドは、使用される実装を指定します。
-
EventPersister.SetInterfaceResolver() — 引数として InterfaceResolver のインスタンスを取ります。ImportSchema() が呼び出されると、指定されたインスタンスを使用して、インタフェースとして宣言されたフィールドを解決します。
以下の例は、クラスごとに InterfaceResolver の異なるカスタマイズされた実装を呼び出して、2 つの異なるクラスをインポートします。
try {
myPersister.SetInterfaceResolver(new test.MyFirstInterfaceResolver());
myPersister.ImportSchema("test.MyMainClass");
myPersister.SetInterfaceResolver(new test.MyOtherInterfaceResolver());
myPersister.ImportSchema("test.MyOtherClass");
}
catch (XEPException e) {Console.WriteLine("Import failed:\n" + e);}
SetInterfaceResolver() の最初の呼び出しによって、インポート・メソッドの呼び出し中に使用する実装として MyFirstInterfaceResolver (次の例で説明) の新しいインスタンスが設定されます。この実装は、SetInterfaceResolver() が再び呼び出されて別の実装が指定されるまで、ImportSchema() のすべての呼び出しで使用されます。
ImportSchema() の最初の呼び出しで、クラス test.MyMainClass がインポートされます。これには、インタフェース test.MyFirstInterface として宣言されたフィールドが含まれています。MyFirstInterfaceResolver のインスタンスが、そのインポート・メソッドで使用され、このフィールドの実際のクラスが解決されます。
SetInterfaceResolver() の 2 回目の呼び出しによって、ImportSchema() が再び呼び出されたときに使用する新しい実装として別の InterfaceResolver クラスのインスタンスが設定されます。
InterfaceResolver のすべての実装で、以下のメソッドを定義する必要があります。
-
InterfaceResolver.GetImplementationClass() は、インタフェースとして宣言されたフィールドの実際のデータ型を返します。このメソッドには以下のパラメータがあります。
-
interfaceClass — 解決されるインタフェース。
-
declaringClass — interfaceClass として宣言されたフィールドが含まれているクラス。
-
fieldName — インタフェースとして宣言された declaringClass のフィールドの名前が含まれている文字列。
-
次の例は、インタフェース、そのインタフェースの実装、およびそのインタフェースのインスタンスを解決する InterfaceResolver の実装を定義します。
この例では、解決されるインタフェースは、test.MyFirstInterface です。
namespace test {
public interface MyFirstInterface{ }
}
test.MyFirstImpl クラスは、InterfaceResolver によって返される必要がある test.MyFirstInterface の実装です。
namespace test {
public class MyFirstImpl : MyFirstInterface {
public MyFirstImpl() {}
public MyFirstImpl(String s) { fieldOne = s; }
public String fieldOne;
}
}
InterfaceResolver の以下の実装は、インタフェースが test.MyFirstInterface である場合はクラス test.MyFirstImpl を返し、それ以外の場合は null を返します。
using Intersystems.XEP;
namespace test {
public class MyFirstInterfaceResolver : InterfaceResolver {
public MyFirstInterfaceResolver() {}
public Type GetImplementationType(Type declaringClass,
String fieldName, Type interfaceClass) {
if (interfaceClass == typeof(test.MyFirstInterface)) {
return typeof(test.MyFirstImpl);
}
return null;
}
}
MyFirstInterfaceResolver のインスタンスが SetInterfaceResolver() によって指定されている場合、ImportSchema() の後続の呼び出しでは自動的にそのインスタンスが使用されて、test.MyFirstInterface として宣言されているすべてのフィールドが解決されます。そのようなフィールドごとに、そのフィールドを含むクラスにパラメータ declaringClass を設定し、fieldName をそのフィールドの名前に設定し、interfaceClass を test.MyFirstInterface に設定して、GetImplementationClass() メソッドが呼び出されます。このメソッドは、インタフェースを解決し、test.MyFirstImpl か null のいずれかを返します。
スキーマ・マッピング・ルール
このセクションでは、XEP スキーマがどのように構築されるかについて説明します。以下の項目について説明します。
-
インポートされるクラスの要件 — 永続イベントとして投影できるオブジェクトを作成するために .NET クラスが満たす必要がある構造ルールについて説明します。
-
名前付け規約 — .NET クラスおよびフィールドの名前をインターシステムズの名前付け規則に従うように変換する方法について説明します。
インポートされるクラスの要件
XEP スキーマ・インポート・メソッドは、以下の要件を満たさない限り、.NET クラスに対して有効なスキーマを作成できません。
-
インポートされる InterSystems IRIS クラスまたは派生クラスが、格納されたイベントにクエリを実行してそれにアクセスするために使用される場合、.NET ソース・クラスは引数のないパブリック・コンストラクタを明示的に宣言する必要があります。
-
.NET ソース・クラスは、Object として宣言されたフィールドと、Object を宣言の一部として使用する配列またはコレクションのいずれも含むことはできません。XEP エンジンがこのようなフィールドを検出すると、例外がスローされます。 [Transient] 属性 (“属性の使用法” を参照) を使用して、これらがインポートされないようにします。
Event.IsEvent() メソッドを使用して、.NET クラスまたはオブジェクトを分析し、それが XEP の意味で有効なイベントを作成できるかどうかを判定できます。前述の条件に加えて、以下の条件が検出された場合、このメソッドは XEPException をスローします。
-
循環した依存関係
-
決まった形式のないコレクション
-
String、単純な数値型またはそれに関連する System 型、列挙のいずれでもない Dictionary キー値。
永続イベントのフィールドは、単純な数値型またはそれらに関連する System 型、オブジェクト (組み込み/連続オブジェクトとして投影される)、列挙、およびコレクション・クラスから派生した型にすることができます。これらのデータ型を配列、入れ子になったコレクション、および配列のコレクションに含めることもできます。
既定では、投影されるフィールドは、.NET クラスのすべての機能を保持しない場合があります。特定のフィールドが以下の方法で変更されます。
-
.NET クラスに静的フィールドが含まれていても、これらは既定でプロジェクションから除外されます。対応する ObjectScript クラス・プロパティはありません。追加のフィールドは、[Transient] 属性を使用して除外できます (“属性の使用法” を参照してください)。
-
フラット・スキーマ (“スキーマ・インポート・モデル” を参照) では、内部 (入れ子にされた) .NETクラスを含むすべてのオブジェクト・タイプは、%SerialObjectOpens in a new tab クラスとしてデータベースに投影されます。オブジェクト内のフィールドは、別の ObjectScript プロパティとして投影されません。このオブジェクトは、ObjectScript の観点からは不明瞭です。
-
フラット・スキーマは、すべての継承されたフィールドを、それらが子クラスで宣言されたかのように投影します。
名前付け規約
対応する ObjectScript クラスおよびプロパティの名前は .NET での名前と同じです。ただし、.NET では許可されているがインターシステムズでは許可されていない以下の 2 つの特別な文字は例外です。
-
$ (ドル記号) は、InterSystems IRIS 側では "d" の 1 文字に投影されます。
-
_ (アンダースコア) は、InterSystems IRIS 側では "u" の 1 文字に投影されます。
クラス名は 255 文字に制限されます。この文字数はほとんどのアプリケーションで十分のはずです。ただし、対応するグローバル名には 31 文字の制限があります。これは通常 1 対 1 マッピングには不十分なため、XEP エンジンは、31 文字よりも長いクラス名用に透過的に一意のグローバル名を生成します。生成されるグローバル名は元の名前と同じではありませんが、それでも簡単に認識できるはずです。例えば、xep.samples.SingleStringSample クラスは、グローバル名 xep.samples.SingleStrinA5BFD を受け取ります。