Tamino API for .NET Version 8.2.2
 —  Tamino API for .NET  —

Working with Datasets and the TaminoDataAdapter

The TaminoDataAdapter class enables you to use .NET’s DataSet object together with Tamino XML data. The DataSet object is a table/column-oriented object which is suited for disconnected working and can easily be bound to Windows Forms and Web Forms controls. For more information about .NET datasets, see Microsoft's .NET Framework documentation.

However, a table/column-oriented object is not appropriate for complex XML data. For processing a list of complex XML data, and in order to get the full benefit of Tamino’s native XML support, your application should use the TaminoCommand and TaminoItemIterator/TaminoPageIterator objects instead (see 'Using Tamino Cursors' and 'Iterating through Query Results').

For processing a list of simple XML data or a list of simple subtrees of complex XML documents, the TaminoDataAdapter provides a convenient option to the TaminoItemIterator/TaminoPageIterator. It allows the application to fill a dataset with data from Tamino, modify the dataset and write the changes back to Tamino just by calling the Update method of the TaminoDataAdapter object.

Since Tamino is a native XML database, the behavior of the TaminoDataAdapter differs slightly from the corresponding behavior of the SQL-oriented data adapters of the .NET Framework. The behavior of the individual functions is described below in detail in the following sections:


Creating and Using TaminoDataAdapter Objects

A TaminoDataAdapter object can be used to:

It is not mandatory to define tables and columns in a dataset before populating it with Tamino data. If no tables and columns are defined, the schema will be inferred from the data when the dataset is populated. One disadvantage of this is that the process of inferring a schema from XML is not deterministic; therefore the result (i.e., the schema) relies heavily on the specific data from which the schema is inferred. For more information about inference limitations, see Microsoft's .NET documentation (ms-help://MS.NETFrameworkSDK/cpguidenf/html/cpconinferringdatasetrelationalstructurefromxml.htm).

The same TaminoDataAdapter object can be used to fill and update multiple datasets. A TaminoDataAdapter object is always tied to a specific TaminoConnection object and can only handle data belonging to one Tamino collection.

Updates must be done by the same TaminoDataAdapter object that populated the dataset. This is because the table/column-oriented DataSet object does not hold all information about native XML data, and therefore the TaminoDataAdapter object has to keep additional information for subsequent updates.

Creating TaminoDataAdapter Objects

Collection and connection properties must be specified when creating a TaminoDataAdapter object.

An application that wants to leave the handling of connections and transactions to the TaminoDataAdapter object should just specify the database URL and the collection name as strings. The TaminoDataAdapter object will then create and use its own TaminoConnection/TaminoCommand object internally and handle the opening and closing of connections as well as transactions without any intervention from the application.

An application that prefers to customize transactional settings during command execution, or control connections and transactions on its own, can pass TaminoConnection or TaminoCommand objects in the constructor. The TaminoDataAdapter will then use the passed objects for command execution.

Examples

...
TaminoDataAdapter adapter =
      new TaminoDataAdapter("http://myserver/tamino/mydb", "mycollection");
...
...
TaminoConnection connection = new TaminoConnection("http://myserver/tamino/mydb");
TaminoDataAdapter adapter =
      new TaminoDataAdapter(connection, "mycollection");
...
...
TaminoConnection connection = new TaminoConnection("http://myserver/tamino/mydb");
TaminoCommand command = connection.CreateCommand("mycollection");
TaminoDataAdapter adapter = new TaminoDataAdapter(command);
...

Disconnected Working

Datasets are designed for disconnected (offline) working. To support this, the FillSchema, Fill and Update methods of the TaminoDataAdapter class handle opening and closing of the associated connection objects according to the following rules:

Similar rules apply to the transaction handling. However, the rules for transaction handling also depend on whether Fill or Update is executed and on whether filling is done for read-only or update mode.

TaminoDataAdapter Behavior

The same methods of the TaminoDataAdapter object can be used to fill a dataset for update or readonly mode. However, filling for readonly is faster, uses fewer resources and poses fewer limitations on the data with which the dataset can be filled. You should therefore decide whether your application needs to modify the data or not before the dataset is filled and set the Behavior property of the TaminoDataAdapter object accordingly.

A DataSet object that has been filled for readonly can subsequently be filled for update. However, once a DataSet object has been filled for update, it cannot be filled for readonly. You should therefore fill a DataSet object either always for readonly or always for update.

Examples

...
DataSet ds = new DataSet();
adapter.Behavior = TaminoAdapterBehavior.ReadOnly;
adapter.Fill(ds);
...

TaminoDataAdapter and Synchronized XmlDataDocuments

.NET supports the synchronization of an XmlDataDocument object with a corresponding DataSet object. The same DataSet object may only be synchronized with exactly one XmlDataDocument object. The TaminoDataAdapter class uses synchronized XmlDataDocuments when filling datasets for later updates. An application can use the TaminoDataAdapter object's GetTaminoResponse method to access the specific XmlDataDocument that is synchronized with the dataset. An application must not synchronize another XmlDataDocument object with a DataSet object that has been filled or should be filled for update by a TaminoDataAdapter object.

Examples

...
DataSet ds = new DataSet();
adapter.Behavior = TaminoAdapterBehavior.UpdateByItemMapping;
adapter.Fill(ds);
XmlDataDocument xdoc = adapter.GetTaminoResponse(ds);
...

Releasing Resources

Especially when working with data in update mode, an application should call the TaminoDataAdapter object's Dispose method when it has finished using the dataset together with the TaminoDataAdapter object.

Examples

...
DataSet ds = new DataSet();
...
adapter.Dispose(ds); // release resources for this DataSet
...
...
adapter.Dispose();  // release resources for all DataSets handled
                    // by the adapter
...

Top of page

Defining Dataset Tables and Columns

An application can use the FillSchema methods to define tables and columns in the dataset. Depending on the data with which the dataset should be populated, the application can use different parameters to specify the dataset schema. To populate the dataset with complete root elements of Tamino documents, the application can specify the name of the corresponding schema in Tamino. To populate the dataset with subtrees of XML documents, the application can specify an XmlReader object to read the corresponding schema.

Examples

...
XmlTextReader reader = new XmlTextReader("c:\\tests\\myschema.xsd");
adapter.FillSchema(ds, reader);
...
...
adapter.FillSchema(ds, "Property"); // schema name within Tamino
...

For a complete code sample, see the FillSchema example in the DataSetSamples.

Top of page

XmlSchema Validation for Inserted XML Elements

There is an overloaded method of the FillSchema method which takes an additional parameter. This method is important if you plan to do updates and these updates imply the addition of new XML elements. If you modify the XmlDataDocument directly using the DOM API, insertion should work fine and you should not use this overloaded method.

If you work with the DataSet object directly, the DataSet object will not maintain the correct sequence of inserted child elements in relation to the parent. Please note that not only inserting new rows, but also modifying a row can imply the insertion of a new XML element. This is the case if the corresponding column has not previously contained a value. By default, the dataset will insert a new child at any position within the parent XML element. This position may not be the same as the position defined in the corresponding Tamino document type, and a subsequent call to the Update method would fail.

The application can use the overloaded FillSchema method to tell the TaminoDataAdapter object to use the specified schema for validation when new elements are inserted. The TaminoDataAdapter will then try to correct the element sequence according to the schema.

Example

...
DataSet ds = new DataSet(); 
adapter.Behavior = TaminoAdapterBehavior.UpdateByItemMapping; 
adapter.FillSchema(ds, "Property", true);
...

Correction capabilities are limited in the current version of the Tamino API for .NET. The parent element in which the sequence of the child elements should be corrected must be a global XML element in the sense of the XML Schema recommendation (see http://www.w3.org/TR/xmlschema-0/). The TaminoDataAdapter cannot correct the element ordering for inserts resulting from calls to the DataSet.RejectChanges() method. This means: If you delete complete rows and call RejectChanges(), then the correct ordering is not guaranteed. You can avoid this problem by using TaminoDataAdapter.Fill instead of DataSet.RejectChanges() to undo changes in the dataset.

The status of the correction implementation is "experimental", meaning that the TaminoDataAdapter may not be able to correct the sequence if the schema is very complex.

Top of page

Populating a Dataset for Read-Only

The Fill methods can be used to populate a dataset with Tamino data. To populate a dataset for read-only, the Behavior property of the TaminoDataAdapter must be set to "TaminoAdapterBehavior.ReadOnly" before calling the Fill methods.

The XQuery that is used to populate the dataset can either be set via the Query property of the TaminoDataAdapter class, or it can be passed as a parameter to the Fill method. The Query property contains the TaminoQuery object that was used for the most recently called Fill method.

Example

...
DataSet ds = new DataSet();
adapter.Behavior = TaminoAdapterBehavior.ReadOnly;
adapter.Query    = new TaminoQuery("input()/Property/Address");
adapter.Fill(ds);
...
adapter.Fill(ds); // (re)filling several times using the same query
...
...
DataSet ds = new DataSet();
adapter.Behavior = TaminoAdapterBehavior.ReadOnly;
adapter.Fill (ds, new TaminoQuery("input()/Property/Address"));
...

See also the FillReadOnly example in the DataSetSamples.

Loaded Data and Tables

Note:
When the Fill method is called, all rows of all tables in the dataset are cleared.

If the dataset contains tables before the Fill method is called, then XmlReadMode.IgnoreSchema is used for filling. If the dataset does not contain any tables, then XmlReadMode.InferSchema is used for filling.

Connection and Transaction Handling

If the TaminoConnection object associated with the TaminoDataAdapter is not open, the connection is opened in "TaminoConnectionMode.AutoCommit" and it is closed before the Fill method returns.

Top of page

Populating a Dataset for Update

The Fill methods can be used to populate a dataset with Tamino data. To populate a dataset for update, the Behavior property of the TaminoDataAdapter must be set to "TaminoAdapterBehavior.UpdateByItemMapping" before calling the Fill methods. If "TaminoAdapterBehavior.UpdateByItemMapping" has not been set before filling, the TaminoDataAdapter object will not create nor keep the necessary information for subsequently updating the data in Tamino. Calling the Update method later with the dataset will throw a TaminoException.

The TaminoQuery object that is used to populate a dataset is created using the BuildXQuery method in the TaminoXQueryBuilder class. This extends the passed XQuery expression to enrich the returned Tamino data with identifying information for subsequent updates.

Example

...
DataSet ds = new DataSet();
adapter.Behavior = TaminoAdapterBehavior.UpdateByItemMapping;
TaminoQuery query =
  TaminoXQueryBuilder.BuildXQuery(null, "input()/Property/Address",
                                  TaminoAdapterBehavior.UpdateByItemMapping);
adapter.Fill(ds, query);
...

See also the XmlUpdate and RowUpdate examples in the DataSetSamples.

Loaded Data and Tables

Note:
When the Fill method is called, all rows of all tables in the dataset are cleared.

Similarly as when filling for readonly, XmlReadMode.IgnoreSchema is used for filling if the dataset already contains tables and XmlReadMode.InferSchema is used if it does not. An additional restriction applies to filling for update: the schema of the dataset must reflect the hierarchical structure of the complete XML subtree that is returned from Tamino. Let us assume that the query returns Sample subtrees like the following:

<Sample>
  <Description>filling for update sample</Description>
  <Person>
     <Phone>999999999</Phone>
     <Name>
        <Fírst>FirstName</First>
        <Last>LastName</Last>
     </Name>
     <Email>myemail@mycompany.com</Email>
  </Person>
</Sample>

The Fill method will, for instance, throw a TaminoException if the dataset does not contain a corresponding table for the Person element and therefore does not maintain the following nested relations: Sample_Person, Person_Name. You usually can avoid such problems by applying an appropriate schema to the dataset.

There is one special case that can occur even with schemas: If the root element of your single query items does not contain any attributes and only contains children which themselves have children, like <AttributeLessRoot> below, .NET will not create a table for <AttributeLessRoot>; instead it will apply the name "AttributeLessRoot" to the whole dataset.

<AttributeLessRoot>
   <ChildWithChildren>
      <Child1>Child1</Child1>
      <Child2>Child2</Child2>
   </ChildWithChildren>
</AttributeLessRoot>

The Tamino API for .NET will throw a TaminoException when the dataset is filled for update. You can easily avoid this problem: note that the <AttributeLessRoot> itself does not contain any useful information that you might want to modify from within your clients. Instead of querying for <AttributeLessRoot> elements, query for <ChildWithChildren>.

Connection and Transaction Handling

Filling a dataset for update requires the connection to be opened in "TaminoConnectionMode.LocalTransaction". If the connection is already open in "TaminoConnectionMode.AutoCommit", an exception will be thrown. The query is always executed with lock mode "TaminoLockMode.Shared".

Top of page

Modifying Data in a Dataset

After a dataset has been filled for updates, the application will in general want to modify data. The following describes the most common ways of updating the data in the dataset.

Binding the Dataset to a Control

Datasets can easily be bound to Windows Forms or Web Forms controls, for instance the DataGrid control. The control displays the data which is bound to it, and the data can then be modified interactively through the control. See the RowUpdate example of the DataSetSamples for a running example.

Merging Datasets

Another useful feature of datasets is that they can be used together with Web services. A Web service scenario with the TaminoDataAdapter can be implemented as follows: A client application might request a filled dataset from a Web service, make some modifications to the dataset, and send a dataset that just includes the changes back to the Web service. The Web service might then merge the received changes into the original dataset and write the changes back to Tamino.

graphics/datadapt.gif

Modifying the Associated XML document

After a dataset has been filled for update by the TaminoDataAdapter, an XmlDataDocument is synchronized with the DataSet object. Synchronization means that changes made to either of the two objects are reflected in the other object. This allows an application to modify the data in the dataset by modifying the XmlDataDocument using the XML DOM interface. An application can access the synchronized XmlDataDocument through the TaminoDataAdapter class's GetTaminoResponse method. See also the XmlUpdate example of the DataSetSamples.

Handling Sequence Problems of Inserted XmlElements

If you do not make the modifications by modifying the associated XML document, you might run into problems with the sequence of inserted child elements. To overcome those problems, you can switch on the validation capability of the TaminoDataAdapter object. See "XmlSchema Validation for inserted XML Elements".

Example

...
XmlDataDocument xdoc = adapter.GetTaminoResponse(ds);
ds.EnforceConstraints = false;
...
// do modifications via DOM interface
...
ds.EnforceConstraints = true:
...

Top of page

Writing Dataset Modifications Back to Tamino

The TaminoDataAdapter class's Update method allows modifications made to the dataset to be written back to the Tamino database. When the Update method is called, the TaminoDataAdapter looks for inserted, updated and deleted items in the dataset. If no modifications are found, the method returns immediately. If any modifications are found, it creates corresponding XQuery update expressions for Tamino and executes the updates. An application should not rely on individual items being updated in a defined sequence.

After successfully updating the data in Tamino, the TaminoDataAdapter calls the AcceptChanges() method in the DataSet object.

Note:
The Update() method does not implicitly refill the dataset. If you have used the Update() method to update your modified items in the database and you want to modify the same items again, you must call the Fill method before making the modifications. Otherwise you will get an error message during the next update, stating that the items have been modified in the meanwhile.

Disconnected Working and Optimistic Concurrence

Datasets are designed to support disconnected (offline) working, in the sense that reading the data (for filling the dataset) and subsequent updating are not usually done within the same connection and/or transaction. One impact of this is that the data may be changed by another application between the Fill and the Update. To avoid overwriting changes that have already been made by other applications, the TaminoDataAdapter refuses to update a Tamino document if it has been changed in Tamino since the Fill. Notice that even if the dataset has been filled with subtrees of Tamino documents, the TaminoDataAdapter always checks for any modifications of the whole documents.

Updating Root Elements of Tamino Documents

If the dataset rows correspond to complete root elements of Tamino documents and their contents, an application can simply call the Update method taking just the DataSet object as parameter. The unit of update is always the complete XML tree starting with the root element.

Example

int count = adapter.Update(ds);

Updating XML Subtrees of Tamino Documents

The unit of update is always the complete subtree as returned from Tamino when the dataset was filled. Within those subtrees all modifications are allowed: inserting, deleting and updating XML subtrees. However, adding a complete new subtree to the query result is not supported because the TaminoDataAdapter would not know into which Tamino document and at which point within the document the new subtree should be inserted.

In contrast to root elements, the application must specify a TaminoQueryItemMapping object for each different kind of subtree in the dataset. These TaminoQueryItemMapping objects must provide information specifying how to identify uniquely a single subtree within its parent Tamino XML document. TaminoQueryItemMappings are very similar to unique constraints in XML schemas (see also: http://www.w3.org/TR/xmlschema-0/ - specifyingUniqueness). An XPath selector is specified to select a set of elements within its including XML document. “Fields” are then specified that must be unique within the scope of the set of selected elements. Notice that the scope of this uniqueness is only a single document and not a document set.

The following example shows how to specify TaminoQueryItemMappings.

Assume that the dataset has been filled with <Person> subtrees like the following:

<Person>
   <Phone>999999999</Phone>
   <Name>
      <First>FirstName</First>
      <Last>LastName</Last>
   </Name>
   <Email>myemail@mycompany.com</Email>
</Person>

According to .NET's rules for mapping XML to dataset tables and columns, the dataset will contain the following tables:

Person:

Phone Email

Name:

First Last

Further assume that the combination of first name and last name uniquely identifies a single Person subtree within a TaminoDocument. Remember that the scope of this uniqueness is only the document and not the document set.

The application would then specify the following TaminoQueryItemMapping for <Person> subtrees:

TaminoQueryItemMapping[] mapping = new TaminoQueryItemMapping[1];
TaminoFieldMapping[]     fields  = new TaminoFieldMapping[2];

mapping[0] =
    new TaminoQueryItemMapping
    (
        "Person",    // table name
        ".//Person", // XPath to select Person element sets
                     // within their including Tamino document
        fields       // fields defining uniqueness of Person
                     // elements within the element set.
    );

fields[0] = new TaminoFieldMapping("Name", "First");// table name, column name
fields[1] = new TaminoFieldMapping("Name", "Last"); // table name, column name

int count = adapter.Update(ds, mapping);

Connection and Transaction Handling

The Update method requires the connection to be opened in "TaminoConnectionMode.LocalTransaction". If the connection is already open in "TaminoConnectionMode.AutoCommit", an exception is thrown.

If a transaction is not already active when the Update method is called, the TaminoDataAdapter object starts a new transaction and either does a commit or a rollback before the Update method returns. For details of the error handling rules, see “Customizing the Update Behavior”.

Top of page

Customizing the Update Behavior

An application can customize the Update behavior of the TaminoDataAdapter object by setting the following properties:

ContinueUpdateOnError

In most cases, the Update method performs multiple update actions in Tamino, which are all executed within the scope of a single transaction. In some cases, the application might want to commit the transaction even though some of the actions have failed. For example, it might be impossible to update one of the documents because another application has changed it in the meanwhile; nevertheless, all other changes should be written back to the Tamino. The behavior is as follows:

Value of ContinueUpdateOnError Description
False

This is the default setting. At the first thrown exception, the processing of update actions is stopped and the Update method throws an exception.

If a transaction had already been started before the Update method was called, no Rollback is done.

If the transaction was started by the TaminoDataAdapter, a Rollback is done before the exception is thrown.

True

The TaminoDataAdapter continues to process update actions. Instead of throwing an exception, it puts the text of a caught exception into the RowError information of the row, the item which caused the error is mapped to. This allows the application to handle these errors after the Update method returns.

There is one exception to this rule: If SingleItemUpdate (see below) is set to false, the very last step of the update processing is the execution of an update command which contains all collected update actions. If an error occurs in this very last step, an exception is always thrown and the transaction is rolled back.

SingleItemUpdates

The behavior is as follows:

Value of SingleItemUpdates Description
True This is the default setting. For each item to be updated, the TaminoDataAdapter issues a single update command to Tamino. This setting is useful if the dataset contains only few and/or very large updated items, and/or the application would like to have detailed control over individual command execution.
False The TaminoDataAdapter minimizes the number of round-trips to the Tamino Server. As far as possible, it combines individual update actions into one big update command. This setting is useful if the dataset contains many small updated items.

Top of page

Working with TaminoDataAdapter Events

The TaminoDataAdapter exposes two events to which an application can respond in order to gain detailed control over the individual update actions that occur when processing the Update method. The TaminoUpdating and TaminoUpdated events are similar to .NET’s RowUpdating and RowUpdated events, see ms-help://MS.NETFrameworkSDK/cpguidenf/html/cpconaddingremovingadonetproviderevents.htm.

Event Description
TaminoUpdating This event signifies that an update operation for a changed item in the dataset is about to begin. The application can respond to this event to do validation checks or modify the item before it is updated, to customize the command that will be used for the update, to skip the updating of single items, to cancel the update, and so on.
TaminoUpdated This event signifies that an update operation for a changed item in the dataset has been completed. The application can respond to this event by handling errors that occurred during the update, by deciding whether update processing should continue with the next item or whether it should be cancelled, and so on.

For a working example of using TaminoDataAdapter events, see the TaminoUpdatingEvent example in the DataSetSamples.

TaminoUpdatingEventArgs and TaminoUpdatedEventArgs

When a TaminoUpdating event is raised, a TaminoUpdatingEventArgs object is passed to the event handlers. When a TaminoUpdated event is raised, a TaminoUpdatedEventArgs object is passed accordingly.

The TaminoUpdatingEventArgs object provides the following properties:

Property Description
StatementType The type of the update operation: "INSERT", "UPDATE", "DELETE".
Command The TaminoCommand object to perform the update operation. The application can, for instance, change transactional properties.
QueryExpression The XQuery update expression for the update operation. For an "INSERT" operation this is always null. The application can modify this expression.
Namespaces A collection of namespace declarations for the XQuery update expression. For an "INSERT" operation this is always null. The application can modify the namespace declaration.
Node The item as an XML element. The application can use this property to modify the item.
Row The row to which the XML element is mapped within the dataset. The application can use this property to modify the item.
Status Indicates whether an error has occurred. When the event is raised, the value is either "Continue" or "ErrorsOccurred". The application can set the Status to a different value.
Errors The caught exception if status is "ErrorsOccurred". The application can set its own exception.

The TaminoUpdatedEventArgs object provides the following properties:

Property Description
StatementType The type of the update operation: "INSERT", "UPDATE", "DELETE". If SingleUpdateItem is set to false, this property is always set to "UPDATE".
Command The TaminoCommand object that was used to perform the update operation. An application can, for instance, reset transactional properties that it had changed during the TaminoUpdating event.
TaminoResponse The Tamino response object returned from the update operation.
Node The item as an XML element. If SingleUpdateItem is set to false, this property is always null.
Row The row to which the XML element is mapped within the dataset. If SingleUpdateItem is set to false, this property is always null.
Status Indicates whether an error has occurred. When the event is raised, the value is either "Continue" or "ErrorsOccurred". The application can set the Status to a different value.
Errors The caught exception if status is "ErrorsOccurred". The application can set its own exception.

Event Raising and Execution Order

The time when the events are raised depends on whether SingleItemUpdates is set to true or false.

If SingleItemUpdate is "true", for each modified item the behavior is as follows:

  1. Create update expression.

  2. Check whether the Tamino document that contains this item has been modified since the dataset was filled.

  3. Raise TaminoUpdating event.

  4. Check Status property returned from the event handler and continue or abort update processing.

  5. Issue update command to Tamino.

  6. Raise TaminoUpdated event.

  7. Check Status property returned from the event handler and continue with the next item or abort update processing.

If SingleItemUpdate is "false", the behavior is as follows:

  1. For each modified item:

    1. Create update expression.

    2. Raise TaminoUpdating event.

  2. For each item that requires an INSERT operation:

    1. Issue command to Tamino.

    2. Raise TaminoUpdated event.

    3. Check Status property returned from the event handler and continue or abort update processing.

  3. For all affected Tamino documents: Check whether they have been changed since the dataset was filled.

  4. Create an update command that combines all update actions for the remaining items.

  5. Issue the update command to Tamino.

  6. Raise TaminoUpdated event.

Top of page

Limitations of the Dataset Support

Since Tamino is a native XML database and the dataset is a table/column-oriented object, the support provided for datasets by the TaminoDataAdapter is limited:

Top of page