Microsoft.NET

……………………………………………….Expertise in .NET Technologies

ADO.NET Core Classes:XML and the DataSet – Part 15

Posted by Ravi Varma Thumati on December 4, 2009

Microsoft and other technology vendors have introduced a variety of different options for converting relational data into XML. Unfortunately, no single approach so far has combined ease-of-use with the flexibility developers require. For example, later versions of ADO introduced XML as a format for persisting a stream to disk. However, the developer had little control over the structure of the XML document. Around the same time, SQL Server 2000 introduced the new FOR XML query syntax. This technique was more flexible, but proved difficult to manage, could complicate queries horribly, and limited solutions to a single database product.

ADO.NET improves upon this picture. With ADO.NET, you can easily convert data into XML. It’s also easy to generate a matching XSD schema, perform an XPath search on a result set, or even interact with an ordinary XML document through the ADO.NET data objects. All these features of ADO.NET’s support for XML far exceed previous Microsoft data-access technologies.

Things aren’t as straightforward if you need to use XML for cross-platform use (for example, sharing data between a .NET web service and a Java client). If you can write a Java client to accommodate the default ADO.NET XML structure, your task is easy. However, if you have an existing client that expects information in a different format, you may need to perform some tedious processing with the .NET XML classes. You’ll also need to master the DiffGram schema if you want a non-.NET client to use the DataSet to submit batch changes.

This chapter introduces all these concepts, focusing on three topics:

  • Using XML as a format to store and share DataSet information, even across process and platform boundaries.
  • Using ADO.NET as a wrapper to allow convenient editing of otherwise ordinary XML documents.
  • Synchronizing XML and the ADO.NET representation of a DataSet with the XmlDataDocument object. This also makes it easy to use XPath, XSLT, and other XML-specific technologies with an ADO.NET DataSet.

We’ll conclude with a quick look at the XML extensions available in SQL Server 2000.

DataSet XML Methods

The ADO.NET DataSet stores information internally in a proprietary binary format that’s optimized for XML representation. This means that data can be retrieved in XML format seamlessly, without any data loss or conversion errors. Table 17-1 lists the DataSet methods that work with XML.

Table 17-1. ADO.NET DataSet XML methods
Method Description
GetXml(  )
Retrieves the XML representation of the data in the DataSet as a single string.
GetXmlSchema(  )
Retrieves the XSD schema for the DataSet XML as a single string. No data is returned.
ReadXml(  )
Reads XML data from a file or a TextReader, XmlReader, or Stream object, and uses it to populate the DataSet. The XML document can include an inline schema.
ReadXmlSchema(  )
Reads an XML schema from a file or a TextReader, XmlReader, or Stream object, and uses it to configure the DataSet (for example, creating Constraint and DataColumn objects).
WriteXml(  )
Writes the contents of the DataSet to a file or a TextWriter, XmlWriter, or Stream object. You can choose to write the schema inline.
WriteXmlSchema(  )
Writes just the XSD schema describing the contents of the DataSet to a file or a TextWriter, XmlWriter, or Stream object.
InferXmlSchema(  )
Infers the XML schema and applies it to the DataSet by reading through an XML document supplied by a file or a TextReader, XmlReader, or Stream object.

The key decision you make when dealing with the XML representation of a DataSet is deciding how to handle the schema, which defines the allowed structure and data types for the XML document. If you save the schema, you can use it as a basic form of error checking. Simply reload the schema into the DataSet before you insert any data.

Example 17-1 shows a console application that writes the retrieved XML to a file, reads it back, and then displays the XML for the retrieved DataSet using the GetXml( ) method. Note that this code doesn’t provide any error handling for its file operations (i.e., the WriteXml( ) and ReadXml( ) methods).

Example 17-1. Writing a DataSet to XML with a schema
using System;
using System.Data;
using System.Data.SqlClient;

public class SaveDataSet
{
    private static string connectionString = "Data Source=localhost;" +
         "Initial Catalog=Northwind;Integrated Security=SSPI";

    public static void Main()
    {
        string SQL = "SELECT CategoryID, CategoryName, " +
                     "Description FROM Categories";

        // Create ADO.NET objects.
        SqlConnection con = new SqlConnection(connectionString);
        SqlCommand com = new SqlCommand(SQL, con);
        SqlDataAdapter adapter = new SqlDataAdapter(com);
        DataSet ds = new DataSet("Nortwind");

        // Execute the command.
        try
        {
            con.Open();
            adapter.FillSchema(ds, SchemaType.Mapped, "Categories");
            adapter.Fill(ds, "Categories");
        }
        catch (Exception err)
        {
            Console.WriteLine(err.ToString());
        }
        finally
        {
            con.Close();
        }

        // Save DataSet to disk (with schema).
        ds.WriteXmlSchema("mydata.xsd");
        ds.WriteXml("mydata.xml");

        // Reset DataSet.
        ds.Reset();

        // Read schema and reload data.
        ds.ReadXmlSchema("mydata.xsd");
        ds.ReadXml("mydata.xml");

        // Display DataSet.
        Console.WriteLine("DataSet retrieved.");
        Console.WriteLine(ds.GetXml());
    }
}

Dissecting the DataSet XML

The DataSet XML follows a predefined format that follows a few simple rules:

  • The root document element is the DataSet.DataSetName (in our example, Northwind).
  • Each row in every table is contained in a separate element, using the name of the table. In our example with one table, this means multiple Categories elements. If there are two tables, these rows are followed by a list of other elements (such as Customers elements).
  • An element for every column is included in each table row element. The actual column value is recorded as text inside the tag (although this is configurable).

Here’s the default XML (excerpted to the first two rows) created by Example 17-1 for the Categories table:

<?xml version="1.0" standalone="yes"?>
<Northwind>
  <Categories>
    <CategoryID>1</CategoryID>
    <CategoryName>Beverages</CategoryName>
    <Description>Soft drinks, coffees, teas, beers, and ales</Description>
  </Categories>
  <Categories>
    <CategoryID>2</CategoryID>
    <CategoryName>Condiments</CategoryName>
    <Description>Sweet and savory sauces, relishes, spreads, and
     seasonings</Description>
  </Categories>
  <!-- Other categories omitted. -->
</Northwind>

It’s possible to modify this representation without resorting to additional code or an XSLT transform.

Dissecting the DataSet XML Schema

The rules for the schema document are a little more subtle. First of all, a complexType is defined for each type of table row. Complex types define a structure that is composed of several separate pieces of information. In the following example, the Categories element is a complex type that contains several subtags:

<?xml version="1.0" standalone="yes"?>
<xs:schema xmlns=""
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

  <xs:element msdata:IsDataSet="true">

    <xs:complexType>
      <xs:choice maxOccurs="unbounded">
        <xs:element>
          <xs:complexType>

          <!-- Definition of Categories type omitted. -->

          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>

    <!-- Additional code omitted. -->

</xs:schema>

The XSD sequence element is nested inside the complexType element, indicating that the fields must occur in a set order:

        <xs:element>
          <xs:complexType>
            <xs:sequence>

            <!-- Definition of Categories type omitted. -->

            </xs:sequence>
          </xs:complexType>
        </xs:element>

Every field in a row is declared using the corresponding XSD data type. If the field is optional (in other words, DataColumn.AllowDbNull is True), the minOccurs attribute is set to 0, indicating that this element isn’t necessary. Similarly, a maxLength restriction element is added to a type if the DataColumn.MaxLength property is set.

        <xs:element>
          <xs:complexType>
            <xs:sequence>

              <xs:element msdata:ReadOnly="true"
                  msdata:AutoIncrement="true" />

              <xs:element>
                <xs:simpleType>
                  <xs:restriction base="xs:string">
                    <xs:maxLength value="15" />
                  </xs:restriction>
                </xs:simpleType>
              </xs:element>

              <xs:element minOccurs="0">
                <xs:simpleType>
                  <xs:restriction base="xs:string">
                    <xs:maxLength value="1073741823" />
                  </xs:restriction>
                </xs:simpleType>
              </xs:element>
            </xs:sequence>

          </xs:complexType>
        </xs:element>

Additional database-specific information is added using the msdata namespace, which allows attributes such as ReadOnly and AutoIncrement that aren’t part of the XSD standard but are recognized by ADO.NET.

Finally, the XSD document ends with a definition of unique elements to represent DataSet constraints. In the next snippet, a single unique element represents the primary key definition for the CategoryID field. Two XPath elements are also contained: a selector element that indicates how to find the table this constraint applies to and a field element that indicates how to find the relevant column.

<xs:unique msdata:PrimaryKey="true">
  <xs:selector xpath=".//Categories" />
  <xs:field xpath="CategoryID" />
</xs:unique>

Remember, the XSD schema is created based on the characteristics of the DataColumn and Constraint objects in the DataSet. To make sure you have as much information as possible, use the FillSchema( ) method before the WriteXmlSchema( ) method (or a strongly typed DataSet).

XML Write and Read Modes

By default, the WriteXml( ) method simply outputs the XML data. You must create the XSD document separately. However, you can use an overloaded version of the WriteXml( ) method, which accepts a value from the XmlWriteMode enumeration. These values are described in Table 17-2.

Table 17-2. XmlWriteMode values
Value Description
IgnoreSchema
Writes the current contents of the DataSet as XML data, without an XML Schema. This is the default.
WriteSchema
Writes the current contents of the DataSet as XML data with the relational structure as inline XML schema.
DiffGram
Writes the entire DataSet as a DiffGram, which includes information about original and current values. We’ll examine DiffGrams in the next section.

For example, you can choose to write the XSD inline with the XML. This shortens the coding but can waste some disk space if you store multiple DataSet files with the same schema. It can also lead to versioning problems if you modify the DataSet structure later on, and it no longer matches the schema.

// Save DataSet to disk (with schema).
ds.WriteXml("mydata.xml", XmlWriteMode.WriteSchema);

// Reset the DataSet.
ds.Reset();

// Read schema and reload data.
ds.ReadXml("mydata.xml", XmlReadMode.ReadSchema);

The mydata.xml file now has the following structure:

<?xml version="1.0" standalone="yes"?>
<Northwind>

  <!-- Inline XSD schema document goes here. -->

  <Categories>
    <CategoryID>1</CategoryID>
    <CategoryName>Beverages</CategoryName>
    <Description>Soft drinks, coffees, teas, beers, and ales</Description>
  </Categories>

  <!-- Other rows omitted. -->

</Northwind>

In this case, you don’t need to specify the XmlReadMode.ReadSchema when retrieving the data. The default, XmlReadMode.Auto, inspects the file and uses ReadSchema mode if it contains a schema. On the other hand, the default when saving data is XmlWriteMode.IgnoreSchema, which uses only the data. The full list of XmlWriteMode values is shown in Table 17-3.

Table 17-3. XmlReadMode values
Value Description
Auto
Inspects the XML file and tries to perform the most appropriate action, depending on whether the file is a DiffGram or contains an inline schema. (Note that the inline schema is ignored if the DataSet already has a schema or the file contains a DiffGram.)
DiffGram
Reads a DiffGram, which specifies the contents of the DataSet and uses additional attributes to indicate changed values and inserted and deleted rows. These changes are applied to the DataSet automatically. However, it must have the same schema as the DataSet used to create the DiffGram, or an exception will be thrown.
Fragment
Reads partial XML documents, such as those generated by executing FOR XML queries with SQL Server 2000. The default namespace is read as the inline schema.
IgnoreSchema
Ignores any inline schema and reads data into the existing DataSet (preserving any schema information it already has). If any data doesn’t match the existing schema, it is discarded. You can also use this mode to read a DiffGram.
InferSchema
Ignores any inline schema, loads the data, and infers the schema from the XML document. If the DataSet already contains a schema, the current schema is extended by adding new tables or adding columns to existing tables. An exception is thrown if the inferred table already exists but with a different namespace or if any of the inferred columns conflict with existing columns.
ReadSchema
Reads any inline schema and loads the data. If the DataSet already contains schema, and the schema refers to the same table, an exception is thrown. Otherwise, the new schema information extends the current DataSet schema by adding new tables.

Dissecting the DiffGram

The DataSet doesn’t just contain schema information and a single set of data, it also tracks the state of each row and, if modified, the current and original values. In order to record this information in XML, ADO.NET defines a special DiffGram format.

The DiffGram format is divided into three sections: the current data, the original data, and any errors:

<?xml version="1.0"?>
<diffgr:diffgram
        xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
        xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">

   <DataInstance>
   </DataInstance>

  <diffgr:before>
  </diffgr:before>

  <diffgr:errors>
  </diffgr:errors>

</diffgr:diffgram>

The DataInstance element contains the actual DataSet information. Rows that have been changed are marked with the diffgr:hasChanges attribute. The diffgr:before element lists the information about the original values, while elements in diffgr:errors represent the DataRow.RowError property. Elements are matched between these three sections using the diffgr:id attribute.

Example 17-2 creates a DiffGram (and displays it in a console window). The DataSet is made up of three rows retrieved from the database. The first row is modified, the second is deleted, and a fourth row is created and added programmatically.

Example 17-2. Writing a DataSet DiffGram
using System;
using System.Data;
using System.Data.SqlClient;

public class SaveDiffGram
{
    private static string connectionString = "Data Source=localhost;" +
         "Initial Catalog=Northwind;Integrated Security=SSPI";

    public static void Main()
    {
        string SQL = "SELECT TOP 3 CategoryID, CategoryName, " +
                     "Description FROM Categories";

        // Create ADO.NET objects.
        SqlConnection con = new SqlConnection(connectionString);
        SqlCommand com = new SqlCommand(SQL, con);
        SqlDataAdapter adapter = new SqlDataAdapter(com);
        DataSet ds = new DataSet("Nortwind");

        // Execute the command.
        try
        {
            con.Open();
            adapter.FillSchema(ds, SchemaType.Mapped, "Categories");
            adapter.Fill(ds, "Categories");
        }
        catch (Exception err)
        {
            Console.WriteLine(err.ToString());
        }
        finally
        {
            con.Close();
        }

        // Modify the DataSet (change first row, delete second,
        // and add a fourth).
        DataRow row = ds.Tables["Categories"].Rows[0];
        row["CategoryName"] = "Pastries";
        row["Description"] = "Danishes, donuts, and coffee cake";

        ds.Tables["Categories"].Rows[1].Delete();

        row = ds.Tables["Categories"].NewRow();
        row["CategoryName"] = "Baked goods";
        row["Description"] = "Bread, croissants, and bagels";
        ds.Tables["Categories"].Rows.Add(row);

        // Save DataSet diffgram to disk.
        ds.WriteXml("mydata.xml" , XmlWriteMode.DiffGram);

        // Display DataSet diffgram.
        ds.WriteXml(Console.Out , XmlWriteMode.DiffGram);
    }
}

The DiffGram includes all four rows. However, the deleted row appears only in the diffgr:before section:

<?xml version="1.0" standalone="yes"?>
<diffgr:diffgram
        xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
        xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">

  <Nortwind>
    <Categories diffgr:id="Categories1" msdata:rowOrder="0"
     diffgr:hasChanges="modified">
      <CategoryID>1</CategoryID>
      <CategoryName>Pastries</CategoryName>
      <Description>Danishes, donuts, and coffee cake</Description>
    </Categories>
    <Categories diffgr:id="Categories3" msdata:rowOrder="2">
      <CategoryID>3</CategoryID>
      <CategoryName>Confections</CategoryName>
      <Description>Desserts, candies, and sweet breads</Description>
    </Categories>
    <Categories diffgr:id="Categories4" msdata:rowOrder="3"
     diffgr:hasChanges="inserted">
      <CategoryID>4</CategoryID>
      <CategoryName>Baked goods</CategoryName>
      <Description>Bread, croissants, and bagels</Description>
    </Categories>
  </Nortwind>

  <diffgr:before>
    <Categories diffgr:id="Categories1" msdata:rowOrder="0">
      <CategoryID>1</CategoryID>
      <CategoryName>Beverages</CategoryName>
      <Description>Soft drinks, coffees, teas, beers,
       and ales</Description>
    </Categories>
    <Categories diffgr:id="Categories2" msdata:rowOrder="1">
      <CategoryID>2</CategoryID>
      <CategoryName>Condiments</CategoryName>
      <Description>Sweet and savory sauces, relishes, spreads, and
       seasonings</Description>
    </Categories>
  </diffgr:before>

</diffgr:diffgram>

Without the DiffGram, the XML file resembles the first section, without the added msdata and diffgr attributes. In other words, the deleted row isn’t saved, and no distinction is made between the original rows and the inserted row. When you reload the non-DiffGram XML into a DataSet, every row is set to DataRowState.Unchanged. If you tried to update the data source with this DataSet, no changes are made.

The default output generated with WriteXml( ) and WriteXmlSchema( ) includes the current contents. The DiffGram output, on the other hand, generates the information needed to use the DataSet change tracking.

If you use a DataSet as a return value from a method in a web service or a component exposed through .NET remoting, the DiffGram is automatically returned. For example, consider what happens if you modify the code in Example 17-2 to become a rudimentary web service in Example 17-3.

Example 17-3. A web service that returns a modified DataSet
<%@ Webservice %>

using System;
using System.Web.Services;
using System.Data;
using System.Data.SqlClient;

public class ADOService : System.Web.Services.WebService
{
    private string connectionString = "Data Source=localhost;" +
        "Initial Catalog=Northwind;Integrated Security=SSPI";

    [WebMethod]
    public DataSet GetCategoriesTest()
    {
    string SQL = "SELECT TOP 3 CategoryID, CategoryName, " +
                 "Description FROM Categories";

    // Create ADO.NET objects.
    SqlConnection con = new SqlConnection(connectionString);
    SqlCommand com = new SqlCommand(SQL, con);
    SqlDataAdapter adapter = new SqlDataAdapter(com);
    DataSet ds = new DataSet("Nortwind");

    // Execute the command.
    con.Open();
    adapter.FillSchema(ds, SchemaType.Mapped, "Categories");
    adapter.Fill(ds, "Categories");
    con.Close();

    // Modify the DataSet.
    DataRow row = ds.Tables["Categories"].Rows[0];
    row["CategoryName"] = "Pastries";
    row["Description"] = "Danishes, donuts, and coffee cake";

    ds.Tables["Categories"].Rows[1].Delete();

    row = ds.Tables["Categories"].NewRow();
    row["CategoryName"] = "Baked goods";
    row["Description"] = "Bread, croissants, and bagels";
    ds.Tables["Categories"].Rows.Add(row);

    // Return DataSet
    return ds;
    }
}

If you try this web method using the Internet Explorer test page, you’ll find that the retrieved result includes a schema (at the beginning of the message), followed by a DiffGram containing the DataSet contents and recording all changes Figure 17-1 shows a partially collapsed view of this information.

Figure 17-1. A DataSet schema and DiffGram returned by a web method

Thus, a .NET client can capture this information and automatically recreate an identical DataSet instance. A third-party client, however, needs to prepare for this information and handle it accordingly.

Shaping DataSet XML

The default format used for the DataSet XML works perfectly well when you create a new cross-platform application. However, most systems include legacy clients that require data in a set format. Unfortunately, even a minute difference between the expected and the actual XML file format can prevent a client from successfully reading the data.

ADO.NET offers limited ways to customize the generated XML for a DataSet. Before writing your own custom code, you should begin by examining these features. If you need to perform more significant changes, you need to perform these additional operations using the .NET XML classes or XLST transformation.

Attributes and Elements

One recurring question in XML modeling is the question of whether to use attributes to store data or contained elements. For example, here’s a category row that uses only elements:

<Categories>
  <CategoryID>1</CategoryID>
  <CategoryName>Beverages</CategoryName>
  <Description>Soft drinks, coffees, teas, beers, and ales</Description>
</Categories>

And here’s the same row with attributes:

<Categories CategoryID="1" CategoryName="Beverages"
 Description="Soft drinks, coffees, teas, beers, and ales" />

With ADO.NET, you can configure whether column values are stored in attributes or elements. Best of all, you can do it on a per-column basis (for example, storing the unique identity column as an attribute and all other columns as elements). All you need to do is set the DataColumn.ColumnMapping property to one of the values shown in Table 17-4.

Table 17-4. MappingStyle values
Value Description
Element
The column is written as an XML element. The column name becomes the name of the element, and the contents of the column are written as the text of the element. This is the default.

<ColumnName>ColumnContent</ColumnName>

Attribute
The column is written as an XML attribute of the XML element for the current row. The column name becomes the name of the attribute, and the contents of the column become the value of the attribute.

<RowElement ColumnName=”ColumnContent” />

SimpleContent
The contents of the column are written as text in the XML element for the current row. Note that only one column in a table can be set to SimpleContent, and SimpleContent can’t be used for a table that has other MappingStyle.Element columns or nested relations.

<RowElement>ColumnContent</RowElement>

Hidden
The column isn’t written as part of the XML output.

The following code snippet iterates through all the column objects in a DataSet and configures them to use attribute-based representation. You can add this code to Example 17-1 earlier in this chapter to try out the technique.

foreach (DataColumn col in ds.Table["Categories"].Columns)
{
    col.ColumnMapping = MappingStyle.Attribute;
}

Unfortunately, there is no easy way to use a different element or attribute name. If you don’t want to use the default DataColumn.ColumnName, you have to create an export routine that manually renames columns before serializing the DataSet.

Relational XML Data

Relational databases organize information into tables, in which each row represents an independent item. To relate different types of items (such as customers and orders), table relations are added. XML, on the other hand, often uses a hierarchical model. For example, you can model a list of products by creating product elements inside category elements. This type of organization is more efficient for some operations (for example, finding all the products in a given category), and less flexible for others (for example, creating a composite list of products).

By default, when writing a DataSet with multiple tables to XML, ADO.NET places the row elements for each table consecutively. For example, in XML, a table with rows from the Categories and Products tables is stored with the following format:

<?xml version="1.0" standalone="yes"?>
<Northwind>
  <Categories>
    <CategoryID>1</CategoryID>
    <CategoryName>Beverages</CategoryName>
    <Description>Soft drinks, coffees, teas, beers, and ales</Description>
  </Categories>
  <Categories>
    <CategoryID>2</CategoryID>
    <CategoryName>Condiments</CategoryName>
    <Description>Sweet and savory sauces, relishes, spreads, and
     seasonings</Description>
  </Categories>

  <!-- Other categories omitted. -->

  <Products>
    <ProductID>1</ProductID>
    <ProductName>Chai</ProductName>
    <CategoryID>1</CategoryID>
  </Products>
  <Products>
    <ProductID>2</ProductID>
    <ProductName>Chang</ProductName>
    <CategoryID>1</CategoryID>
  </Products>

  <!-- Other products omitted. -->

</Northwind>

Alternatively, you can create XML using a tree-based structure with relations. All you need to do is create the DataRelation and set the DataRelation.Nested property to true. In this case, the format is hierarchical, based on the parent-child relationship, and products are grouped by categories:

<?xml version="1.0" standalone="yes"?>
<Northwind>

  <Categories>
    <CategoryID>1</CategoryID>
    <CategoryName>Beverages</CategoryName>
    <Description>Soft drinks, coffees, teas, beers, and ales</Description>

    <Products>
      <ProductID>1</ProductID>
      <ProductName>Chai</ProductName>
      <CategoryID>1</CategoryID>
    </Products>
    <Products>
      <ProductID>2</ProductID>
      <ProductName>Chang</ProductName>
      <CategoryID>1</CategoryID>
    </Products>

    <!-- Other products in this category omitted. -->

  </Categories>

  <Categories>
    <CategoryID>2</CategoryID>
    <CategoryName>Condiments</CategoryName>
    <Description>Sweet and savory sauces, relishes, spreads, and
     seasonings</Description>

    <Products>
      <ProductID>3</ProductID>
      <ProductName>Aniseed Syrup</ProductName>
      <CategoryID>2</CategoryID>
    </Products>

    <!-- Other products in this category omitted. -->

  </Categories>

  <!-- Other categories omitted. -->

</Northwind>

Figure 17-2 diagrams the difference.

Figure 17-2. Related tables in an XML document

This is all well and good for a one-to-many relationship, but what about a many-to-many relationship, which is always implemented by three tables? For example, in the Northwind database, there are Territories, Employees, and linking EmployeeTerritories tables.

The hierarchical tree-based model isn’t well suited to show this relationship. If you nest Territory elements in EmployeeTerritory elements, which in turn are placed inside Employee elements, you quickly end up with duplicate Territory elements. If you use a tree starting with Territory elements, you end up with a duplicate employee under more than one territory branch. ADO.NET avoids this problem by disallowing it. If you try to set the Nested property on both columns, you receive an ArgumentException.

Advertisements

One Response to “ADO.NET Core Classes:XML and the DataSet – Part 15”

  1. pikavippi said

    I have read a scarcely any of the articles on your website in the present circumstances, and I extremely like your line of blogging. I added it to my favorites web page roster and resolve be checking promote soon. Please check into public notice my site as highly and fail me be familiar with what you think. Thanks.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: