Skip Headers
Oracle® Coherence Client Guide
Release 3.7.1

Part Number E22839-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

24 Sample Windows Forms Application for .NET Clients

This chapter provides step-by-step instructions that explains how to create a simple Windows Forms Application that uses the Coherence for .NET library.

The following sections are included in this chapter:

24.1 Create a Windows Application Project

To create a Windows Application, follow these steps:

  1. Go to the File->New->Project... tab in Visual Studio 2005.

  2. In the New Project window choose the Visual C# project type and Windows Application template. Enter the name, location (full path where you want to store your application), and solution for your project.

    Figure 24-1 illustrates the New Project window with the name, location, and solution for the project.

    Figure 24-1 New Project Window

    new project window
  3. Click OK.

    Visual Studio should have created the following files: Program.cs, Form1.cs and Form1.Designer.cs. Figure 24-2 illustrates the Solution Explorer with the created project files

    Figure 24-2 Solution Explorer with the Created Project Files

    Solution Explorer with the Created Project Files
  4. Rename these files if you want.

    In this example they have been renamed to ContactCacheClient.cs, ContactForm.cs, and ContactForm.Designer.cs respectively.

24.2 Add a Reference to the Coherence for .NET Library

To use the Coherence for .NET library in your .NET application, you must first add a reference to the Coherence.dll library.

Adding a reference to the Coherence.dll library:

  1. In your project go to Project->Add Reference... or right click References in the Solution Explorer and choose Add Reference...

  2. In the Add Reference window that appears choose the Browse tab and find the Coherence.dll library on your file system. Figure 24-3 illustrates the .dll files in the Add Reference window.

    Figure 24-3 Add Reference Window

    The Add Reference Window
  3. Click OK.

24.3 Create an App.config File

To correctly configure the Coherence for .NET library, you must create an App.config XML file that contains the appropriate file names for each configuration file used by the library.

  1. Right-click the project in the Solution Explorer and choose the Add->New Item... tab.

  2. In the Add New Item window select the Application Configuration File.

    Figure 24-4 illustrates the contents of the Add New Item window.

    Figure 24-4 Add New Item Window

    The Add New Item Window
  3. Click OK.

Example 24-1 illustrates a sample valid App.config configuration file.

Example 24-1 Sample App.config File

<?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>

In <configSections> you must specify a class that handles access to the Coherence for .NET configuration section.

Elements within the Coherence for .NET configuration section are:

24.4 Create Coherence for .NET Configuration Files

Example 24-2 illustrates a sample coherence.xml configuration file

Example 24-2 Sample coherence.xml File for .NET

<?xml version="1.0"?>

<coherence xmlns="http://schemas.tangosol.com/coherence"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://schemas.tangosol.com/coherence
   assembly://Coherence/Tangosol.Config/coherence.xsd">
   <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>

Example 24-3 illustrates a sample cache-config.xml configuration file.

Example 24-3 Sample cache-config.xml File for .NET

<?xml version="1.0"?>

<cache-config xmlns="http://schemas.tangosol.com/cache"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://schemas.tangosol.com/cache
   assembly://Coherence/Tangosol.Config/cache-config.xsd">
  <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>

Example 24-4 illustrates a sample pof-config.xml configuration file.

Example 24-4 Sample pof-config.xml File for .NET

<?xml version="1.0"?>

<pof-config xmlns="http://schemas.tangosol.com/pof"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://schemas.tangosol.com/pof
   assembly://Coherence/Tangosol.Config/pof-config.xsd">
   <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>

Having created these configuration files, everything is now in place to connect to a Coherence cluster and perform all operations supported by Coherence for .NET.

24.5 Create and Design the Application

Next, you must add controls to your Windows form. The example shows you how to store objects into a INamedCache; read from the cache; query the cache; remove an item from the cache; and clear the cache. The example uses buttons that raise events when clicked; a couple of TextBox components for editing objects, and a DataGridView for displaying the current contents of a INamedCache. The example demonstrates just a ContactInfo user type, but a similar approach can be used with any other user defined type.

To add controls in your application follow these steps:

  1. Go to View->Toolbox.

  2. In the Toolbox window choose the controls you want to use and drag them on the Windows form.

  3. For each control, right-click it, choose Properties tab, and set the necessary properties.

Figure 24-5 illustrates what the Contact Cache Info application UI should look after you have finished the previous steps.

Figure 24-5 Contact Cache Client UI

Contact Cache Client UI

24.6 Implement the Application

The first step in the implementation of the example Windows application is to create a ContactInfo class that implements the IPortableObject interface.

Example 24-5 Sample Class that Implements 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
}

Before the application can start handling events, bind the DataGridView control with a data source object:

  1. In the Toolbox window choose the BindingSource object and drag it onto the form.

  2. Set its properties. Enter contactsBindingSource into the Name field and then set its data source by clicking the arrow button on the right end of the DataSource field. In the drop down window choose Add Project Data Source... and the Data Source Configuration Wizard displays. Chose Object and find the ContactInfo class in your project.

    Figure 24-6 Using Data Source Wizard to Bind a Control to a Data Source

    Data Source Configuration Window
  3. The final step is to bind the DataGridView control to the contactBindingSource. This is done by simply choosing the contactsBindingSource in the drop down window in the DataSource field of the DataGridView properties window. This is illustrated in Figure 24-7.

    Figure 24-7 Choosing a Data Source to Bind to the Control

    Editing the DataSource Property

Now, contactsBindingSource is bound to our DataGridView control and all further interaction with the data, including navigating, sorting, filtering, and updating, is accomplished with calls to the BindingSource component. IFilter and CacheEventFilter fields are required to manage filtering and a WindowsFormsCacheListener field used to ensure that any event handling code that must run as a response to a cache event is executed on the UI thread. For this to work, delegate methods for each cache event that is being handled and then register a listener with the cache by using the AddCacheListener() method. This is explained in more details in "Responding to Cache Events". In the constructor, obtain the INamedCache by using the CacheFactory.GetCache() static method and initialize the ComboBox used for choosing the search attribute.

Example 24-6 Adding Listeners

/// <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;
}

As with any other Windows application, most of the remaining implementation has to do with event handling. Since each component in the Windows form can raise an event, event handlers must be created to handle each event. Event handlers in Visual Studio can be added to your application by following these steps:

  1. Right-click the Window component for which you'd like to implement an event handler and choose Properties.

  2. In the upper toolbar of the Properties window, select the lighting button and all events that the component can raise are displayed.

    Figure 24-8 Properties Window

    The Properties Window
  3. Choose the event you want to handle and double-click it. Visual Studio adds the necessary code to your application to enable you to handle the event. Next, you must implement the empty event handler method.

Example 24-7 illustrates the event code in the sample Windows application:

Example 24-7 Adding Events

/// <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 are
/// 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 = "";
}

In addition, cache event handlers must be written as delegated in the constructor. Example 24-8 illustrates cache event handlers:

Example 24-8 Adding Cache Event Handlers

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

Example 24-9 illustrates helper methods used by the event handlers in the previous example:

Example 24-9 Adding Helper Methods for Event Handlers

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