ヘッダーをスキップ
Oracle® Coherenceクライアント・ガイド
リリース3.6.1
B61370-02
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次

前
 
次
 

26 .NETクライアントのWindowsフォーム・アプリケーションのサンプル

この章では、Coherence for .NETライブラリを使用する簡単なWindowsフォーム・アプリケーションを作成する手順について説明します。

一般的な手順

Coherence for .NETを使用するWindowsフォーム・アプリケーションを開発して構成するには、次の6つの基本的な手順が必要です。

  1. Windowsアプリケーション・プロジェクトの作成

  2. Coherence for .NETライブラリへの参照の追加

  3. App.configファイルの作成

  4. Coherence for .NET構成ファイルの作成

  5. アプリケーションの作成および設計

  6. アプリケーションの実装

Windowsアプリケーション・プロジェクトの作成

Windowsアプリケーションを新規に作成する手順は、次のとおりです。

  1. Visual Studio 2005で、「ファイル」→「新規」→「プロジェクト」タブに進みます。

  2. 新しいプロジェクト」ウィンドウで、プロジェクトの種類に「Visual C#」を、テンプレートに「Windowsアプリケーション」を選択します。プロジェクトの名前、場所(アプリケーションの格納先となるフルパス)およびソリューションを入力します。

    図26-1は、プロジェクトの名前、場所およびソリューションを入力した「新しいプロジェクト」ウィンドウを示しています。

    図26-1 「新しいプロジェクト」ウィンドウ

    「新しいプロジェクト」ウィンドウ
  3. OK」をクリックします。

    Visual Studioで、Program.csForm1.csおよびForm1.Designer.csの各ファイルが作成されます。図26-2は、作成されたプロジェクト・ファイルを表示する「ソリューション エクスプローラ」を示しています。

    図26-2 作成されたプロジェクト・ファイルを表示するソリューション エクスプローラ

    作成されたプロジェクト・ファイルを表示するソリューション エクスプローラ
  4. 必要に応じて、作成されたファイルの名前を変更します。

    この例では、ファイル名がContactCacheClient.csContactForm.csおよびContactForm.Designer.csにそれぞれ変更されます。

Coherence for .NETライブラリへの参照の追加

.NETアプリケーションでCoherence for .NETライブラリを使用するには、まずCoherence.dllライブラリに対する参照を追加する必要があります。

Coherence.dllライブラリに対する参照を追加する手順は、次のとおりです。

  1. プロジェクトで「プロジェクト」→「参照の追加」を選択するか、または「ソリューション エクスプローラ」の「参照設定」を右クリックして「参照の追加」を選択します。

  2. 表示される「参照の追加」ウィンドウで「参照」タブを選択し、ファイル・システム上のCoherence.dllライブラリを探します。図26-3は、「参照の追加」ウィンドウに表示された.dllファイルを示しています。

    図26-3 「参照の追加」ウィンドウ

    「参照の追加」ウィンドウ
  3. OK」をクリックします。

App.configファイルの作成

Coherence for .NETライブラリを正しく構成するには、ライブラリで使用される各構成ファイルの適切なファイル名を記述したApp.config XMLファイルを作成する必要があります。

  1. ソリューション エクスプローラ」でプロジェクトを右クリックし、「追加」→「新しい項目」タブを選択します。

  2. 新しい項目の追加」ウィンドウで「アプリケーション構成ファイル」を選択します。

    図26-4は、「新しい項目の追加」ウィンドウの内容を示しています。

    図26-4 「新しい項目の追加」ウィンドウ

    「新しい項目の追加」ウィンドウ
  3. 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の構成セクションの要素は、次のとおりです。

Coherence for .NET構成ファイルの作成

例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} &lt;{level}&gt; (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ユーザー定義型のみを操作しますが、他のユーザー定義型にも同様の手法を使用できます。

アプリケーションでコントロールを追加する手順は、次のとおりです。

  1. 「表示」→「ツールボックス」に進みます。

  2. ツールボックス」ウィンドウで使用するコントロールを選択して、Windowsフォーム上にドラッグします。

  3. コントロールごとに、コントロールを右クリックして「プロパティ」タブを選択し、必要なプロパティを設定します。

図26-5は、ここまでの手順が完了したContact Cache InfoアプリケーションのUIの外観を示しています。

図26-5 Contact Cache ClientのUI

Contact Cache Clientの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コントロールをデータ・ソース・オブジェクトにバインドする必要があります。

  1. ツールボックス」ウィンドウでBindingSourceオブジェクトを選択して、フォーム上にドラッグします。

  2. そのプロパティを設定します。NameフィールドにcontactsBindingSourceと入力し、DataSourceフィールドの右端の矢印ボタンをクリックしてそのデータ・ソースを設定します。ドロップダウン・ウィンドウで「プロジェクト データ ソースの追加」を選択すると、「データ ソース構成ウィザード」が表示されます。「オブジェクト」を選択し、プロジェクト内のContactInfoクラスを探します。

    図26-6 「データ ソース構成ウィザード」を使用したデータ・ソースへのコントロールのバインド

    データ・ソース構成ウィンドウ
  3. 最後に、DataGridViewコントロールをcontactBindingSourceにバインドします。これを行うには、DataGridViewのプロパティ・ウィンドウのDataSourceフィールドで、ドロップダウン・ウィンドウからcontactsBindingSourceを選択します。これを図26-7に示します。

    図26-7 コントロールにバインドするデータ・ソースの選択

    DataSourceプロパティの編集

これでcontactsBindingSourceDataGridViewコントロールにバインドされました。これ以降のデータ処理は、ナビゲート、ソート、フィルタリングおよび更新を含め、すべて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でイベント・ハンドラをアプリケーションに追加する手順は次のとおりです。

  1. イベント・ハンドラを実装するWindowコンポーネントを右クリックし、「プロパティ」を選択します。

  2. プロパティ」ウィンドウ上部のツールバーにある稲妻ボタンをクリックすると、対象コンポーネントで発生させることのできるイベントがすべて表示されます。

    図26-8 「プロパティ」ウィンドウ

    「プロパティ」ウィンドウ
  3. 処理するイベントを選択してダブルクリックします。必要なコードがアプリケーションに追加され、イベントを処理できるようになります。次に、空のイベント・ハンドラ・メソッドを実装する必要があります。

例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);
        }
    }
}