この章では、Coherence for .NETライブラリを使用する簡単なWindowsフォーム・アプリケーションを作成する手順について説明します。
Windowsアプリケーションを新規に作成する手順は、次のとおりです。
Visual Studio 2005で、「ファイル」→「新規」→「プロジェクト」タブに進みます。
「新しいプロジェクト」ウィンドウで、プロジェクトの種類に「Visual C#」を、テンプレートに「Windowsアプリケーション」を選択します。プロジェクトの名前、場所(アプリケーションの格納先となるフルパス)およびソリューションを入力します。
図26-1は、プロジェクトの名前、場所およびソリューションを入力した「新しいプロジェクト」ウィンドウを示しています。
「OK」をクリックします。
Visual Studioで、Program.cs
、Form1.cs
およびForm1.Designer.cs
の各ファイルが作成されます。図26-2は、作成されたプロジェクト・ファイルを表示する「ソリューション エクスプローラ」を示しています。
必要に応じて、作成されたファイルの名前を変更します。
この例では、ファイル名がContactCacheClient.cs
、ContactForm.cs
およびContactForm.Designer.cs
にそれぞれ変更されます。
.NETアプリケーションでCoherence for .NETライブラリを使用するには、まずCoherence.dll
ライブラリに対する参照を追加する必要があります。
Coherence.dll
ライブラリに対する参照を追加する手順は、次のとおりです。
プロジェクトで「プロジェクト」→「参照の追加」を選択するか、または「ソリューション エクスプローラ」の「参照設定」を右クリックして「参照の追加」を選択します。
表示される「参照の追加」ウィンドウで「参照」タブを選択し、ファイル・システム上のCoherence.dll
ライブラリを探します。図26-3は、「参照の追加」ウィンドウに表示された.dll
ファイルを示しています。
「OK」をクリックします。
Coherence for .NETライブラリを正しく構成するには、ライブラリで使用される各構成ファイルの適切なファイル名を記述したApp.config
XMLファイルを作成する必要があります。
「ソリューション エクスプローラ」でプロジェクトを右クリックし、「追加」→「新しい項目」タブを選択します。
「新しい項目の追加」ウィンドウで「アプリケーション構成ファイル」を選択します。
図26-4は、「新しい項目の追加」ウィンドウの内容を示しています。
「OK」をクリックします。
例26-1は、有効なApp.config
構成ファイルのサンプルを示しています。
例26-1 App.configファイルのサンプル
<?xml version="1.0"?> <configuration> <configSections> <section name="coherence" type="Tangosol.Util.CoherenceConfigHandler, Coherence"/> </configSections> <coherence> <cache-factory-config>coherence.xml</cache-factory-config> <cache-config>cache-config.xml</cache-config> <pof-config>pof-config.xml</pof-config> </coherence> </configuration>
<configSections>
には、Coherence for .NETの構成セクションへのアクセスを処理するクラスを指定する必要があります。
Coherence for .NETの構成セクションの要素は、次のとおりです。
cache-factory-config
: CacheFactory
で使用するIConfigurableCacheFactoryおよびLoggerを構成するために、CacheFactory
で使用される構成ディスクリプタのパスを記述します。
cache-config
: 前述のキャッシュ構成(「クライアントでのCoherence*Extendの構成」を参照)を含むキャッシュ構成ディスクリプタのパスを記述します。このキャッシュ構成ディスクリプタは、DefaultConfigurableCacheFactoryで使用されます。
pof-config
: アプリケーションで使用するカスタム型を登録するために、ConfigurablePofContext
で使用される構成ディスクリプタのパスを記述します。
例26-2は、coherence.xml
構成ファイルのサンプルを示しています。
例26-2 .NET用coherence.xmlファイルのサンプル
<?xml version="1.0"?> <coherence xmlns="http://schemas.tangosol.com/coherence"> <logging-config> <destination>ContactCache.log</destination> <severity-level>5</severity-level> <message-format>{date} <{level}> (thread={thread}): {text}</message-format> <character-limit>8192</character-limit> </logging-config> </coherence>
例26-3は、cache-config.xml
構成ファイルのサンプルを示しています。
例26-3 .NET用cache-config.xmlファイルのサンプル
<?xml version="1.0"?> <cache-config xmlns="http://schemas.tangosol.com/cache"> <caching-scheme-mapping> <cache-mapping> <cache-name>dist-contact-cache</cache-name> <scheme-name>extend-direct</scheme-name> </cache-mapping> </caching-scheme-mapping> <caching-schemes> <remote-cache-scheme> <scheme-name>extend-direct</scheme-name> <service-name>ExtendTcpCacheService</service-name> <initiator-config> <tcp-initiator> <remote-addresses> <socket-address> <address>localhost</address> <port>9099</port> </socket-address> </remote-addresses> </tcp-initiator> <outgoing-message-handler> <request-timeout>30s</request-timeout> </outgoing-message-handler> </initiator-config> </remote-cache-scheme> </caching-schemes> </cache-config>
例26-4は、pof-config.xml
構成ファイルのサンプルを示しています。
例26-4 .NET用pof-config.xmlファイルのサンプル
<?xml version="1.0"?> <pof-config xmlns="http://schemas.tangosol.com/pof"> <user-type-list> <!-- include all "standard" Coherence POF user types --> <include>assembly://Coherence/Tangosol.Config/coherence-pof-config.xml</include> <!-- include all application POF user types --> <user-type> <type-id>1001</type-id> <class-name>ContactCache.Windows.ContactInfo, ContactCacheClient</class-name> </user-type> </user-type-list> </pof-config>
これらの構成ファイルを作成すると、Coherenceクラスタに接続する準備が完了し、Coherence for .NETでサポートされるすべての操作を実行できるようになります。
次に、Windowsフォームにコントロールを追加する必要があります。この例では、INamedCache
へのオブジェクトの格納、キャッシュからの読取り、キャッシュの問合せ、キャッシュからのアイテムの削除、およびキャッシュのクリアを行う方法について示します。そのために、ユーザーがクリックしたときにイベントを発生させるボタン、オブジェクトを編集するための数個のTextBox
コンポーネント、およびINamedCache
の現在の内容を表示するDataGridView
を使用します。ここではContactInfo
ユーザー定義型のみを操作しますが、他のユーザー定義型にも同様の手法を使用できます。
アプリケーションでコントロールを追加する手順は、次のとおりです。
「表示」→「ツールボックス」に進みます。
「ツールボックス」ウィンドウで使用するコントロールを選択して、Windowsフォーム上にドラッグします。
コントロールごとに、コントロールを右クリックして「プロパティ」タブを選択し、必要なプロパティを設定します。
図26-5は、ここまでの手順が完了したContact Cache InfoアプリケーションのUIの外観を示しています。
サンプルのWindowsアプリケーションを実装するには、まずIPortableObject
インタフェースを実装するContactInfo
クラスを作成します。
例26-5 IPortableObjectを実装するサンプル・クラス
public class ContactInfo : IPortableObject { private string name; private string street; private string city; private string state; private string zip; public ContactInfo() { } public ContactInfo(string name, string street, string city, string state, string zip) { this.name = name; this.street = street; this.city = city; this.state = state; this.zip = zip; } public void ReadExternal(IPofReader reader) { name = reader.ReadString(0); street = reader.ReadString(1); city = reader.ReadString(2); state = reader.ReadString(3); zip = reader.ReadString(4); } public void WriteExternal(IPofWriter writer) { writer.WriteString(0, name); writer.WriteString(1, street); writer.WriteString(2, city); writer.WriteString(3, state); writer.WriteString(4, zip); } // property definitions omitted for brevity }
アプリケーションでイベントを処理するには、事前にDataGridView
コントロールをデータ・ソース・オブジェクトにバインドする必要があります。
「ツールボックス」ウィンドウでBindingSource
オブジェクトを選択して、フォーム上にドラッグします。
そのプロパティを設定します。NameフィールドにcontactsBindingSource
と入力し、DataSourceフィールドの右端の矢印ボタンをクリックしてそのデータ・ソースを設定します。ドロップダウン・ウィンドウで「プロジェクト データ ソースの追加」を選択すると、「データ ソース構成ウィザード」が表示されます。「オブジェクト」を選択し、プロジェクト内のContactInfo
クラスを探します。
最後に、DataGridView
コントロールをcontactBindingSource
にバインドします。これを行うには、DataGridView
のプロパティ・ウィンドウのDataSource
フィールドで、ドロップダウン・ウィンドウからcontactsBindingSource
を選択します。これを図26-7に示します。
これでcontactsBindingSource
がDataGridView
コントロールにバインドされました。これ以降のデータ処理は、ナビゲート、ソート、フィルタリングおよび更新を含め、すべてBindingSourceコンポーネントに対するコールによって行われます。この他、フィルタリングを管理するためのIFilter
フィールドとCacheEventFilter
フィールド、およびキャッシュ・イベントへの応答として実行する必要のあるイベント処理コードをUIスレッド上で実行するためのWindowsFormsCacheListener
フィールドが必要です。そのためには、各キャッシュ・イベントの処理をメソッドに委任し、AddCacheListener()
メソッドを使用してキャッシュにリスナーを登録する必要があります。これについての詳細は、「キャッシュ・イベントへの応答」を参照してください。さらにコンストラクタでは、アプリケーションで使用するINamedCache
をstaticなCacheFactory.GetCache()
メソッドで取得し、検索属性の選択に使用するComboBoxを初期化します。
例26-6 リスナーの追加
/// <summary> /// Named cache. /// </summary> private INamedCache cache; /// <summary> /// Listener that allows end users to handle Coherence cache events, /// which are always raised from a background thread. /// </summary> private WindowsFormsCacheListener listener; /// <summary> /// Evaluate the specified extracted value. /// </summary> private IFilter filter; /// <summary> /// Wrapper filter, used by listeners. /// </summary> private CacheEventFilter cacheEventFilter; /// <summary> /// Search pattern. /// </summary> private string pattern; /// <summary> /// Default constructor. /// </summary> public ContactForm() { listener = new WindowsFormsCacheListener(this); listener.EntryInserted += new CacheEventHandler(AddRow); listener.EntryUpdated += new CacheEventHandler(UpdateRow); listener.EntryDeleted += new CacheEventHandler(DeleteRow); cache = CacheFactory.GetCache("dist-contact-cache"); cache.AddCacheListener(listener); InitializeComponent(); InitializeComboBox(); } /// <summary> /// Initialize <b>ComboBox</b> with attribute names. /// </summary> /// <remarks> /// Choosing attribute from the ComboBox allows to search for given /// pattern in choosen entry attribute inside the named cache. /// </remarks> private void InitializeComboBox() { cmbAttribute.Items.Add("Name"); cmbAttribute.Items.Add("Street"); cmbAttribute.Items.Add("City"); cmbAttribute.Items.Add("State"); cmbAttribute.Items.Add("Zip"); cmbAttribute.SelectedIndex = 0; }
他のWindowsアプリケーション同様、残りの実装の大半はイベント処理に関係します。Windowsフォーム内の各コンポーネントでイベントが発生する可能性があるため、各イベントを処理するイベント・ハンドラを作成する必要があります。Visual Studioでイベント・ハンドラをアプリケーションに追加する手順は次のとおりです。
イベント・ハンドラを実装するWindowコンポーネントを右クリックし、「プロパティ」を選択します。
「プロパティ」ウィンドウ上部のツールバーにある稲妻ボタンをクリックすると、対象コンポーネントで発生させることのできるイベントがすべて表示されます。
処理するイベントを選択してダブルクリックします。必要なコードがアプリケーションに追加され、イベントを処理できるようになります。次に、空のイベント・ハンドラ・メソッドを実装する必要があります。
例26-7は、サンプルのWindowsアプリケーションでのイベント・コードを示しています。
例26-7 イベントの追加
/// <summary> /// Load form event handler. /// </summary> /// <param name="sender"> /// The source of the event. /// </param> /// <param name="e"> /// An <b>EventArgs</b> that contains no event data. /// </param> private void ContactForm_Load(object sender, EventArgs e) { RefreshContactsGrid(true); } /// <summary> /// Closed form event handler. /// </summary> /// <remarks> /// Removes the event handlers. /// </remarks> /// <param name="sender"> /// The source of the event. /// </param> /// <param name="e"> /// An <b>EventArgs</b> that contains no event data. /// </param> private void ContactForm_FormClosed(object sender, FormClosedEventArgs e) { cache.RemoveCacheListener(listener, cacheEventFilter); } /// <summary> /// Enter cell event handler for the <b>addressDataGridView</b>. /// </summary> /// <remarks> /// Refreshes the <b>TextBox</b>es with data from selected /// <b>addressDataGridView</b> row. /// </remarks> /// <param name="sender"> /// The source of the event. /// </param> /// <param name="e"> /// An <b>EventArgs</b> that contains no event data. /// </param> private void addressDataGridView_CellEnter(object sender, DataGridViewCellEventArgs e) { DataGridViewCellCollection cells = addressDataGridView.CurrentRow.Cells; txtName.Text = (string) cells[0].Value; txtStreet.Text = (string) cells[1].Value; txtCity.Text = (string) cells[2].Value; txtState.Text = (string) cells[3].Value; txtZip.Text = (string) cells[4].Value; } /// <summary> /// Click event handler for <b>Put</b> button. /// </summary> /// <remarks> /// Stores the <see cref="ContactInfo"/> data entered in /// <b>TextBox</b>es into the INamedCache. /// </remarks> /// <param name="sender"> /// The source of the event. /// </param> /// <param name="e"> /// An <b>EventArgs</b> that contains no event data. /// </param> private void btnPut_Click(object sender, EventArgs e) { String name = txtName.Text; ContactInfo contact = new ContactInfo(txtName.Text, txtStreet.Text, txtCity.Text, txtState.Text, txtZip.Text); cache.Insert(name, contact); } /// <summary> /// Click event handler for the <b>Remove</b> button. /// </summary> /// <remarks> /// Removes the <see cref="ContactInfo"/> mapped by the current /// Name <b>TextBox</b> value. If there is no such entry in the /// <b>INamedCache</b>, a simple warning box is displayed. /// </remarks> /// <param name="sender"> /// The source of the event. /// </param> /// <param name="e"> /// An <b>EventArgs</b> that contains no event data. /// </param> private void btnRemove_Click(object sender, EventArgs e) { cache.Remove(txtName.Text); ResetTextBoxes(); } /// <summary> /// Click event handler for the <b>Clear</b> button. /// </summary> /// <remarks> /// Clears the <b>INamedCache</b>. /// </remarks> /// <param name="sender"> /// The source of the event. /// </param> /// <param name="e"> /// An <b>EventArgs</b> that contains no event data. /// </param> private void btnClear_Click(object sender, EventArgs e) { cache.RemoveCacheListener(listener, cacheEventFilter); cache.Clear(); cache.AddCacheListener(listener, cacheEventFilter, false); contactsBindingSource.Clear(); ResetTextBoxes(); } /// <summary> /// Click event handler for <b>Refresh</b> button. /// </summary> /// <remarks> /// Refreshes the <b>addressDataGridView</b>, filtering named cache /// entries by a given attribute and string pattern. If empty string /// is provided as a pattern all entries in the named cache will be /// accounted and displayed. /// </remarks> /// <param name="sender"> /// The source of the event. /// </param> /// <param name="e"> /// An <b>EventArgs</b> that contains no event data. /// </param> private void btnRefresh_Click(object sender, EventArgs e) { string newPattern = txtPattern.Text; string attribute = (string) cmbAttribute.SelectedItem; if (!newPattern.Equals(pattern)) { pattern = newPattern; cache.RemoveCacheListener(listener, cacheEventFilter); if (pattern != String.Empty) { IValueExtractor extractor = new ReflectionExtractor("get" + attribute); filter = new LikeFilter(extractor, pattern, '\\', false); cacheEventFilter = new CacheEventFilter(CacheEventFilter.CacheEventMask.All | CacheEventFilter.CacheEventMask.UpdatedEntered | CacheEventFilter.CacheEventMask.UpdatedLeft, filter); } else { filter = null; cacheEventFilter = null; } cache.AddCacheListener(listener, cacheEventFilter, false); } RefreshContactsGrid(true); } /// <summary> /// Click event handler for <b>SelectIndexChanged</b> event. /// </summary> /// <remarks> /// Resets the pattern string to Refresh button click event /// handler would work properly. /// </remarks> /// <param name="sender"> /// The source of the event. /// </param> /// <param name="e"> /// An <b>EventArgs</b> that contains no event data. /// </param> private void cmbAttribute_SelectedIndexChanged(object sender, EventArgs e) { pattern = ""; }
また、コンストラクタで委任したとおりにキャッシュ・イベント・ハンドラを作成する必要があります。これを例26-8に示します。
例26-8 キャッシュ・イベント・ハンドラの追加
/// <summary> /// Event handler for <see cref="ICacheListener.EntryInserted"/> /// event. /// <param name="sender"> /// The source of the event. /// </param> /// <param name="args"> /// An <see cref="CacheEventArgs"/>. /// </param> private void AddRow(object sender, CacheEventArgs args) { contactsBindingSource.Add(args.NewValue); } /// <summary> /// Event handler for <see cref="ICacheListener.EntryUpdated"/> /// event. /// </summary> /// <param name="sender"> /// The source of the event. /// </param> /// <param name="args"> /// An <see cref="CacheEventArgs"/>. /// </param> public void UpdateRow(object sender, CacheEventArgs args) { int index = contactsBindingSource.IndexOf(args.OldValue); if (index < 0) { // updated entered contactsBindingSource.Add(args.NewValue); } else { if (SatisfiesFilter(args.NewValue)) { contactsBindingSource[index] = args.NewValue; } else { contactsBindingSource.RemoveAt(index); } } } /// <summary> /// Event handler for <see cref="ICacheListener.EntryDeleted"/> /// event. /// </summary> /// <param name="sender"> /// The source of the event. /// </param> /// <param name="args"> /// An <see cref="CacheEventArgs"/>. /// </param> public void DeleteRow(object sender, CacheEventArgs args) { contactsBindingSource.Remove(args.OldValue); }
例26-9は、前述の例のイベント・ハンドラで使用されるヘルパー・メソッドを示しています。
例26-9 イベント・ハンドラで使用するヘルパー・メソッドの追加
/// <summary> /// Resets all of the text boxes on the form. /// </summary> private void ResetTextBoxes() { txtName.Text = ""; txtStreet.Text = ""; txtCity.Text = ""; txtState.Text = ""; txtZip.Text = ""; } /// <summary> /// Initialize <b>ComboBox</b> with attribute names. /// </summary> /// <remarks> /// Choosing attribute from the ComboBox allows to search for given /// pattern in choosen entry attribute inside the named cache. /// </remarks> private void InitializeComboBox() { cmbAttribute.Items.Add("Name"); cmbAttribute.Items.Add("Street"); cmbAttribute.Items.Add("City"); cmbAttribute.Items.Add("State"); cmbAttribute.Items.Add("Zip"); cmbAttribute.SelectedIndex = 0; } /// <summary> /// Queries the object with specified filter criteria. /// </summary> /// <param name="obj"> /// An object to which the test is applied. /// </param> /// <returns> /// <b>true</b> if the test passes, <b>false</b> otherwise. /// </returns> private bool SatisfiesFilter(object obj) { IFilter clientFilter = new LikeFilter(new ReflectionExtractor((string) cmbAttribute.SelectedItem), pattern, '\\', false); return clientFilter.Evaluate(obj); } /// <summary> /// Refreshes the contacts table. /// </summary> /// <param name="updateContacts"> /// Flag specifying whether to query against cache to get /// the most recent data or not. /// </param> private void RefreshContactsGrid(bool updateContacts) { if (updateContacts) { RefreshContacts(); } contactsBindingSource.ResetBindings(false); } /// <summary> /// Refreshes the contacts table with the most recent data within the /// cache. /// </summary> private void RefreshContacts() { contactsBindingSource.Clear(); ICollection cacheEntries = (filter == null ? cache.Values : cache.GetEntries(filter)); foreach (object entry in cacheEntries) { if (entry is DictionaryEntry) { contactsBindingSource.Add(((DictionaryEntry) entry).Value); } else { contactsBindingSource.Add(entry); } } }