Just Code‎ > ‎

C# - Dataset from XSD schema with multiple datatables in hierachy. Data from only one stored procedure.

posted Feb 22, 2010, 12:13 AM by Peter Henell   [ updated Jan 25, 2012, 4:48 AM ]

Loading big sets of data from the database can be a resource hog. Especially if you need to have the data in a hierachical structure like an xml or a series of datasets. You don't want to use loops in your C# to fetch child data from the database, that would be horrible.
This is what i used when i had to create a complex XML result from a single stored procedure. The result was to be sent from a webservice so it had to be checked against an XSD schema.

Note: All of this is wasted if all you want is the XML and using SQL 2005 Server or later. Then you would just use the XML datatype/functionallity.


Lets begin:

Add New Item -> XML Schema OrderHistory.xsd
In properties window for the newly added file, set Custom Tool to MSDataSetGenerator

Configure the schema for your needs using text or design mode.
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="OrderView" targetNamespace="http://tempuri.org/OrderView.xsd" elementFormDefault="qualified" xmlns="http://tempuri.org/OrderView.xsd" xmlns:mstns="http://tempuri.org/OrderView.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified">
    <xs:element name="Order">
        <xs:complexType>
            <xs:sequence minOccurs="1" maxOccurs="unbounded">
                <xs:element name="Summa" type="xs:float" />
                <xs:element name="OrderDate" type="xs:int" />
                <xs:element name="CustomerID" type="xs:int" />
                <xs:element name="OrderID" type="xs:int" />
                <xs:element name="OrderLines" minOccurs="1" maxOccurs="unbounded">
                    <xs:complexType>
                        <xs:sequence minOccurs="1" maxOccurs="unbounded">
                            <xs:element name="ProductCode" type="xs:string" />
                            <xs:element name="Qty" type="xs:float" />
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

Set up the code to get the data into the generated dataset:
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;


namespace ConsoleApplication1
{ class Program
{ static void Main(string[] args) { //Ett(); Tva(); } private static void Tva() { OrderView ds = new OrderView(); ds.EnforceConstraints = false; ExecuteStoredProcedure( ds, "dbo.GetOrdersTest2", new string[] { }, new object[] { }, new DataTableMapping[] { new DataTableMapping("Table", "Order", new DataColumnMapping[] {}), new DataTableMapping("Table1", "OrderLines", new DataColumnMapping[] {}), }, -1 ); ds.WriteXml(@"c:\temp\testar2.xml"); //ds.EnforceConstraints = true; //ds.EnforceConstraints = true; } public static void ExecuteStoredProcedure(DataSet ds, string storedProcedureName, string[] paramNames, object[] paramValues, DataTableMapping[] dataTableMappings, int timeOut) { SqlDataAdapter da; using (SqlConnection con = new SqlConnection("Data Source=.;Database=OrderSystem;User ID=;Password=;Trusted_Connection=False;")) { using (SqlCommand cmd = new SqlCommand(storedProcedureName, con)) { da = new SqlDataAdapter(); da.SelectCommand = cmd; da.TableMappings.Clear(); da.TableMappings.AddRange(dataTableMappings); cmd.CommandType = CommandType.StoredProcedure; if (timeOut > 0) cmd.CommandTimeout = timeOut; else cmd.CommandTimeout = 45; for (int i = 0; i < paramNames.Length; i++) { if (paramValues[i] == null) cmd.Parameters.AddWithValue(paramNames[i], DBNull.Value); else cmd.Parameters.AddWithValue(paramNames[i], paramValues[i]); } da.Fill(ds); } } } } }


Then create the stored procedure to fetch both these DataTables needed.
create procedure [dbo].[GetOrdersTest2]
as
begin



create table #order
(    Id_row            INT IDENTITY,
    OrderId            INT,
    summa            FLOAT,
    OrderDate        int,
    CustomerId        INT
    
)

INSERT INTO #ORDER
SELECT DISTINCT
    ord.o02, -- OrderId
    NULL, -- summa
    NULL, -- OrderDate
    NULL -- CustomerId
    
FROM orders ord
WHERE nr > 1019664 update #order set summa = 552.20
, OrderDate = ord.o03
, CustomerId = ord.o01
from
orders ord
where
ord.o02 = #order.OrderID


--[Xml parent Node]: Order Order_Id is a magic column, more on that below. select OrderId as Order_Id, Summa as Summa, OrderDate as OrderDate

from #order --[Xml parent Node]: OrderLines, you need that magic column Order_Id here aswell. That column will reference to the Order object in the dataset. select distinct OrderId as Order_Id, 'Candy' as ProductCode, 14 as Qty
from #order end

How does it all work?

There are two DataTables returned from this procedure, each marked with the [Xml Parent node]-tag. Each of those DataTables include a column named Order_Id, but that column is not part of our XSD so what does it do?

The Order_Id column is a hidden autogenerated column in the OrderViewDataset that have a unique constraint put on it. It is used as an internal key in the Dataset to identify relationships and dependencies between DataTables. If you provide a column from the stored procedure with that name then the value from the procedure will be used to maintain the relationships.

Since it is hidden, it will now show up in the XML Output. Since it need to be unique from the database i have created a temporary table with an identity column that i use for the Order_Id.

Imagine you want to include some deeper dependencies like Order/BillingInformation/BillToInfo, where BillToInfo should contain some address. Then you need to create another temporary table and provide another "key" column: BillingInformation_Id and reference that in the BillToInfo-datatable. An example like this is included in the attachements!

And the result XML

<?xml version="1.0" standalone="yes"?>
<OrderView xmlns="http://tempuri.org/OrderView.xsd">
  <Order>
    <Summa>552.2</Summa>
    <OrderDate>20100224</OrderDate>
    <OrderLines>
      <ProductCode>Banana</ProductCode>
      <Qty>14</Qty>
    </OrderLines>
  </Order>
  <Order>
    <Summa>552.2</Summa>
    <OrderDate>20100224</OrderDate>
    <OrderLines>
      <ProductCode>banana</ProductCode>
      <Qty>14</Qty>
    </OrderLines>
  </Order>
  <Order>
    <Summa>552.2</Summa>
    <OrderDate>20100224</OrderDate>
    <OrderLines>
      <ProductCode>banana</ProductCode>
      <Qty>14</Qty>
    </OrderLines>
  </Order>
  <Order>
    <Summa>552.2</Summa>
    <OrderDate>20100224</OrderDate>
    <OrderLines>
      <ProductCode>banana</ProductCode>
      <Qty>14</Qty>
    </OrderLines>
  </Order>
  <Order>
    <Summa>552.2</Summa>
    <OrderDate>20100224</OrderDate>
    <OrderLines>
      <ProductCode>banana</ProductCode>
      <Qty>14</Qty>
    </OrderLines>
  </Order>
  <Order>
    <Summa>552.2</Summa>
    <OrderDate>20100224</OrderDate>
    <OrderLines>
      <ProductCode>banana</ProductCode>
      <Qty>14</Qty>
    </OrderLines>
  </Order>
  <Order>
    <Summa>552.2</Summa>
    <OrderDate>20100224</OrderDate>
    <OrderLines>
      <ProductCode>banana</ProductCode>
      <Qty>14</Qty>
    </OrderLines>
  </Order>
  <Order>
    <Summa>552.2</Summa>
    <OrderDate>20100224</OrderDate>
    <OrderLines>
      <ProductCode>banana</ProductCode>
      <Qty>14</Qty>
    </OrderLines>
  </Order>
  <Order>
    <Summa>552.2</Summa>
    <OrderDate>20100224</OrderDate>
    <OrderLines>
      <ProductCode>banana</ProductCode>
      <Qty>14</Qty>
    </OrderLines>
  </Order>
  <Order>
    <Summa>552.2</Summa>
    <OrderDate>20100224</OrderDate>
    <OrderLines>
      <ProductCode>banana</ProductCode>
      <Qty>14</Qty>
    </OrderLines>
  </Order>
  <Order>
    <Summa>552.2</Summa>
    <OrderDate>20100309</OrderDate>
    <OrderLines>
      <ProductCode>banana</ProductCode>
      <Qty>14</Qty>
    </OrderLines>
  </Order>
  <Order>
    <Summa>552.2</Summa>
    <OrderDate>20100309</OrderDate>
    <OrderLines>
      <ProductCode>banana</ProductCode>
      <Qty>14</Qty>
    </OrderLines>
  </Order>
</OrderView>
ċ
OrderHistoryViewIncludingAdvancedexample.zip
(274k)
Peter Henell,
Mar 24, 2010, 3:32 AM
ċ
TestForOrderHistoryView.zip
(271k)
Peter Henell,
Mar 23, 2010, 6:58 AM
Comments