XSD Schemas: An Introduction

31. March 2003 15:35 by Chris in dev  //  Tags:   //   Comments (0)

Note that this article was first published on 02/01/2003. The original article is available on DotNetJohn.

Introduction

The XML Schema definition language (XSD) enables you to define the structure (elements and attributes) and data types for XML documents. It enables this in a way that conforms to the relevant W3C recommendations for XML schema. XSD is just one of several XML schema definition languages but is the one best supported by Microsoft in .NET.

The schema specifies the ordering of tags in the document, indicates fields that are mandatory or that may occur different numbers of times, gives the datatypes of fields and so on. The schema importantly is able to ensure that data values in the XML file are valid as far as the parent application is concerned.

Schemas are also useful when developers in different companies or even in different parts of the same company read and write XML documents that they will share. The schema acts as a contract specifying exactly what one application or part of an application must write into an XML file and another program can expect to be there. The schema unambiguously states the correct format for the shared XML.

A well formed XML document is one that satisfies the usual rules of XML. For example, in a well formed document there is exactly one data root node, all opening tags have corresponding closing tags, tag names do not contain spaces, the names in opening and closing tags are spelt in exactly the same way, tags are properly nested, etc.

A valid document is one that is well formed and that satisfies a schema.

Visual Basic .NET provide several methods for validating an XML document against a schema. There are articles on how to do this already on dotnetjohn ( Upload an XML File and Validate Against a Schema ). The focus of this article however shall be on the basic elements of the Microsoft preferred schema language (XSD), after a brief history lesson / an introduction to other common types of schema you may come across and why the XSD alternative was developed.

DTD and XDR

While XML is a relatively new technology the need for schemas was recognised early and so several have already been created. Microsoft focuses heavily on the most recent version, XSD, so VB has the most support for this form of schema, and hence will probably be the one Microsoft developers use most.

However, you may well happen upon the situation, particularly with enterprise development, where you are required to work with other forms of XML schema. While VB has few tools for building other types of schema, it can validate data using DTD and XDR.

The first schema standard was developed alongside XML v1.0 and is DTD (Document Type Definition) schemas. This, many believed, was not an ideal solution as a schema definition language which is why Microsoft came up with XSD as its own suggested replacement and submitted this to the W3C for consideration. One of the problems was, and is, that DTDs are not XML based so you have yet another language to learn to go with the proliferation that comes with XML (XPath and XSL for example). Further, developers also found that DTD lacked the power and flexibility they needed to completely define all of the datatypes they wanted to represent in XML. A schema that can’t validate all of the data’s requirements is of limited use.

XDR (XML Data Reduced) is another schema language, this time XML based and providing a superset of the functionality of DTDs. XDR should not be confused with Sun’s XDR (External Data Representation) … another format for data description but in this case physical representation of data rather than logical representation as per XML and XML Data Reduced schemas.

The last few paragraphs were just to let you know there are other schema formats out there, some of which have limited support in .NET. Now we’ll focus on XSD.

XSD

Wherever you see 'schema' from now we’re referring to XSD. As per many topics relating to XML (see my article on XSL Understanding How to Use XSL Transforms) the XSD specification is complex as well as being quickly evolving. The following will cover the basics of XSD so you can start to construct some useful schemas for use in your own applications. You’ll then need to follow up the information presented elsewhere.

Note that Visual Studio .NET includes an XSD editor that makes generating schemas relatively painless. Unless you understand some of the basic rules of XSD, however, the editor may prove a tad confusing.

Types and Elements

XSD schemas contain type definitions and elements. A type definition defines an allowed XML data type. An 'address' might be an example of a type you might want to define. An element represents an item created in the XML file. If the XML file contains an Address tag, then the XSD file will contain a corresponding element named Address. The data type of the Address element indicates the type of data allowed in the XML file’s Address tag.

Type definitions may be simple or complex. Simple and complex types allow definition of the new data types in addition to the 19 built in primitive data types which include string, Boolean, decimal, date, etc.

A simpleType allows a type definition for a value that can be used as the content of an element or attribute. This data type cannot contain elements or have attributes.

A complexType allows a type definition for elements that can contain attributes and elements.

Let’s pause here and take a look at an example. Let’s work backwards from an XML document as I’ll assume we’re all reasonably familiar with XML but less so with XSD. Here’s an XML file representing a simplified contacts database containing just one record currently:

 <?xml version="1.0" encoding="utf-8" ?>
 <Contacts>
   <Contact>
     <FirstName>Chris</FirstName>
     <Surname>Sully</Surname>
     <Address>
       <Street>22 Denton Road</Street>
       <City>Cardiff</City>
       <Country>Wales</Country>
     </Address>
     <Tel>02920371877</Tel>
   </Contact>
 </Contacts> 

In fact in Visual Studio .NET you can simply right click on this XML file and generate the schema from it. Of course it may well not be quite correct for your needs, as it shall be based on one record of data. Not even Visual Studio .NET can predict the future with any accuracy … ;) Here’s what it comes up with:

 <?xml version="1.0" ?>
 <xs:schema id="Contacts" targetNamespace="http://tempuri.org/XMLFile1.xsd" xmlns:mstns="http://tempuri.org/XMLFile1.xsd" xmlns="http://tempuri.org/XMLFile1.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified">
   <xs:element name="Contacts" msdata:IsDataSet="true" msdata:Locale="en-GB" msdata:EnforceConstraints="False">
     <xs:complexType>
       <xs:choice maxOccurs="unbounded">
         <xs:element name="Contact">
           <xs:complexType>
             <xs:sequence>
               <xs:element name="FirstName" type="xs:string" minOccurs="0" />
               <xs:element name="Surname" type="xs:string" minOccurs="0" />
               <xs:element name="Tel" type="xs:string" minOccurs="0" />
               <xs:element name="Address" minOccurs="0" maxOccurs="unbounded">
                 <xs:complexType>
                   <xs:sequence>
                     <xs:element name="Street" type="xs:string" minOccurs="0" />
                     <xs:element name="City" type="xs:string" minOccurs="0" />
                     <xs:element name="Country" type="xs:string" minOccurs="0" />
                   </xs:sequence>
                 </xs:complexType>
               </xs:element>
             </xs:sequence>
           </xs:complexType>
         </xs:element>
       </xs:choice>
     </xs:complexType>
   </xs:element>
 </xs:schema> 

Picking out some key elements:

An XML Schema is composed of the top-level schema element. The schema element definition must include the following namespace:

http://www.w3.org/2001/XMLSchema

and you can see from the above this isn’t all that is generated, but we’ll ignore the extra elements for now.

The actual definition commences with the first <xs:element… definition which has the name attribute 'Contacts'. Again the other attributes we can ignore for now. Contacts is necessarily defined as a complex type as it contains other elements.

We then encounter <xs:choice…: a choice element allows the XML file to contain one of the elements inside the choice element. The attribute maxOccurs="unbounded" is used to indicate that the Contacts element can contain any number of Contact elements.

The contact element is again a complex type comprised of a sequence of further elements. A sequence element requires the XML document to contain the items inside the sequence in order. By default sequence elements must appear exactly once; this can be overridden using the minOccurs and maxOccurs attributes to indicate it can occur any number of times (including 0).

The individual elements are defined to be of type string (a simple type). Address is similar defined as a complex type of a sequence of elements of type string.

Hopefully that has been an informative introduction to some commonly encountered constructs by way of an example. We’ll now continue on to look at some of the XSD language constructs in a little more detail, starting with elements.

Elements and their attributes

e.g. <xs:element name="Street" type="xs:string" minOccurs="0" />

An element defines an entity in an XML file. So the above defines an element of name <Street> and type string. The element can have several attributes which modify the elements behaviour, for example:

minOccurs and maxOccurs: as indicated already these give the minimum and maximum allowed number of times an element can occur within a complex type. To make an element optional minOccurs is set to 0. To allow an unlimited number of the element maxOccurs is set to 'unbounded'.

ref: makes the element a copy of another element defined in the schema. This is best avoided however... it is better to define a distinct type and base both element definitions on this type rather than introduce such dependencies into the schema, e.g.

 <xsd:simpleType name=”PhoneNumberType>
   <xsd:restriction base=”xsd:string/>
 </xsd:simpleType>
 ...
 <xsd:complexType name=”Contact>
   <xsd:sequence>
   ...
     <xsd:element name=”HomeTeltype=”PhoneNumberType/>
     <xsd:element name=”WorkTeltype=”PhoneNumberType/>
   ...
   <xsd:sequence>
 </xsd:complexType> 

Though you might at the same time like to tie down your definition of the PhoneNumberType more tightly. We’ll return to <xsd:restriction … shortly.

default: assigns a default value to the element in which case if the XML document omits the corresponding field it will be assumed to have this value. An element that has a default value should also have minOccurs set to 0 so the XML document may omit it.

fixed: gives the element an unchangeable value. The corresponding XML element cannot have another value, although it may be omitted if minOccurs is 0. Why is this useful? Well, you may want to ensure that an XML data field has the same value throughout the document; for example, you may want to add a new Country field to an existing XML document and ensure that its value is UK for every record.

Types

Type definitions have two goals:

  1. To describe the data allowed in a simple field, e.g. text format of an e-mail address. Simple types achieve this goal.
  2. To describe relationships amongst different fields, e.g. a contact type consists of a sequence of firstname, surname, telephone, etc. Complex types achieve this goal of designing more complex data types.

In addition to simple and complex types there are built in types, similar to simple data types such as integers, dates, etc. in other programming languages or .NET’s value data types provided by the Common Type System (CTS). We’ve seen one of the built in types already in our element definitions in the form of the often-employed string type. These built in types are W3C defined and include date, dateTime, decimal, double, float, Year, etc. etc. See the SDK documentation for an authoritative list.

A facet is a characteristic of a data type that you can use to restrict the values allowed by a type. Facets are effectively attributes of the data type. For example, the string datatype has a maxLength facet. Again for further details of the facets of each built in type see the SDK documentation.

Facets enable short cuts to building simple types by restricting another data type. We’ve already seen the restriction construct in example code above; using this and the enumeration facet of the string built in data type we can define allowable values for a type, e.g.

 <xsd:simpleType name=”Colours>
   <xsd:restriction base=”xsd:string>
     <xsd:enumeration value=”red/>
     <xsd:enumeration value=”green/>
     <xsd:enumeration value=”blue/>
   </xsd:restriction>
 </xsd:simpleType> 

The pattern facet is particularly powerful as it specifies a regular expression that the XML field data must match. Regular Expressions are worthy of an article or three in themselves, and there are several books on the subject if interested in improving your knowledge. Look out for an article on regular expressions on dotnetjohn in the not too distant future! For now, we’ll largely skip over the topic of regular expressions though here is an example:

 <xsd:simpleType name=”emailType>
   <xsd:restriction base=”xsd:string>
     <xsd:pattern value=”[^@]+@[^@]+\.[^@]+” />
   <xsd:restriction>
 <xsd:simpleType /> 

Let’s decipher ”[^@]+@[^@]+\.[^@]+” – this matches an e-mail address of the form a@b.c where a,b and c are any strings that do not contain the @ symbol. The value string equates to 'match any character other than the @ symbol one or more times; then match an @ symbol; then again match any character other than the @ symbol one or more times; next match a full stop and then once more any character other than the @ symbol one or more times'.

The use of the length, minLength, maxLength, totalDigits, fractionDigits, minExclusive, maxExclsuive, minInclusive, maxInclusive facets are all self-describing but it’s important to know they are available.

In addition to the primitive built in types there exist built in data types derived from these primitive types. These derived built in data types refine the definition of primitive types to create more restrictive types. They are based on the string and decimal primitive types.

The string derived types represent various entities that occur in XML syntax itself. For example, the Name type represents a string that satisfies the form of XML token names – it begins with a letter, underscore or colon and the rest of the string contains letters and digits.

The decimal derived types represent various kinds of numbers and thus are considerably more useful for validating data. There are thirteen such decimal derived types, e.g. byte, int, negativeInteger. See the SDK documentation for the full list.

Attributes

Just as you use an XSD schema’s element entities to define the data that can be contained in the corresponding XML data elements you can use attribute entities to define the attributes the XML element can have. Let’s return to Visual Studio .Net and see what schema it comes up with for the following small attribute-centric piece of XML.

 <contacts>
   <contact firstname=”ChrisSurname=”Sully/>
 </contacts> 

 

 <?xml version="1.0" ?>
 <xs:schema id="contacts" targetNamespace="http://tempuri.org/attribute_centric.xsd" xmlns:mstns="http://tempuri.org/attribute_centric.xsd" xmlns="http://tempuri.org/attribute_centric.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified">
   <xs:element name="contacts" msdata:IsDataSet="true" msdata:Locale="en-GB" msdata:EnforceConstraints="False">
     <xs:complexType>
       <xs:choice maxOccurs="unbounded">
         <xs:element name="contact">
           <xs:complexType>
             <xs:attribute name="firstname" form="unqualified" type="xs:string" />
             <xs:attribute name="Surname" form="unqualified" type="xs:string" />
           </xs:complexType>
         </xs:element>
       </xs:choice>
     </xs:complexType>
   </xs:element>
 </xs:schema> 

You can see that attributes equate to the

<xs:attribute name="firstname" form="unqualified" type="xs:string" />

construct. The form attribute of the attribute tag (yes that does make sense!) is set to unqualified. This means that the attributes in the XML file do not need to be qualified by the schema’s namespace.

Why use attributes rather than elements (referred to as attribute-centric and element-centric XML)? Well, they are often interchangeable and it is largely a matter of taste. Generally however, elements should contain data and attributes should contain information that describes the data. So for contacts one could recommend an attribute centric approach.

However, such decisions are mitigated by the following:

  • the attribute centric approach consumes less file space
  • attributes can specify default values whereas elements generally do not
  • you can order elements via the sequence construct; there is no method of enforcing order with attributes
  • elements can occur more than once in complex types but an attribute can occur only once

Complex Types

As previously stated, whereas a simple type determines the type of data a simple text field can hold, a complex type defines relationships among other types. For example we defined a contact record to include fields to store first name and surname. Simple types are then used to define the allowable values in the fields. The complex type determines the fields that make up the contact type.

Complex types are also useful for defining XML elements that can have attributes – simple types cannot have attributes. A complex type can contain only one of a small number of elements. The elements within that element define the relationship the complex type represents. The most common elements are simpleContent, sequence, choice and all, as follows:

simpleContent: a complex type that contains a simpleContent element must contain only character data or a simple type. This construct is primarily so one may add attributes to a simple type.

sequence: as we’ve seen this allows specification of a required order to elements of a complexType.

choice: again as we’ve seen the corresponding XML data must include exactly one of the elements listed inside the choice. Note this is entirely different from the enumeration facet previously introduced: rather than a fixed set of values the choice construct allows the complex type to contain one of several types.

all: when a complex type includes the all element the corresponding XML data can include some or all of the listed elements in any order.

Named and Unnamed Types

Finally, to finish off our overview: if you will use a type only once there is no need to give it a name as you will not need to reference it again. You may choose to include the type definition in the code which uses it. This is the case for the Visual Studio generated schemas above, e.g.:

 <xs:complexType>
   <xs:choice maxOccurs="unbounded">
     <xs:element name="contact">
       <xs:complexType>
         <xs:attribute name="firstname" form="unqualified" type="xs:string" />
         <xs:attribute name="Surname" form="unqualified" type="xs:string" />
       </xs:complexType>
     </xs:element>
   </xs:choice>
 </xs:complexType> 

If the schema referenced this complexType again it would be more succinct to add a name attribute to the <xs:complexType … definition so you could reference it again later in the schema definition.

To clarify by example, the following uses the email simple type we introduced earlier to reduce the size of an ‘EmailContactType’ definition:

 <xsd:simpleType name=”emailType>
   <xsd:restriction base=”xsd:string>
     <xsd:pattern value=”[^@]+@[^@]+\.[^@]+” />
   <xsd:restriction>
 <xsd:simpleType />
 
 <xsd:complexType name=”emailContactType>
   <xsd:sequence>
     <xsd:element name=”nametype=”xsd:string/>
     <xsd:element name=”emailtype=”emailType/>
   </xsd:sequence>
 </xsd:complexType> 

Alternatively you could have defined the email type within the email element. Whether reusing or not, employing this convention will generally make your code tidier.

Conclusion

There we shall halt our introduction to XML Schemas and to the basic XSD constructs specifically and hope you are better placed to understand why and how to use XML schemas in .NET

References

.NET Framework SDK documentation

Visual Basic .Net and XML
Stephens and Hochgurtel

Programming Visual Basic .NET
Francesco Balena
Microsoft Press

Error Handling in ASP.NET

30. March 2003 13:40 by Chris in dev  //  Tags: ,   //   Comments (0)

Introduction

Note that this article was first published on 30/03/2003. The original article is available on DotNetJohn.

I came to the realisation a little time ago that I really wasn’t making the most of the error handling facilities of ASP.NET. The sum of my ASP.Net error handling knowledge up until that point was the new (to VB) Try … Catch … Finally construct at the page level. While I shall examine this new addition there are more facilities at our disposal and this article shall share the results of my recent investigations.

In anything but the simplest of cases your application WILL contain errors. You should identify where errors might be likely to occur and code to anticipate and handle them gracefully.

The .NET Framework’s Common Language Runtime (CLR) implements exception handling as one of its fundamental features. As you might expect due to the language independence of the framework you can also write error handling in VB.NET that can handle errors in C# code, for example. The exception object thrown is of the same primitive type (System.Exception).

Options … Options (preventing runtime errors)

If we can we should capture errors as early as possible as fewer will then make it through to the runtime environment. VB.Net offers the Option Strict and Option Explicit statements to prevent errors at design time.

Classic ASP programmers should be familiar with Option Explicit – it forces explicit declaration of all variables at the module level. In addition when programming ASP.NET pages in VB.NET when Option Explicit is enabled, you must declare all variables using Public, Private, Dim or Redim.

The obvious mistake that Option Explicit captures is using the same variable name multiple times within the same scope … a situation which is very likely to lead to runtime errors, if not exceptions.

Thankfully, in ASP.NET Option Explicit is set to on by default. If for any reason you did need to reset, the syntax is:

 Option Explicit Off

at the top of a code module or

 <%@ Page Explicit=”False”%> 

in a web form.

Enabling Option Strict causes errors to be raised if you attempt a data type conversion that leads to a loss in data. Thus, if this lost data is of potential importance to your application Option Strict should be enabled. Option Strict is said to only allow ‘widening’ conversions, where the target data type is able to accommodate a greater amount of data than that being converted from.

The syntax is as per Option Explicit.

Exceptions

What precisely is an exception? The exception class is a member of the System namespace and is the base class for all exceptions. Its two sub-classes are the SystemException class and the ApplicationException class.

The SystemException class defines the base class for all .NET predefined exceptions. One I commonly encounter is the SQLException class, typically when I haven’t quite specified my stored procedure parameters correctly in line with the stored procedure itself.

When an exception object is thrown that is derived from the System.Exception class you can obtain information from it regarding the exception that occurred. For example, the following properties are exposed:

HelpLink Gets or sets a link to the help file associated with this exception.
InnerException Gets the Exception instance that caused the current exception.
Message Gets a message that describes the current exception.
Source Gets or sets the name of the application or the object that causes the error.
StackTrace Gets a string representation of the frames on the call stack at the time the current exception was thrown.
TargetSite Gets the method that throws the current exception.

See the SDK documentation for more information.

The ApplicationException class allows you to define your own exceptions. It contains all the same properties and methods as the SystemException class. We shall return to creating such custom exceptions after examining the main error handling construct at our disposal: 'Try … Catch … Finally'.

Structured and Unstructured Error Handling

Previous versions of VB had only unstructured error handling. This is the method of using a single error handler within a method that catches all exceptions. It’s messy and limited. Briefly, VB’s unstructured error handlers (still supported) are:

On Error GoTo line[or]label
On Error Resume Next
On Error GoTo 0
On Error GoTo -1

But forget about these as we now have new and improved C++ like structured exception handling with the Try ... Catch … Finally construct, as follows:

Try
[ tryStatements ]
[ Catch [ exception [ As type ] ] [ When expression ]
[ catchStatements ] ]
[ Exit Try ]
...
[ Finally
[ finallyStatements ] ]
End Try

Thus we try to execute some code; if this code raises an exception the runtime will check to see if the exception is handled by any of the Catch blocks in order. Finally we may execute some cleanup code, as appropriate. Exit Try optionally allows us to break out of the construct and continue executing code after End Try. When optionally allows specification of an additional condition which must evaluate to true for the Catch block to be executed.

Here’s my code snippet for SQL server operations by way of a simple, and not particularly good (see later comments), example:

 Try
 
   myConnection.open()
   myCommand = new SQLCommand("USP_GENERIC_select_event_dates", myConnection)
   myCommand.CommandType = CommandType.StoredProcedure
 
   myCommand.Parameters.Add(New SQLParameter("@EventId",SQLDBType.int))
   myCommand.Parameters("@EventId").value=EventId
 
   objDataReader=myCommand.ExecuteReader()
 
 Catch objError As Exception
 
   'display error details
   outError.InnerHtml = "<b>* Error while executing data command (ADMIN: Select Event Dates)</b>.<br />" _
   & objError.Message & "<br />" & objError.Source & _
   ". Please <a href='mailto:mascymru@cymru-web.net'>e-mail us</a> providing as much detail as possible including the error message, what page you were viewing and what you were trying to achieve.<p /><p />"
 
   Exit Function ' and stop execution
 
 End Try 

There are several problems with this code as far as best practice is concerned, the more general of which I’ll leave to the reader to pick up from the following text, but in particular there should be a Finally section which tidies up the database objects.

Note it is good form to have multiple Catch blocks to catch different types of possible exceptions. The order of the Catch blocks affects the possible outcome … they are checked in order.

You can also throw your own exceptions for the construct to deal with; or re-throw existing exceptions so they are dealt with elsewhere. See the next section for a little more detail.

You could just have a Catch block that trapped general exceptions – exception type ‘exception’ (see above!). This is not recommended as it suggests a laziness to consider likely errors. The initial catch blocks should be for possible specific errors with a general exception catch block as a last resort if not covered by earlier blocks.

For example, if accessing SQLServer you know that a SQLException is possible. If you know an object may return a null value and will cause an exception, you can handle it gracefully by writing a specific catch statement for a NullReferenceException.

For you information here’s a list of the predefined exception types provided by the .NET Runtime:

Exception typeBase typeDescriptionExample
Exception Object Base class for all exceptions. None (use a derived class of this exception).
SystemException Exception Base class for all runtime-generated errors. None (use a derived class of this exception).
IndexOutOfRangeException SystemException Thrown by the runtime only when an array is indexed improperly. Indexing an array outside its valid range: arr[arr.Length+1]
NullReferenceException SystemException Thrown by the runtime only when a null object is referenced. object o = null; o.ToString();
InvalidOperationException SystemException Thrown by methods when in an invalid state. Calling Enumerator.GetNext() after removing an Item from the underlying collection.
ArgumentException SystemException Base class for all argument exceptions. None (use a derived class of this exception).
ArgumentNullException ArgumentException Thrown by methods that do not allow an argument to be null. String s = null; "Calculate".IndexOf (s);
ArgumentOutOfRangeException ArgumentException Thrown by methods that verify that arguments are in a given range. String s = "string"; s.Chars[9];
ExternalException SystemException Base class for exceptions that occur or are targeted at environments outside the runtime. None (use a derived class of this exception).
ComException ExternalException Exception encapsulating COM HRESULT information. Used in COM interop.
SEHException ExternalException Exception encapsulating Win32 structured exception handling information. Used in unmanaged code interop.

Throwing Exceptions

As indicated earlier, not only can you react to raised exceptions, you can throw exceptions too when needed. For example, you may wish to re-throw an exception after catching it and not being able to recover from the exception. Your application-level error handling could then redirect to an appropriate error page.

You may further wish to throw your own custom exceptions in reaction to error conditions in your code.

The syntax is

Throw new [Exception]

Creating Custom Exceptions

As mentioned earlier, via the ApplicationException class you have the ability to create your own exception types.

Here’s an example VB class to do just that:

 Imports System
 Imports System.Text
 
 Namespace CustomExceptions
 
   Public Class customException1: Inherits ApplicationException
 
     Public Sub New()
 
       MyBase.New("<H4>Custom Exception</H4><BR>")
       Dim strBuild As New StringBuilder()
       strBuild.Append("<p COLOR='RED'>")
       strBuild.Append("For more information ")
       strBuild.Append("please visit: ")
       strBuild.Append("<a href='http://www.cymru-web.net/exceptions'>")
       strBuild.Append("Cymru-Web.net</a></p>")
       MyBase.HelpLink = strBuild.ToString()
 
     End Sub
 
   End Class
 
 End Namespace 

Looking at this code. On line 6 you see that to create a custom exception we must inherit from the ApplicationException class. In the initialization code for the class we construct a new ApplicationException object using a string (one of the overloaded constructors of ApplicationException – see the .NET documentation for details of the others). We also set the HelpLink property string for the exception – this is a user friendly message for presentation to any client application.

The MyBase keyword behaves like an object variable referring to the base class of the current instance of a class (ApplicationException). MyBase is commonly used to access base class members that are overridden or shadowed in a derived class. In particular, MyBase.New is used to explicitly call a base class constructor from a derived class constructor.

Next a small test client, written in VB.NET using Visual Studio.Net so we have both web form and code behind files:

WebForm1.aspx:

 <%@ Page Language="vb" AutoEventWireup="false" Codebehind="WebForm1.aspx.vb" Inherits="article_error_handling.WebForm1"%>
 <html>
 <body>
 </body>
 </html>

WebForm1.aspx.vb:

 Imports article_error_handling.CustomExceptions
 
 Public Class WebForm1
 
   Inherits System.Web.UI.Page
 
   Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
     Try
       Throw New customException1()
     Catch ex As customException1
       Response.Write(ex.Message & " " & ex.HelpLink)
     End Try
   End Sub
 
 End Class 

This (admittedly simple) example you can extend to your own requirements.

Page Level Error Handling

Two facets:

  1. Page redirection
  2. Using the Page objects error event to capture exceptions

Taking each in turn:

Page redirection

Unforeseen errors can be trapped with the ErrorPage property of the Page object. This allows definition of a redirection URL in case of unhandled exceptions. A second step is required to enable such error trapping however – setting of the customErrors attribute of your web.config file, as follows:

 <configuration>
   <system.web>
     <customErrors mode="On">
     </customErrors>
   </system.web>
 </configuration> 

Then you’ll get your page level redirection rather than the page itself returning an error, so the following would work:

 <%@ Page ErrorPage="http://www.cymru-web.net/GenericError.htm" %> 

A useful option in addition to ‘on’ and ‘off’ for the mode attribute of customErrors is ‘RemoteOnly’ as when specified redirection will only occur if the browser application is running on a remote computer. This allows those with access to the local computer to continue to see the actual errors raised.

Page Objects Error Event

The Page object has an error event that is fired when an unhandled exception occurs in the page. It is not fired if the exception is handled in the code for the page. The relevant sub is Page_Error which you can use in your page as illustrated by the following code snippet:

 Sub Page_Error(sender as Object, e as EventArgs)
 
   dim PageException as string = Server.GetLastError().ToString()
   dim strBuild as new StringBuilder()
   strBuild.Append("Exception!")
   strBuild.Append(PageException)
 
   Response.Write(strBuild.ToString())
   Context.ClearError()
 
 End Sub 

As with page redirection this allows you to handle unexpected errors in a more user-friendly fashion. We’ll return to explain some of the classes used in the snippet above when we look at a similar scenario at the application level in the next section.

Application Level Error Handling

In reality you are more likely to use application level error handling rather than the page level just introduced. The Application_Error event of the global.asax exists for this purpose. Unsurprisingly, this is fired when an exception occurs in the corresponding web application.

In the global.asx you code against the event as follows:

 Sub Application_Error(sender as Object, e as EventArgs)
   'Do Something
 End Sub 

Unfortunately the EventArgs in this instance are unlikely to be sufficiently informative but there are alternative avenues including that introduced in the code of the last section – the HttpServerUtility.GetLastError method which returns a reference to the last error thrown in the application. This can be used as follows:

 Sub Application_Error(sender as Object, e as EventArgs)
 
   dim LastException as string = Server.GetLastError().ToString()
 
   Context.ClearError()
 
   Response.Write(LastException)
 
 End Sub 

Note that the ClearError method of the Context class clears all exceptions from the current request – if you don’t clear it the normal exception processing and consequent presentation will still occur.

Alternatively there is the HttpContext’s class Error property which returns a reference to the first exception thrown for the current HTTP request/ response. An example:

 Sub Application_Error(sender as Object, e as EventArgs)
 
   dim LastException as string = Context.Error.ToString()
 
   Context.ClearError()
 
   Response.Redirect("CustomErrors.aspx?Err=" & Server.UrlEncode(LastException))
 
 End Sub 

This illustrates one method for handling application errors – redirection to a custom error page which can then customise the output to the user dependent on the actual error received.

Finally, we also have the ability to implement an application wide error page redirect, via the customErrors section of web.config already introduced, using the defaultRedirect property:

 <configuration>
   <system.web>
     <customErrors mode="on" defaultRedirect="customerrors.aspx?err=Unspecified">
     <error statusCode="404" redirect="customerrors.aspx?err=File+Not+Found"/>
     </customErrors>
   </system.web>
 </configuration> 

Note this also demonstrates customised redirection via the error attribute. The HttpStatusCode enumeration holds the possible values of statusCode. This is too long a list to present here - see the SDK documentation.

Conclusion

I hope this has provided a useful introduction to the error handling facilities of ASP.NET. I further hope you’ll now go away and produce better code that makes use of these facilities! In summary:

  • Trap possible errors at design time via Option Explicit and Option Strict.
  • Consider in detail the possible errors that could occur in your application.
  • Use the Try … Catch … Finally construct to trap these errors at the page level and elsewhere.
  • It is generally considered poor programming practice to use exceptions for anything except unusual but anticipated problems that are beyond your programmatic control (such as losing a network connection). Exceptions should not be used to handle programming bugs!
  • Use application level exception handling (and perhaps also Page level) to trap foreseen and unforeseen errors in a user friendly way.

References

ASP.NET: Tips, Tutorial and Code
Scott Mitchell et al.
Sams

Programming Visual Basic .NET
Francesco Balena
Microsoft Press

.NET SDK Documentation

http://15seconds.com/issue/030102.htm 
15 Seconds : Web Application Error Handling in ASP.NET
 

http://msdn.microsoft.com/msdnmag/issues/02/11/NETExceptions/default.aspx
.NET Exceptions: Make the Transition from Traditional Visual Basic Error Handling to the Object-Oriented Model in .NET

State Management in ASP.NET

3. March 2003 15:31 by Chris in dev  //  Tags:   //   Comments (0)

Note that this article was first published on 02/01/2003. The original article is available on DotNetJohn.

Introduction

The web is a stateless medium – state is not maintained between client requests by default. Technologies must be utilized to provide some form of state management if this is what is required of your application, which will be the case for all but the simplest of web applications. ASP.NET provides several mechanisms to manage state in a more powerful and easier to utilize way than classic ASP. It is these mechanisms that are the subject matter for this article.

Page Level State - ViewState

Page level state is information maintained when an element on the web form page causes a subsequent request to the server for the same page – referred to as ‘postback’. This is appropriately called ViewState as the data involved is usually, though not necessarily, shown to the user directly within the page output.

The Control.ViewState property provides a dictionary object for retaining values between such multiple requests for the same page. This is the method that the page uses to preserve page and control property values between round trips.

When the page is processed, the current state of the page and controls is hashed into a string and saved in the page as a hidden field. When the page is posted back to the server, the page parses the view state string at page initialization and restores property information in the page.

ViewState is enabled by default so if you view a web form page in your browser you will see a line similar to the following near the form definition in your rendered HTML:

 <input type="hidden" name="__VIEWSTATE"
 value="dDwxNDg5OTk5MzM7Oz7DblWpxMjE3ATl4Jx621QnCmJ2VQ==" /> 

When a page is re-loaded two methods pertaining to ViewState are called: LoadViewState and SaveViewState. Page level state is maintained automatically by ASP.NET but you can disable it, as necessary, by setting the EnableViewState property to false for either the controls whose state doesn’t need to be maintained or for the page as a whole. For the control:

 <asp:TextBox id=”tbNamerunat=”serverEnableViewState=”false/> 

for the page:

 <%@ Page EnableViewState=”false” %> 

You can validate that these work as claimed by analyzing the information presented if you turn on tracing for a page containing the above elements. You will see that on postback, and assuming ViewState is enabled, that the LoadViewState method is executed after the Page class’ Init method has been completed. SaveViewState is called after PreRender and prior to actual page rendering.

You can also explicitly save information in the ViewState using the State Bag dictionary collection, accessed as follows:

 ViewState(key) = value 

Which can then be accessed as follows:

 Value = ViewState(key) 

It is important to remember that page level state is only maintained between consecutive accesses to the same page. When you visit another page the information will not be accessible via the methods above. For this we need to look at other methods and objects for storing state information.

Session Level State

What is a user session? Slightly simplifying it’s the interaction between a user’s first request for a page from a site until the user leaves the site again. Now what if you login into this site at your first request, assuming this is a site you have previously registered with. How does the application remember who you are for the rest of the ‘session’? Or if you have items in your shopping cart within an e-commerce web application how does the application ‘remember’ this information when you request to go to the checkout?

The answer may well be session state though the underlying mechanism by which this is achieved may be one of several options. ASP.NET creates a session (it reserves a section of memory) for a user when they first arrive at the site and assigns that user session a unique id that is tied to that section of memory. By default, ASP.NET then creates a cookie on the client that contains this same id. As this id will be sent with any subsequent http requests to this server ASP.NET will be able to match the user against the reserved section of memory. Further the application can store data related to that user session in this reserved memory space to maintain state between requests. It must be remembered that using session state is using server resources for every user of the site so you need to consider the resources required of items you choose to store.

An example:

 session(“name”)=”Chris Sully” 

sets a session variable with key ‘name’ and value “Chris Sully”. To retrieve/ display this we use:

 lblUserName.text=session(“name”) 

In this case assigning to the text property of a label web server control.

By default the resources associated with this session state maintenance are released if the user does not interact with the site for 20 minutes. If the user returns after a 20 minutes break a new session will have been created and any data associated with their previous session will have been lost. However, you can also destroy the session information for a user earlier within the application if so desired via

 Session.Abandon 

and you may also change the default value from 20 minutes. To redefine the session timeout property you use:

 Session.Timeout = 5 

Alternatively, and representing a more likely scenario, you would specify the value in your web.config file:

 <configuration>
   <system.web>
     <sessionState timeout=10 />
   </system.web>
 </configuration> 

which would half the default timeout from 20 to 10 minutes.

We'll return to some of the other sessionState properties in subsequent sections.

Looking at a little more detail at the session initialization process:

  1. User makes a request of the server
  2. ASP.NET retrieves the user’s sessionID via the cookie value passed in the HTTP request from the client computer. If one does not exist ASP.NET creates one, and raises the Session_OnStart event (which can be reacted to in the global.asax, amongst other locations).
  3. ASP.NET retrieves any data relating to the sessionID from a session data store. This data store may be of a variety of types, as we shall explore in subsequent sections.
  4. A System.Web.SessionState.SessionState object is created and populated with the data from the previous step. This is the object you access when using the shortcut session(“name”)=”Chris Sully”

There is also a Session_OnEnd which you can code against in your global.asax.

SQLServer

We can use SQLServer to store our session state. We can use other databases if we want to but ASP.NET includes built in support for SQLServer as well as other data stores that make life easier for developers. As you might expect, SQLServer should be the choice for storage of session information in high end, performance critical web applications.

To enable session state storage with SQLServer we'll need the infrastructure to support this, i.e. the tables and stored procedures. Microsoft has supplied these and they are located at

C:\winnt\Microsoft.net/framework/[version directory]

On my system:

C:\winnt\Microsoft.net/framework/v1.0.3705/InstallSQLState.sql

And you also have the TSQL to remove the setup: UninstallSQLState.sql.

So, to enable SQLServer session support open up and execute InstallSQLState.sql in query analyzer. If you then investigate what’s new in your SQLServer setup you will see a new database named AspState with 15 or so stored procedures used to insert and retrieve the associated session data. You won’t see any new tables! However, if you expand the tempdb database and view the tables therein, you will see two new tables: ASPStateTempApplications and ASPStateTempSessions which is where our session state information shall be held.

Now, all we need to do, as ASP.NET takes care of everything else for us, is modify web.config so that the ASP.Net application knows it should be using SQLServer for session state management:

 <configuration>
   <system.web>
     <sessionState mode=”sqlserversqlConnectionString=”connectionString/>
   </system.web>
 </configuration> 

where you should replace connectionString with that for you own machine.

If you want to test this, create a simple page which adds an item to the session state. This can be as simple as assigning a value to a session variable a la: session(“name”)=”Chris Sully”, perhaps simply placing this in the OnLoad sub of an otherwise blank aspx. View this in your browser. Don’t close the browser window after the page is loaded as you’ll end the session. Remember that after the first request by default the session will last 20 minutes.

If you now examine the contents of the ASPStateTempApplications table in tempdb either with Enterprise Manager or Query Analyser, you will see an entry corresponding to the above set session variable.

The other possibly important consideration is that the session data is stored external to the ASP.Net process. Hence, even if we restart the web server the information will remain available to subsequent user requests. We’ll look at another mechanism for such data isolation shortly.

Cookies

We’ve already introduced that cookies are central to the concept of session – they are the client half of how session state is maintained over browser requests. As well as using them for user identification purposes we can also use them to store custom session information. Cookie functionality is exposed via the HttpCookie class in .NET. This functionality may also be accessed via the Response and Request ASP.NET classes.

Cookies used in this way don’t tie in strongly with the inbuilt state management capabilities of .NET but they can provide useful custom session state management functionality.

To add a cookie (store on the client browser machine) you use the response object, e.g.:

 response.cookies(“ExampleCookie”)(“Time”)= datetime.now.tostring(“F”) 

i.e.,

 response.cookies(“CookieName”)(“keyName”) = value 

‘F’ refers to the Full date/time pattern (long time) by the way.

To retrieve a cookie you use the request object. The cookie is associated with the server URL and hence will be automatically appended to the request parameter collection and hence accessible as follows:

 TimeCookieSet = Request.Cookies(“ExampleCookie”)(“Time”) 

There are other options available. For example, you may set the Expires property of the cookie. This may be a fixed date or a length of time from the present date:

 Response.cookies(“ExampleCookie”).Expires = DateTime.FromString(“12/12/2003”)
 
 Response.cookies(“ExampleCookie”).Expires = DateTime.Now.AddMonths(6) 

Cookie Munging

You may wish to configure your applications to use session state without relying on cookies. There could be several reasons for this:

  1. You need to support old browser types that do not support cookies.
  2. You wish to cater for people who have chosen to disable cookie support within their browser.
  3. Certain types of domain name redirection mean that cookies / conventional state management will not work.

Cookie munging causes information regarding the session state id to be added to URL information. Thus the link between client and session data on the server is maintained.

It is simply enabled, as follows:

 <configuration>
   <system.web>
     <sessionState cookieless=”true/>
   </system.web>
 </configuration> 

If you change your web.config file to the above and then view a page which uses both the session object and postback you’ll see ASP.Net has inserted some extra information in the URL of the page. This extra information represents the session ID.

Cookie munging certainly does the job in instances where it is required but should be avoided where it is not, as it is insecure being susceptible to manual manipulation of the URL string.

Session State Server

Session State Server runs independently of ASP.Net, the current application, web server and (possibly) the server meaning you have good isolation of data and hence if there is a problem with the web server you may still be able to recover the user session data. This without the need for SQLServer. The State Server also presents a number of extra facilities including the choice of where and how to deploy the facility.

You can run the StateServer in the same process as the ASP.Net application (“InProc” – the default). This is similar to how classic ASP managed session state. Why would we want to do this when we have just lauded the benefits of data isolation? Performance is the answer – data stored in the same process is accessed quickly.

You can test this via restarting IIS (iisreset) when you know you have some session data in your application. You could also try restarting the ASP.NET application via the MMC snap-in, the effect is the same. This is achieved by removing and recreating the IIS application (right click-properties on the application sub-directory/ virtual directory).

A couple of simple test scripts would be:

1:

 <configuration>
   <system.web>
     <sessionState cookieless=”true/>
   </system.web>
 </configuration><%@ Page Language="VB" %>
 <html>
 <head>
 </head>
 <body>
   <form runat="server" ID="Form1">
   <% session("test")="test" %>
   </form>
 </body>
 </html>  

2:

 <%@ Page Language="VB" %>
 <html>
 <head>
 </head>
 <body>
   <form runat="server" ID="Form2">
   <%=session("test")%>
   </form>
 </body>
 </html> 

So, if you run 1, then 2 directly after without closing the browser window, the value will be maintained and you’ll see ‘test’ displayed. If you reset IIS or the IIS application in between the browser requests session information will be lost.

You can also run State Server out of process (“Out-of-Proc”) which means that session state information can be stored in alternative locations to the ASP.NET process space. Hence, if there is a problem with the application or web server state is maintained. In this scenario, the ASPNETState process (aspnet_state.exe) is used, which runs as a service, storing session data in memory.

First thing we need to do therefore is make sure this service is up and running. This can be done via the command line:

Net start aspnet_state

Though if you’re going to this out of process support for an application you will want to setup the service to start on machine boot up (admin tools – services).

Next it’s back to that Web.Config file to tell the application to use the facility:

 <configuration>
   <system.web>
     <sessionState mode=”stateserverstateConnectionString=”tcpip=127.0.0.1:80/>
   </system.web>
 </configuration> 

In actual fact the stateConnectionString attribute is not required when pointing to a port on the local machine, as it is above (which supplies the default setting), but is important if you wish to use another machine and port for extra security/ reliability. It is included to demonstrate the syntax.

If you now go back and try the session maintenance test you won’t lose that session data.

Application level state

Using Application state is similar to using session state: the HttpApplicationState class contains an Application property that provides dictionary access to variables:

 Application(“key”) = value 

The difference with session state is that data is stored per IIS application as opposed to per user session. Thus if you set up:

 application(“name”)=”Chris Sully” 

in your global.asax file for the application this value is available to all pages of your application and will be the same value for all users who visit.

Setting state information is not limited to global.asax – any page of the application being run by any user can do this. There is an implication here – multiple users trying to change the value of an application variable could lead to data inconsistencies. To prevent this the HttpApplicationState class provides the lock method that ensures only one user (actually a process thread) is accessing the application state object at any one time. Thus you should lock before amending an application value and unlock immediately afterwards so others have access to the variable.

Other options for state management: caching

Another method of storing data on the server is via the cache class. This effectively extends the capabilities of storing data at the application level. Frequently used data items may be stored in the cache memory for presentation. For example, data for a drop down list may be stored in a cached object so that the cached copy is used rather than obtaining the data from the database on every occasion.

You may store your objects in cache memory and set the properties of the cache to control when the cache might release its resources. For example, you might use the TimeSpan property that specifies how long the item should remain in the cache after it is last accessed. You can also specify a dependency of the cached item on a datasource, for example an XML file. If this file is changed the chance can be programmed to react to this event accordingly and update the cached object.

For further information on this subject see my article on caching on dotnetjohn.

Conclusion

I hope this article has provided a useful overview of the state management options and support in .NET. With all the forms of state management there exists a balance between the needs of the application and the associated resources used.

In particular, be aware that page level state is enabled by default and should be disabled if not required, particularly if you are manipulating significant amounts of data within DataBound controls. As well as using server resources, leaving ViewState enabled in such a situation will increased the page size, and hence download times, significantly. In such a situation it may be better to cache the data on the server for the postback rather than transmit the data in the ViewState, or even rely on the data caching facilities of your chosen DBMS.

ASP.Net provides significantly extended support for session state maintenance via SQLServer and Session State Server. The choice is down to the needs of your application and, in particular, how important data isolation from your IIS application is.

References

.NET Framework SDK documentation

ASP.NET: Tips, Tutorials and Code Sams Mitchell et al.

About the author

I am Dr Christopher Sully (MCPD, MCSD) and I am a Cardiff, UK based IT Consultant/ Developer and have been involved in the industry since 1996 though I started programming considerably earlier than that. During the intervening period I've worked mainly on web application projects utilising Microsoft products and technologies: principally ASP.NET and SQL Server and working on all phases of the project lifecycle. If you might like to utilise some of the aforementioned experience I would strongly recommend that you contact me. I am also trying to improve my Welsh so am likely to blog about this as well as IT matters.

Month List