Executing Queries

Other Options For Working With Results Sets

The interface ITopicMapSystem provides a single method ITopicMapSystem.ExecuteQuery(string) through which an SQL query can be executed against the TMCore system. The method returns an instance of the ITMCoreDataReader interface which gives read-only access to the results of the query.

The ITMCoreDataReader interface extends the standard System.Data.IDataReader interface with the following additional methods:

ITopic GetTopic(int  i);
ITopicName GetTopicName(int  i);
IOccurrence GetOccurrence(int  i);
IVariant GetVariant(int  i);
IAssociation GetAssociation(int  i);
IAssociationRole GetAssociationRole(int  i);
ITopicMapObject GetTopicMapObject(int  i);

Each of the methods listed above take as their single parameter the zero-based position of the column from which the object's ObjectID value is to be retrieved. The method then loads any additional data required from the database and returns an instance of the appropriate topic map object interface. The method GetTopicMapObject() will determine the type of the object and return an instance of the appropriate dervied interface (e.g. an IOccurrence instance if the identifier is from an occurrence object). If the content of the specified column is not the object identifier of the requested type of topic map object, then an InvalidCastException will be raised.

Note

The GetTopicMapObject() method uses additional database queries to determine the type of the object and so its use should be avoided unless the specified column can contain identifiers of a mixture of types of object.

As with other IDataReaders, the ITMCoreDataReader must be closed once it is no longer needed by calling the Close() method. The TMCore code will create a new connection for each concurrent ITMCoreDataReader instance that your program holds open and unless these readers are closed when not needed, your application will quickly use up all the available connections leading to unexpected failures.

The code listing below shows an example of using the ITopicMapSystem.ExecuteQuery() method to retrieve the topic names which contain the string "foo" and have a particular topic in their scope. Note that the call to the ITMCoreDataReader.Close() method is enclosed in the finally block to ensure that it gets called even if the processing of a topic name results in an exception being raised.

Example 9.1. Using ITMCoreDataReader

ITopic scopingTopic;
ITopicMap tm;

// ... set up scopingTopic and tm

string query = "select v.name_id from " +
  "TM_nameValue join TM_nameScope on TM_nameValue.name_id=TM_nameScope.name_id" +
  "where TM_nameValue.name_value like '%foo%' and TM_nameScope.scoping_topic_id=" + scopingTopic.ObjectID;

ITMCoreDataReader dr = tm.TopicMapSystem.ExecuteQuery(query);
try {
  while (dr.Read()) {
    ITopicName tn = dr.GetTopicName(0);
    // Do something with the scoped name
  }
} finally {
  dr.Close();
}

The TopicMapSystem interface also supports executing queries with a set of parameters. This is STRONGLY RECOMMENDED when one or more parameters come from user input as SQL parameter values are properly escaped and checked for SQL injection attacks. There are two overrides of the ExecuteQuery method that support parameters. The method ExecuteQuery(String queryString, IList parameters) takes a list of System.Data.IDataParameter instances (e.g. System.Data.SqlClient.SqlParameter), this method gives you the most control over the parameters you pass at the expense of requiring you to instantiate each parameter object explicitly. The second override is ExecuteQuery(String queryString, Hashtable parameters), which takes a Hashtable of parameter values where the Hashtable key is the parameter name and the value is the parameter value. The parameter type is automatically inferred from the parameter value.

The code samples below show how these two overrides can be used.

Example 9.2. Using ExecuteQuery(string, IList)

string query = "select v.name_id " +
  "from tm_nameValue join tm_nameScope on tm_nameValue.name_id=tm_nameScope.name_id "+
  "where tm_nameValue.name_value like @name and tm_nameScope.scoping_topic_id=@scopingTopic";

ArrayList parameters = new ArrayList();
IDataParameter param = new SqlParameter();
param.Name = "@name";
param.DbType = DbType.String;
param.Value = "%foo%";
parameters.add(param);
param = new SqlParameter();
param.Name = "@scopingTopic";
param.DbType = DbType.Int32;
param.Value = scopingTopic.ID;
parameters.add(param);
ITMCoreDataReader dr = tm.TopicMapSystem.ExecuteQuery(query, parameters);

Example 9.3. Using ExecuteQuery(string, Hashtable)

string query = "select v.name_id " +
  "from tm_nameValue join tm_nameScope on tm_nameValue.name_id=tm_nameScope.name_id "+
  "where tm_nameValue.name_value like @name and tm_nameScope.scoping_topic_id=@scopingTopic";

Hashtable parameters = new Hashtable();
parameters["@name"] = "%foo%";
parameters["@scopingTopic"] = scopingTopic.ID;
ITMCoreDataReader dr = tm.TopicMapSystem.ExecuteQuery(query, parameters);