APIExample1 : Creating a topic map with topics and associations

Running The Example

This section contains a little example of how to create a topic map including two topics and an association. This example also prints the content of the topic map. This example can be found in the directory examples/CS/APIExample1 or examples/VB/APIExample1.

Example 3.1. Creating a Topic Map

      [1] // Initialise the TMCore connection using applicaton configuration settings
      [2] ITopicMapSystem tmSystem = TopicMapSystemFactory.GetTopicMapSystem();
      [4] // Create a new topic map to work in
      [5] ITopicMap tm = tmSystem.CreateTopicMap("http://www.example.com/topicmaps/MyFirstTopicMap");

In line 2 the static method TopicMapSystemFactory.GetTopicMapSystem() is invoked to create and return a new ITopicMapSystem instance. The no-arguments version of GetTopicMapSystem() uses the application's configuration file (APIExample1.exe.config) to provide the necessary configuration information. If you are running this application in the Visual Studio.NET IDE, the file used is the file App.config. An example App.config is shown below. For a more in-depth look on whats happening here, please refer to the section called “ITopicMapSystem and TopicMapSystemFactory”. In line 4 a topic map with the name "http://www.example.com/topicmaps/MyFirstTopicMap" is created.

Example 3.2. Topic Map Application Configuration File

      [1] <configuration>
      [2]   <appSettings>
      [3]     <add key="networkedplanet.tmcore.dbconnect"
      [4]          value="Data Source=localhost;Integrated Security=SSPI; Initial Catalog=topicmap" />
      [5]   </appSettings>
      [6] </configuration>

A topic map's name is a unique identifier for the topic map within the TMCore system. Because of this, if you ran an application with the above example code in it twice, you would get a TopicMapExistsException thrown on the second occasion, informing you that a topic map with the specified name already exists. Instead, you should write code that checks that you are not about to create a topic map with a name that is already in use and take the appropriate action if there is. So rather than use the simple code shown in Example 3.1, “Creating a Topic Map”, something like the following code should be invoked:

Example 3.3. Safely Creating a Topic Map

      [1] private static ITopicMap SafeCreateTM(ITopicMapSystem tms, string name) 
      [2] {
      [3]   ITopicMap tm = tms.GetTopicMap(name);
      [4]   if (tm != null) 
      [5]   {
      [6]     tm.Remove();
      [7]   }
      [8]   return tms.CreateTopicMap(name);
      [9] }

On line 3, an attempt is made to retrieve a topic map with the base locator of the topic map we want to create. If a topic map is found, we cannot create the new topic map directly. In this example, the solution is simply to remove the topic map from the system. This is done using the call to ITopicMap.Remove() on line 6. The Remove() method removes the topic map and all topics and associations that it contains from the system. Alternatively you may choose to retry using some algorithm for generating a new name string that does not already exist or perhaps even prompt the application user for a new topic map name. Finally on line 8 the topic map is created. In this case, because any pre-existing topic map with the same name has been removed, the call to the CreateTopicMap() method will succeed.

Example 3.4. Creating and Populating Topics

      [1] // Create topics
      [2] ITopic t1 = tm.CreateTopic();
      [3] t1.CreateTopicName("hello");
      [4] t1.Save();
      [6] ITopic t2 = tm.CreateTopic();
      [7] t2.CreateTopicName("world");
      [8] t2.Save();

Creating a new topic in a topic map is as easy as calling the CreateTopic() method on the ITopicMap instance. The result of this method is a new ITopic instance. The ITopic interface provides methods for creating new topic names and occurrences. This is the pattern used throughout the TMCore API - to create a new object, you call the appropriate Create...() method on the parent object. Some Create...() methods are overloaded with different parameter lists to allow you to specify the content of the new object, so in this example the call to CreateTopicName() on lines 3 and 7 take a single string parameter which is the string value of the newly created ITopicName instance.

When an object is modified, the changes are not committed to the topic map until the Save() method is called. The Save() method is provided on the ITopic and the IAssociation interfaces only. To save a change to a topic name or occurrence you must call the Save() method on the containing ITopic. To save a change to an IAssociationRole instance you must call the Save() method on the containing IAssociation instance. Calling the Save() method saves all of the changes made to the ITopic or IAssociation instance and the saving takes place in a single database transaction - either all updates are saved or, in the case of an error, none of the changes are saved.

In addition to Save() methods on ITopic and IAssociation, the ITopicMap interface also provides a Save() method. Invoking this ITopicMap.Save() on a topic map will result in all changes to contained ITopic and IAssociation instances being committed in a single transaction.

Example 3.5. Creating and Populating Associations

      [1] // Create an association between the topics
      [2] IAssociation a = tm.CreateAssociation();
      [3] a.CreateAssociationRole(t1, null);
      [4] a.CreateAssociationRole(t2, null);
      [5] a.Save();

Creating an association follows the same pattern as creating a topic. The IAssociation instance is created using the CreateAssociation() method of the ITopicMap interface (line [2]). IAssociationRole instances specify the topics that participate in the association and are created using the CreateAssociationRole() method of the IAssociation interface. This method takes two parameters, the first is the ITopic that is participating in the association and the second is an ITopic instance that specifies the type of role played by the first topic in the association. Either of these parameters may be null. As with the ITopic interface, changes made are not committed to the database until the ITopic.Save() method is called.

Example 3.6. Looping over Topics and Associations

// Print out the names of each topic in the topic map
foreach(ITopic t in tm.Topics) 
{
  PrintTopicNames(t);
}

// Print out the role players of each association in the topic map
foreach(IAssociation assoc in tm.Associations) 
{
  PrintAssociation(assoc);
}

The ITopicMap interface provides read-only properties Topics and Associations. These return an IList of the topics or associations in the topic map. Although these properties make it extremely easy to iterate over the contents of a topic map in a simple application like this, with large topic maps any simple listing of all topics or all associations will quickly become difficult to display and potentially quite slow as you trawl through the whole database! However, for our simple Hello World application, these properties are extremely convenient.

Example 3.7. Accessing Topic Data

private static void PrintTopicNames(ITopic t) 
{
  System.Console.Write("Topic: ");
  foreach(ITopicName tn in t.TopicNames) 
  {
    System.Console.Write("{0} ", tn.Value);
  }
  System.Console.WriteLine();
}

Accessing data is as simple as using the properties defined by each interface. In the example code above, the names of a topic are returned by the ITopic.TopicNames property which is a read-only IList of ITopicName instances. The string value of a topic name is accessed using the ITopicName.Value property which is a read-write property.

The rule-of-thumb for whether a property will be read-only or read-write is that if the property gives access to the parent or children of an object, it will be read-only (you must use the Create...() and Remove() methods to manage parent-child relationships). For other properties, access is read-write. So if we wanted to update a topic name's string value it would be as simple as :

tn.Value = "My New Name";