Data in .NET, it’s all about XML

Over the last ten years I have been writing articles for the SDGN Magazine. Up until now this has always been in Dutch, but the internet is taking its toll. All new articles in the SDGN magazine are now also available on the SDGN website and can be read by anyone interested. It would be asking too much for all visitors to learn Dutch and every Dutch developer does understand English. I have decided to write this one in English.

The theme of this magazine is databases. In this article I want to make a first survey of data in .NET. The data format in .NET is XML rather than a specific database. In this story we’ll first see how any object in .NET can represent itself in XML. Next we’ll see how data from a database is read into XML datasets and how big the similarity between the database data and the object data is. To conclude we’ll see how the database adapter transfers any updates in the XML back to the database. In the process we’ll meet several of the many readers and writers .NET uses to transfer data from one point in code to another.

A very short recap on XML

XML stands for eXtensible Markup Language. The first common markup language was HTML. Using HTML tags you mark up a text to describe what it should look like in a web browser. Take for instance just some text; the tags will be applied to the text in between them. That will look like just some text in the browser.

In XML tags are used to describe data between the tags. In HTML the names of the tags are limited to a common set all browsers have agreed upon. XML is per definition extensible, the names of the tags can be just as diverse as what is between them. Take this snippet of basic XML:



  This is my list

 

    Een

    Twee

 



It holds and describes a named list. The list’s name is between the Aname tags. The ListContents tags mark up a list of Alistmember tag pairs holding the list’s items. The document has one single root node NamedList. All tags have a properly placed closing tag. The document is said to be ‘well formed’. HTML can be forgiving when it comes to leaving out things like closing tags. XML is not. A well formed single rooted XML document can be manipulated using the Document Object Model. This is another W3C standard better known as the DOM.




All XML tags have a properly placed closing tag

In .NET, an XML document holds data and metadata describing that data. The metadata describes, amongst others, the data type of the actual fields. The type of the data contained is an attribute of the data’s tag.



Een



Attributes work just like attributes in HTML. For instance, in HTML the src attribute of an image holds the filename of the image.





This XML attribute is named xsi:type and its value is xsd:string. The xsi and the xsd prefixes qualify the attributes as being part of a namespace. Namespaces are mapped in yet another attribute of the node itself, its parent node, or further up in the tree of the document. In the example snippet the namespace is mapped in the NamedList tag







The xmlns qualifier stands for XML NameSpace. In the namedlist node the XMLschema namespace is applied, its members are qualified via xsd. The schema instance namespace is applied via the xsi qualifier. We have seen the combination in the xsi:type=”xsd:string” attribute.

In this story we’ll see how also the document itself has a namespace in which it describes its members like Aname and Alistmember. And we’ll see how .NET leans on XML schema to generate .NET types.

A class and its serializer

Let’s start with a simple .NET class. It has two fields: a string to give its objects a name and a list of objects to store some real data. The class has one method that adds a new object to the list. The list of objects is implemented as an ArrayList, the constructor of the class initializes that and sets the fields to some demo values.



public class Class1

{

  public String Aname;

  public ArrayList aList;

  public Class1()

  {

    Aname = "Say cuckoo";

    aList = new ArrayList();

    aList.Add("Een");

    aList.Add("Twee");

  }

  public void Add(object o)

  {

    aList.Add(o);

  }

}



Having this declaration I can instantiate objects of this class from code. Serious applications need to persist these objects, that is save them in some format to disk or network and recreate the objects from storage some other time. In .NET (almost) all classes support the concept of serialization. A serialized object is a representation of the object in a linear format. That can be a stream of bits or text, which is saved to disk or transported over the (inter-)network. Out of this stream the object can be recreated in a process called deserialization.

Adding the possibility to serialize is a snap in .NET. It is actually just one bit in the metadata of the class by applying the Serializable() attribute to the class. The actual (de-)serialization is done by serializer objects. They need the type of the object as a parameter to their constructor. The serialize and deserialize methods of the serializer object will do the hard work.



The actual (de-)serialization is done by serializer objects



The .NET framework houses several serializer classes. The BinaryFormatter class serializes an object into a binary format which is handy for plain storage. The SoapFormatter class serializes an object into the SOAP format which is very handy when you use the object in a web service. Here I will show the third, XmlSerializer, which renders an XML representation of an object.

The demo Class1 gets a serialize and a deserialize method. It will be able to create a new object out of the blue from a stream. This implies that the deserializer will be a static method. Static, or class, methods can be called straight from the class, you do not need an object instance. The serializer used in the method will also be a static member. This is good as now only one serializer will be created, regardless of how many objects of the class will be instantiated. This serializer is initiated in the static constructor of the class.



[Serializable()]

public class Class1

{

  public String Aname;

  public ArrayList Alist;

  static private System.Xml.Serialization.XmlSerializer

    serializer;

  static Class1()

  {

    Type t = Type.GetType("O2db.Class1");

    serializer = new

      System.Xml.Serialization.XmlSerializer(t);

  }

  public Class1()

  {

    Aname = "Say cuckoo";

    aList = new ArrayList();

    aList.Add("Een");

    aList.Add("Twee");

  }

  public void Add(object o)

  {

    aList.Add(o);

  }

  public void Serialize(System.Xml.XmlWriter xw)

  {     

    serializer.Serialize(xw, this);

  }

  static public Class1 Deserialize(System.IO.Stream s)

  {

    return serializer.Deserialize(s) as Class1;

  }

}



The class is decorated with the Serializable() attribute. If also a default constructor (a constructor without parameters) is supplied, the runtime is able to (de-)serialize objects of the class.

Static Class1() is a static constructor. It is executed once, at the moment the first time an object of this class is created. In this static constructor the serializer is initiated. C# doesn’t have type variables like Delphi, instead it uses the Type class. Objects of the Type class encapsulate the type information of another class. The Type class has a static method GetType that needs the name of the class to construct the type information. This type object is passed to the constructor of the XmlSerializer class. Those of you who know Delphi would have liked to code:



serializer =

  new System.Xml.Serialization.XmlSerializer(Class1);


But the C# compiler requires:



Type t = Type.GetType("O2db.Class1");

serializer =

  new System.Xml.Serialization.XmlSerializer(t);


The actual serialization is a one-liner:



serializer.Serialize(xw, this);



The method needs an object instance of Class1, which is this, and an XmlWriter to write the result to. We’ll meet this XmlWriter later on. The deserialization is another one-liner.



static public Class1 Deserialize(System.IO.Stream s)

{

  return serializer.Deserialize(s) as Class1;

}



The static (class) method deserializes the stream into a new object of the class.

Displaying the XML representation of the object

Let’s use this class in a simple demo to see how serialization works. The demo project is a Windows form that creates a new Class1 object when opened. It has a button and a textbox to add new strings to the list and a textbox to show the XML representation of the object.



private Class1 o2s = new Class1();

private void button2_Click(

  object sender, System.EventArgs e)

{

  o2s.Add(textBox2.Text);

  StringWriter sw = new StringWriter();

  XmlTextWriter tw = new XmlTextWriter(sw);

  tw.Formatting = System.Xml.Formatting.Indented;

  o2s.Serialize(tw);

  textBoxC1xml.text = sw.ToString();

  tw.Close();

}



In .NET you’re not supposed to directly manipulate strings. You can do that but the costs are high. Far better is a StringBuilder object; here it is wrapped by a StringWriter object. Building a piece of XML text also involves some special issues. For instance it’s layout. The XML parser doesn’t care if all XML comes in one long stream; to the human eye some layout can greatly clarify things. The XmlTextWriter class helps you with all tasks in building XML and uses a StringWriter object to write its result to a text format.

The code creates a StringWriter and an XmlTextwriter based on that. The formatting of the textwriter is set to Indented to get a pretty result and then the object can serialize itself into the textwriter, after which the wrapped stringwriter holds the result that is directly written to a textbox on the form.



Using the combination of the serializer and the deserializer I can now persist objects to file. A SaveFileDialog opens a filestream. An overload of the serializer, which uses a stream instead of a textwriter, writes the object to file.



private void buttonSave_Click(

  object sender, System.EventArgs e)

{

  if (saveFileDialog1.ShowDialog() == DialogResult.OK)

  {

    Stream fs = saveFileDialog1.OpenFile();

    o2s.Serialize(fs);

    fs.Close();

  }

}

// Class1

public void Serialize(System.IO.Stream s)

{     

  serializer.Serialize(s, this);

}



The saved file can be viewed in IE, which has a very strong notion of XML. Collapse the nodes to get a better overview of the XML document.



Note that IE does not show the encoding="utf-16" attribute. This attribute describes the character encoding used by the document and is swallowed by IE as a processing instruction.

This saved file is opened by the demo app to recreate the object. An OpenFileDialog opens the stream after which the Deserialize method recreates the object.



private void buttonLoad_Click(

  object sender, System.EventArgs e)

{

  if (openFileDialog1.ShowDialog() == DialogResult.OK)

  {

    Stream fs = openFileDialog1.OpenFile();

    o2s = Class1.Deserialize(fs);

    fs.Close();

    // Show contents of o2s

  }

}



That’s it. Now I can persist a .NET object as an XMLdocument. All functionality needed to do this is part of the .NET framework.

Reading data from a database

In the real world most of the data is stored in a database. Let’s take a look how .NET works with databases. Visual Studio has a tool called the Server Explorer. It displays resources found on a machine, including the SQL Servers. You can browse the SQL Servers from this tool and view all databases and their included data from there. If you are not using MS SQL Server, a connection to any ADO enabled database can be created from the Data connections node in the explorer. This will fire up the classic ADO wizard to set up the connection. In the demo I browse to a table in a SQL Server database and drag that table from the explorer to the form.



When dropped VS.NET generates two components. The sqlConnection1 component sets up the connection, its properties wrap up the connection string. The sqlDataAdapter1 component wraps up the interaction with a tabular set of database data; we will explore that in just a moment. First I’ll drop a second table on the form. Now VS.NET will only generate a second sqlDataAdapter component, this will use the existing sqlConnection1.

The dataAdapter wraps up all interactions with the data. It has a SelectCommand, an UpdateCommand, an InsertCommand and a DeleteCommand. By default VS.NET generates all four on all columns of the selected table. When you’re only going to browse through the data you can discard all but the select command. From the property window the ellipsis start the query builder to fine tune the SQL.



Meet the XML dataset

Another one-click wizard in VS.NET generates a so called DataSet based on the dataAdapter. Right click the adapter to start this wizard. A dataset is the data format every corner of the .NET framework understands. It has three faces:

·          An XML DOM-document in which all data (from the database) is presented as XML.

·          An XML schema describing the XML document.

·          A .NET class where the data is presented in (multiple) typed table properties of the objects.

A dataset is the data format every corner of the .NET framework understands

The wizard generates the schema from the SQL query and then generates the class from the schema. In the project you see the generated datasets as xsd (XML schema definition) files. Double clicking one pops up the schema editor. Before covering that the dataset wizard is set to work once more, I’ll have it regenerate the dataset but now the dataset will be based on both dataAdapters. When serializing the object to XML we already saw that XML can perfectly describe master-detail data. The list had just one name and any number of items. This dataset will describe a master-detail relation between two database tables.

The schema editor shows both tables of data. These two tables are related in the database. To express this relation in the schema drag the primary key in the master table to the foreign key in the details table. A dialog pops up to fill in the details. In the serialized object all detail rows were nested in the master object. To make this dataset also render XML this way, the master table gets the element TableDetails whose type is set to tabledetail. In the properties of the relation the IsNested property is set to true. In database terms you could say that the type of the field is a table, a functionality that is only available in a database like Oracle. But here it is a property of the dataset and a dataset has no notion of the underlying database at all.



The dataset designer is a tool that models the XSD schema, which is just another XML document. When you change views you’ll see its XML representation.



When the schema is saved VS.NET generates the class that holds structured multi-table data. Several types are generated: one for every table in the class. They are used for the named table properties of the dataset class. For every table also a type is generated which describes a row of the table. The Class viewer of VS.NET gives a clear overview.



Before the DataAdapters can fill datasets with data from the database the connection to the database has to be opened.



private void Form1_Load(

  object sender, System.EventArgs e)

{

  sqlConnection1.Open();

}



The Dispose method of a form is designed to clean up all unmanaged resources, like database connections. VS.NET generates a skeleton dispose implementation for a new form or component; here the connection to the database is closed.



///

/// Clean up any resources being used.

///

protected override void Dispose( bool disposing )

{

  if( disposing )

  {

    if (components != null)

    {

      sqlConnection1.Close();

      components.Dispose();

    }

  }

  base.Dispose( disposing );

}



Now there is a database connection available during the lifetime of the form. The dataAdapters can do their work. In the form load a list of available master rows is read into the dataSetMaster1 component.

sqlDataAdapterMasters.Fill(dataSetMasters1);

Datasets are understood by whole wagonloads of properties of almost all components in the framework. In the demo form I have bound this dataSetMasters1 to a listbox. The aName column is bound to the DisplayMember, the idMaster column to the ValueMember.



This data bound listbox selects a specific row. The selected value is used as a parameter to retrieve both master and detail data using two different dataAdapters.



DataSetMD ds = new DataSetMD()

int mId = (int) listBoxMasters.SelectedValue;

sqlDataAdapterMaster.SelectCommand.Parameters[0].Value

  = mId;

sqlDataAdapterDetail.SelectCommand.Parameters[0].Value

  = mId;

sqlDataAdapterMaster.Fill(ds.TableMaster);

sqlDataAdapterDetail.Fill(ds.TableDetail);



Here I use an overload of the Fill method. Instead of passing the whole dataset I pass the table of the dataset to be filled. The Fill method can find out by itself which table it has to fill; more on this mapping later. But passing in the table makes the code a little more efficient and a lot easier to understand.

The data is read in one sweep into the dataset and the dataset is disconnected from the database. Any updates in the database will not be seen by the dataset until it is refilled. Working with disconnected data is a change from many an existing application where the user usually has a live cursor on the data. Keeping data really up to date becomes harder and harder in more distributed scenarios. In an internet application it is per definition impossible (and unwanted) for the user’s browser to have a live connection with a database somewhere on a web server. Working with these disconnected XML datasets, retrieving the data is completely concentrated in the Fill method.

XMLdatadocument

The content of the dataset can be directly bound to controls. To display the data as XML I’ll use the XMLdatadocument class from the .NET framework. This class combines an XML document object model (the DOM as specified by the W3C) view on the data with a DataSet view on the data. The two views are real views on one underlying set of data. When you change the data in documents using the DOM, the dataset is also updated. And an update of a field in one of the dataset’s tables is reflected in the corresponding node of the DOM.

Creating an XMLdatadocument from a dataset is a one-liner:



XmlDataDocument dd = new XmlDataDocument(ds);

Serializing this data document to XML is done the same way I serialized the object. A serializer is constructed for the data document and the XmlTextWriter will get me the XML representation of the dataset

XmlSerializer dds = new

  System.Xml.Serialization.XmlSerializer(dd.GetType());

sw = new StringWriter();

tw = new XmlTextWriter(sw);

tw.Formatting = System.Xml.Formatting.Indented;

dds.Serialize(tw, dd);

textBoxDataDoc.Text = sw.ToString();

tw.Close();





What you see is a single rooted, well formed XML document. The root node bears the name of the dataset object used to construct the document. Notice the namespace attribute http://www.tempuri.org/DataSetMD.xsd. Namespaces in XML datasets are very much like namespaces of classes in the .NET framework as well as the namespace of your application. This document class describes a DataSetMD document which can be, thanks to the internet, published to anyone all over the world. These people will be using all kinds of documents from all kinds of sources. The chance that the document name DataSetMD is unique on the web is practically zero. The chances that this other DataSetMD document has the same structure as my DataSetMD is even less. A very clear way to distinguish objects is using namespaces. In an app your application’s namespace will distinguish your Class1 from my Class1. In a web world you use a web domain to identify something. The namespace of an XMLdocument has the form of an URL. That domain is responsible for the document and using your websites URL as a base for a namespace makes perfect sense. Visual Studio uses the default www.tempuri.org as a base. This is the site where all unknown namespaces go. You can try to visit (or even ping) the url but you will not get anything back. The site is registered to Microsoft. The full namespace attribute refers to an XML document. This DataSetMD.XSD contains the schema of the dataset we met before. The namespace of the dataset is a property that is set using the schema designer.



When you publish documents, it can be useful for consumers to have access to the schema files. As we have seen VS generates classes on these schemas, which makes building sophisticated consumers a snap. But you don’t have to publish your schemas; for a confidential internal app it could even be harmful. So the namespace does not have to be a Universal Resource Locator, but is has to be a Universal Resource Identifier. It is up to the domain owners to maintain what that Identifier stands for and how any metadata can be located.

The dataset’s schema can be recreated straight from the dataset using the WriteXmlSchema method.



sw = new StringWriter();

dd.DataSet.WriteXmlSchema(sw);

textBoxXSD.Text = sw.ToString();



Using the dataset of the data document I can also programmatically access the data. Let’s write some code that displays what’s in the dataset without using the schema, using an untyped dataset. The dataset has a list of tables. I’ll add these to a listbox.



foreach (DataTable dt in dd.DataSet.Tables)

{

  listBoxTables.Items.Add(dt.TableName);

}


Selecting a table in this listbox will list the table’s fields in another listbox.


foreach (DataColumn dc in

  dd.DataSet.Tables[listBoxTables.SelectedIndex].Columns)

{

  listBoxFields.Items.Add(dc.ColumnName);

}


Selecting a field displays the value of the fields in all rows.


DataTable dt = dd.DataSet.Tables[listBoxTables.SelectedIndex];

foreach (DataRow dr in dt.Rows)

{

 listBoxValues.Items.Add(

   dr.ItemArray[listBoxFields.SelectedIndex].ToString());

}


The data document was created from a dataset. The dataset had been filled by the dataAdapters. But a data document can also be created from pure XML, just the XML the Class1 objects serialized them selves.



StringWriter sw = new StringWriter();

XmlTextWriter tw = new XmlTextWriter(sw);

tw.Formatting = System.Xml.Formatting.Indented;

o2s.Serialize(tw);

StringReader sr = new StringReader(sw.ToString());

XmlTextReader xr = new XmlTextReader(sr);

dd = new XmlDataDocument();

dd.DataSet.DataSetName = "BasedOnobject";

dd.DataSet.ReadXml(

  xr, System.Data.XmlReadMode.InferSchema);

tw.Close();



Instead of writing the XML to a textbox, a new StringReader is wrapped around it to prepare for an XmlTextReader. A blank XmlDataDocument is created, the ReadXml method of its DataSet fills the document. The second parameter of the ReadXml method instructs the document to infer a schema from the XML passed in the reader.

After this operation there is a fully functional dataset document. It has three tables. The Class1/Aname and the anyType/anytype_text combinations contain the original field values of the object. The results may seem somewhat weird but these are just the default result using the InferSchema parameter when reading the serialized XML. Instead of inferring the schema, the data document class can load a schema from a schema file. You use the same dataset editor we met before to design a schema for your dataset. A dataset does not have to originate from a SQL related source at all. DataAdapters have a handy Fill method to fill a dataset but there are many other ways to get some content. The dataset does not care and has no notion of any underlying database at all. As all parts of .NET work with these XML datasets .NET is pretty database agnostic. It won’t even notice there is no database behind the dataset.

Updating database data

Using the dataAdapter the dataset is filled from the database. A dataAdapter is a collection of four sqlCommand components. Beside the select command there is the update- the insert- and the delete-command. The main properties of a sqlcommand are its CommandText to house the SQL statement and the Parameters collection to house the query’s parameters. The main methods of a Command-component are ExecuteReader and ExecuteNonQuery. The first one returns data read from the database and is used by the dataAdapter’s Fill method. Executing this method will execute one unidirectional read on the database without anything like a scrollable cursor. This makes it fast. Extremely fast is the ExecuteScalar method. This only returns one single value from the database, the first column of the first row. This makes a lot of sense when the query returns the summed total of a lot of data.

The ExecuteNonQuery method is used to send insert, update or delete SQL to the database. The method returns just the number of rows affected by the update. This method is used by the other three sqlCommand components of the Adapter. Instead of having to fire these commands yourself, you use the dataAdapters Update method. This takes, amongst its overloads, a complete dataset as a parameter. Let’s update the database from code:



(dd.DataSet as DataSetMD).TableMaster[0].aName

   = textBoxObjectName.Text;

sqlDataAdapterMaster.Update(dd.DataSet)


The first line of code casts the document’s dataset to the typed DataSetMD dataset. In this dataset the aName field of the first row of the TableMaster table is updated. The updated dataset is sent to the sqlDataAdapterMaster component, which will update the mastertable in the database.

Let’s take a closer look at the update SQL generated by the VS.NET wizard


UPDATE TableMaster

SET aName = @aName

WHERE (idMaster = @Original_idMaster)

  AND (aName = @Original_aName OR

    @Original_aName IS NULL AND aName IS NULL);

SELECT idMaster, aName

FROM TableMaster

WHERE (idMaster = @idMaster)



The first thing that catches the eye is the amount of named parameters. The query is asked to update the row where not only the id matches but also the original name matches the name. In this way the query checks if the original data has not changed since it was read from the database. The data was in a disconnected recordset but it still does communicate very well with its source.

The second thing catching the eye is the Select statement following the Update. The data is reread after writing. Why this makes sense is clearest in the generated Insert statement.



INSERT INTO TableMaster (aName)

VALUES (@aName);

SELECT idMaster, aName

FROM TableMaster

WHERE (idMaster = @@IDENTITY)



The dataset passed in only contains a value for the aName field. The id is generated by the database. The select statement will fill the dataset with this generated id. So after the Update method the id’s of the new rows are known. Likewise the new value of lookup fields is known in an updated row.

The dataset has to supply the queries with a lot of information. Besides the values of the fields their original values have to be preserved somewhere. And for every row the dataset has to keep track of its status. Has it been updated, inserted or even deleted ? All this is done by the dataset. When you browse the many properties of a DataRow you’ll see properties like RowState and via HasVersion a whole versioning system for the values in the row.

This complex state of a dataset can also been rendered as XML. This XML representation is called a diffgram. Let’s take a look at the diffgram representation of our data document. Obtaining this is a matter of passing a parameter to the WriteXML method of the dataset.



sw = new StringWriter();

tw = new XmlTextWriter(sw);

tw.Formatting = System.Xml.Formatting.Indented;

dd.DataSet.WriteXml(tw, XmlWriteMode.DiffGram);

textBoxDiffGram.Text = sw.ToString();



In the diffgram we can see all these properties of the dataset being rendered as diffgram attributes. The document is wrapped in a diffgram node which includes the namespaces: xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1” and xmlns:msdata="urn:schemas-microsoft-com:xml-msdata". The row nodes have diffgr and msdata qualified attributes that describe their state.



    diffgr:id="anyType1"

    msdata:rowOrder="0"

    diffgr:hasChanges="inserted"

    msdata:hiddenAlist_Id="0">

  Een

.



The xmldocument constructed by serializing the object also has a diffgram representation. In here all rows are marked as diffgr:hasChanges="inserted". The dataset has a method AcceptChanges, this will reset the state of all rows to unmodified. As that is the default value of a row no hasChanges attribute will be rendered in the XML of an unmodified row.

The database was updated by the dataAdapter from a dataset. A dataAdapter can only update one table of data, but a dataset can hold multiple tables of data. To map the tables in the dataset to the tables in the database, the dataAdapter has a TableMappings property. This is a collection of mappings between a database- and a dataset- table name. Each table mapping is a collection of column-mappings. In this collection database columns are mapped to dataset columns. The wizard has set these mappings when generating the adapter, but you can change the mappings in a property editor or from code. Let’s do the latter to update the database from the serialized object. The diffgram representation of the object holds one inserted row; this row should be added to the database as a new masters row.



DataSetMasters ds = new DataSetMasters();;

ds.Merge(dd.DataSet);

SqlDataAdapter da = sqlDataAdapterMaster;

da.TableMappings["Table"].DataSetTable = "Class1";

da.TableMappings["Table"].ColumnMappings["aName"].

  DataSetColumn = "Aname";

da.Update(ds);



You cannot fiddle with the mappings after a dataset is mapped to a loaded XML document, so a new DatasetMasters object is needed. It is filled from the data document using the Merge method. The table mappings of the dataAdapter are changed and after this the Update method will indeed insert a new row whose aName fields holds the Aname value of the original o2S object.

Note that this code is a bit destructive. Having changed the table mappings of the dataAdapter, that adapter will no longer understand how to fill a dataset from the database. The update method has several overloads, in one of them you can pass the name of the table mapping as a parameter. The name of the column to be mapped differs only in its casing. As these mappings are actually case insensitive the column mapping can be omitted and the code changes to:



DataSetMasters ds = new DataSetMasters();

ds.Merge(dd.DataSet);

sqlDataAdapterMaster.Update(ds, "Class1");



You still need a new dataset but the code no longer destroys other functionality.

What I hope to have demonstrated is that you can mix objects and database data to a very high degree in .NET. Everywhere there are possibilities to transform and map one into the other.

What’s next ?

This has been a highly concentrated journey through data in .NET. To summarize the main points:

·          There are several representations of data in .NET. Data can be in binary objects which you can code against, or data can be in XML documents which have a more verbose representation.

·          One representation can be transformed into another using streams and readers.

·          The physical implementation of these streams can vary from a local memory stream to a network stream over the Internet.

All code snippets are in the demo application. In this demo all is concentrated on one form. In a real world application you will separate at least the data from the presentation layer. All data components which interact with an (external) database should be on a component. The Component class is a general container with a somewhat misleading name. It is quite comparable to a Delphi datamodule. The form will keep the dataset components, they are somewhat comparable to the Delphi datasource component, the main difference being that a .NET dataset holds the actual data and a Delphi datasource is just a gateway to a Delphi dataset which contains the actual data. The .NET component and the form will interchange data in XML datasets. But this does not mean that the data is in a physical text format; that depends on the serializer used to transfer the data from component to form.

I have bombarded you with an enormous amount of information and still I have only been able to scratch the surface. There are many .NET things unmentioned. Several databases, including MS SQL Server, natively understand xml. The sqlCommand has a reader method that hooks into this. In the coming .NET 2 Whidbey version there will be wagonloads of new stuff. Yukon, the associated new version of SQL Server, runs native .NET code and a lot of the code presented here, including the XML docs, can even move into the database. Also new in Whidbey are object spaces, which directly map database data to classes. All material presented here is part of .NET 1. It is a base on which Whidbey builds further and you can use to do further exploration in the rich world of XML in .NET.




Commentaar van anderen:
ChristianLouboutin op 17-8-2010 om 4:53
Christian Louboutin Shoes, Christian Louboutin, Christian Louboutin Shoes, Wedding Shoes, Wedding Shoes, Louboutin Shoes, Christian Louboutin Discount copies of the mirror, so the angel of the practical activities of customers who activities. Christian Louboutin Evening, Manolo Blahnik Shoes, Christian, Louboutin, Christian Louboutin Sale, Louboutin Sale, Cheap Christian Louboutin Can you from your shoes, you give yourself into chargeless travel and bear your lifetime. Christian Louboutin Boots, Christian Louboutin Pumps, Christian Louboutin Sandals, Christian Louboutin Flats, Christian Louboutin Wedges, Christian Louboutin Sandals Do you agree to accept and hear the cases angled heel shoes forward or abstract, tear, buttons, and destruction, and the ability to reduce greenhouse gas emissions, it is exactly like daydreaming and shoes. Yves Saint Laurent Shoes, Christian Louboutin Boots, Manolo Blahnik Shoes, Yves Saint Laurent Boots, Miu Miu Shoes, Christian Dior Shoes this is an extra brand and shoes. However, you will never accept the amorous Louboutin christians through brand. Christian Louboutin Flats, Christian, Herve Leger V Neck Dress, Herve Leger Bandage Dress, Herve Leger Dress, Herve Leger V Neck Dress You can visit in your configuration of the life of their shoes were forgiven a brace, air brief you accept defeat them later anniversary, they are careful bag dust, shoe box that you have to go.
Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar