Click here to Skip to main content
15,895,142 members
Articles / Programming Languages / Visual Basic
Article

.NET Data Access Class for Exchange 2000 Webstore

Rate me:
Please Sign up or sign in to vote.
4.82/5 (27 votes)
16 Aug 2005LGPL36 min read 686K   2.4K   64   342
Assembly written in VB.NET that accesses Exchange 2000 Webstore data, using WEBDAV.

Important Update:

The XSLT files that were previously provided online at webservices.bus.oregonstate.edu are no longer going to be available. Instead you will need to download them and change the lines as described in the Updates section below. Please see the Updates section!

Introduction

The ExDAV assembly is a collection of classes that are useful for querying and authoring against a Microsoft Exchange 2000 webstore. This is accomplished within the classes by communicating with the store, using WebDAV. The classes wrap all the communications so that the programmer using the classes doesn't have to implement the WebDAV calls manually. The assembly provides the ability to do SQL like searches using the Exchange 2000 'SEARCH' verb, as well as the standard WebDAV verbs like 'PROPFIND'. Search results can be returned in raw XML form, as an XML.NET XML document, as ADO.NET DataSets, and, in the case of a 'PROPFIND', as a recursive object which is included in the assembly. In terms of authoring, the assembly provides the ability to write properties, create items and collections, copy, move, and delete.

Background

This object was created because of our need to access Exchange 2000 webstore data from our .NET applications. Our intent was to create a set of classes that would allow us to work with the Exchange data, without having to do any messy wrapping of COM components.

Using the code

To use the assembly, there are three classes that are the most important: ExSearcher, ExModifier and ExResponse. The ExSearcher class is used to query the Exchange Store and has two methods for doing this, Search and Find. The Search method takes a string parameter sWhere which is a SQL WHERE clause that is used to filter the results of the search. The request sent to the Exchange Store is done so in a SQL format (code snippets are in C#).

C#
ExDAV.ExSearcher searcher = new 
        ExDav.ExSearcher("http://mystore.edu/myresource");
searcher.Depth = ExDAV.ExRequest.ExRequestDepths.AllChildrenWithRoot;
searcher.ExProps.Add("DAV:", "creationdate");
ExDAV.ExResponse resp = searcher.Search("WHERE \"DAV:isfolder\" = true");
...

I'll get to the ExResponse class in a minute.

Important Note: for both the ExSearcher and the ExModifier, properties which are to be retrieved or modified must be added to the ExProps collection which is a collection of ExProperty objects. Properties are specified using a URN, a Local Name, and in the case of an update, a Value. The code below shows how the DAV:creationdate property is set to be retrieved on our searcher (URN = 'DAV:', Local Name = 'creationdate'):

C#
...
searcher.ExProps.Add("DAV:", "creationdate");
...

The Find method performs a similar function except with no SQL WHERE clause filter. The request sent to the Exchange Store is a typical 'PROPFIND' WebDAV call (here too we need to add the properties we are 'Finding', into the ExProps collection).

C#
ExDAV.ExSearcher searcher = new 
        ExDav.ExSearcher("http://mystore.edu/myresource");
searcher.Depth = ExDAV.ExRequest.ExRequestDepths.AllChildrenWithRoot;
//Add the properties to look for
searcher.ExProps.Add("DAV:", "creationdate");
ExDAV.ExResponse resp = searcher.Find();
...

So now to get back to the ExResponse class. This class is how the response from the server is extracted. The ExResponse class has several methods for returning the results in different ways. The ResponseXML property can be used to retrieve the raw XML returned by the Exchange Store.

C#
...
string sRawXML = resp.ResponseXML;
...

Other methods allow a response to be analyzed as a System.Xml.XmlDocument, or a System.Data.DataSet, or as an ExDAV.ExObject. (Important: the ExObject can only be used for the responses of the ExDAV.Searcher.Find() method.)

C#
...
System.Data.DataSet ds = resp.GetDataSet("dsMyExDataSet", 
                                       "dsMyExTableName");
...

Tip: The ExResponse class' GetDatasetSchemaXMLReader method returns a System.IO.StringReader which contains the schema of the DataSet returned by the GetDataSet method. This allows the programmer to save the schema and even create strongly typed dataset schema files, to data bind controls to.

The ExModifier class is used to author against the Exchange Store. The authoring methods include: CreateNewItem, CreateFolder, CopyTo, MoveTo, Update, and Delete. These methods are pretty self explanatory.

C#
ExDAV.ExModifier modifier = new 
         ExDav.ExModifier("http://mystore.edu/myitem.eml"); 
modifier.Depth = ExDAV.ExRequest.ExRequestDepths.NullDepth;
//set up the credential
modifier.Credential = new System.Net.NetworkCredential("myUserName", 
                                               "myPassword", "myDomain");
//add the property to update
modifier.ExProps.Add("DAV:", "displayname", "MyItemNewName");
ExDAV.ExResponse resp = modifier.Update(true, 
         ExDAV.ExModifier.ModifierFlags.AddIfNotExistsOverwriteIfExists);
...

The StatCode and StatDesc properties on the ExResponse object that is returned, can be used to analyze the success of the request.

For more information on how to use the assembly, see the comments in the source code and the demo project.

Under the covers

I had some requests to discuss the implementation of WebDAV within the assembly as well as our methods for retrieving the results, so for those interested here it goes. Both the ExSearcher and the ExModifier inherit from the ExRequest class. This class contains the implementation of the actual WebDAV requests as well as the common properties such as ExProps. The method in which the WebDAV call is made is GetDAVResponse, which is handed the XML body of the request encoded as a byte array, and a prepackaged System.Net.WebRequest (code written in VB.NET).

VB
'GetDAVResponse is the main method of the whole assembly. 
'This is where we actually communicate with the Exchange Store.
Friend Function GetDAVResponse(ByVal arrData() As Byte, _
        ByVal HTTPRequest As WebRequest) As ExResponse

  Dim oResponse As WebResponse
  Dim oStreamIn As Stream
  Dim oHTTPResp As HttpWebResponse
  Dim oResponseXML As New XmlDocument()
  Dim oStreamRead As StreamReader
  Dim sResp As String
  Dim oExResp As ExResponse
  Dim sCode As String = ""
  Dim sDesc As String = ""
  Dim oStream As Stream

  Try
    'If body data has been submitted add it to the request stream
    If Not arrData Is Nothing Then
      Dim oStreamOut As Stream = HTTPRequest.GetRequestStream()
      oStreamOut.Write(arrData, 0, arrData.Length)
      oStreamOut.Close()
    End If

    'This is where the request is sent and the response recieved
    oResponse = HTTPRequest.GetResponse()

    'Use an HTTPWebResponse object so we can get more info
    oHTTPResp = oResponse

    'Get the Response stream and read it into a string
    oStreamIn = oResponse.GetResponseStream()
    oStreamRead = New StreamReader(oStreamIn)
    sResp = oStreamRead.ReadToEnd()

    'Some requests have no resulting response bodies so set a default one
    If sResp ="" Then
      sResp = "<?xml version=""1.0""?><NoResponse></NoResponse>"
    End If

    'Set up the ExResponse
    object that we will be returning

    oExResp = New ExResponse(m_URI, sResp, oHTTPResp.StatusCode, _
                     oHTTPResp.StatusDescription, oHTTPResp)

  Catch myWebEx As WebException

    'This is where we will end up if the server 
    'the resource is on returns an error
    'So we setup an ExDavException to throw.
    oHTTPResp = myWebEx.Response

    If Not oHTTPResp Is Nothing Then
      sCode = oHTTPResp.StatusCode
      sDesc = oHTTPResp.StatusDescription
    End If

    sResp = "<?xml version=""1.0"" ?><" & _
        "EXDAVError type=""WebRequest Error""><Code>" & _
        sCode & "</Code><Description>" & _
        sDesc & "</Description><Message>" & _
        myWebEx.Message & "</Message></EXDAVError>"
    
    Dim sMsg as String = "There was an error _
              processing the web request." & _
              "See the ErrXML property for more details."

    oExResp = New ExResponse(m_URI, sResp, sCode, sDesc, oHTTPResp)
    Throw New ExDAVException(m_URI, sMsg, sResp, oHTTPResp)

  Catch myEx As Exception

    'Any other exception puts us in here, 
    'so throw a generic ExDavException
    Dim sMsg as String = "There was an error processing _
                         the web request." & _
                         "See the ErrXML property _
                         for more details."

    Throw New ExDAVException(m_URI, sMsg, sResp)

  End Try

  'Return the response
  GetDAVResponse = oExResp

End Function

There are two main classes used when making WebDAV calls using .NET: the System.Net.WebRequest class, and the System.Net.WebResponse. There are two other classes that inherit from these classes, System.Net.HttpWebRequest and System.Net.HttpWebResponse, which have extra functionality for using the HTTP protocol. The WebRequest in this method is pre packaged, and passed in as a parameter as mentioned before, along with the XML body content. Here is a sample of what the code looks like for creating and setting up a WebRequest and the XML body content:

VB
'Instantiate the web request
Dim oHTTP As WebRequest = CType(WebRequest.Create(MyBase.uURI.ToString), 
                                                               WebRequest) 

'Encode the body and get the bytes.
'The body contains XML in the standard WebDAV schema.
Dim bytes() As Byte = Encoding.ASCII.GetBytes(sXML)

'Build the request and its headers
oHTTP.Credentials = MyBase.Credential
oHTTP.ContentType = "text/xml"
oHTTP.ContentLength = bytes.Length
oHTTP.Method = "PROPFIND"
oHTTP.Headers.Add("Depth", sDepth)
oHTTP.Headers.Add("Brief", "t")

This is really all there is to it: create a WebRequest, write the WebDAV XML content to the WebRequest's request stream, then make the call and get the response by using the WebRequest's GetResponse method. Finally the WebResponse's GetResponseStream method is used to retrieve the XML response that was sent by the server.

In order to convert this raw XML from the server into an object model or ADO.NET DataSet, we use an XSLT transform. This transform converts the raw XML into a schema that can be deserialized into an object, or read into a DataSet. For details, see the source code under the ExResponse class. There are obvious performance issues with this approach if large amounts of data are involved, and I would love to hear suggestions on better ways to do this. We have found, however, that the ability to access the data as a recursive object, or as a DataSet is much more useful than as raw XML.

Updates

As changes occur, I'll post them here.

  • August 2nd, 2005 - As mentioned in the final paragraph, the ExObject and ADO.NET DataSets require an XSLT transform. These XSLT stylesheets can be found at ExTransform.xslt and DsTransform.xslt. These style sheets will no longer be available at the locations specified in the code. Please download these stylesheets and save them onto a web server and change the lines 1361 and 1433 to reflect the new locations of these stylesheets. Alternatively, you can download them and embed them as a resource. This will take more modification of the code, but eliminates the dependency on the stylesheets being available online. There is a description of how to do this in the discussion below.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Web Developer
United States United States
Mark is a Software Developer for the College of Business at Oregon State University. A graduate of OSU, he has a BA in Spanish and is a self-taught programmer. Development on Microsoft Technologies is his focus and has been working for the College of Business since 1997.

Comments and Discussions

 
GeneralRe: pulling contact lists Pin
rohancragg31-Jul-03 4:37
rohancragg31-Jul-03 4:37 
GeneralRe: pulling contact lists Pin
clementsm31-Jul-03 5:08
clementsm31-Jul-03 5:08 
Generalparsing mail content Pin
san fermin26-Jun-03 5:31
san fermin26-Jun-03 5:31 
GeneralRe: parsing mail content Pin
clementsm27-Jun-03 10:49
clementsm27-Jun-03 10:49 
GeneralExchange XML Response Pin
Russell Mangel31-May-03 11:46
Russell Mangel31-May-03 11:46 
GeneralRe: Exchange XML Response Pin
rohancragg31-Jul-03 4:31
rohancragg31-Jul-03 4:31 
GeneralGerman Umlaute Pin
CyberFrog1121-May-03 14:55
CyberFrog1121-May-03 14:55 
GeneralPerformance &amp; 403 Forbidden Pin
Dampe21-May-03 11:43
Dampe21-May-03 11:43 
I have been using your WebDAV/Exchange Data Access Class in developing a proof of concept app for accessing Exchange via a web app. First off, I wanted to thank you for posting the code, it really saved me quite a bit of time and headache as WebDAV seems a bit non-intuitive and clunky to me. I do have a couple of items that I would like to ask about.

1) I've noticed that it takes a second or two for the page to start rendering, is this just the nature of working with Exchange/WebDAV, or is there any thing I can do to improve performance (especially for doing larger selects for viewing public folders). My plan was to use DataSets after I saw that your component supported them, do you think they will add too much overhead?

2) This question is even a bigger concern; when trying to add or update an appointment using PROPPATCH I always get a 403 (Forbidden) error, even on trying to add or update a single simple property. I wanted to make sure that this is truly a permissions issue and that I am not doing anything wrong in my code, since the admins are very wary about changing any of the permissions on the Exchange Server. Anyway, here is the code (using dummy values of course):

' Create variables
Dim oDavBase As New ExDAVBase("http://server/exchange/user/Calendar/test.eml", "user", "pass", "domain")
Dim oDavModifier As New ExModifier(oDavBase)
Dim oDavResponse As New ExResponse()

' Add properties that will be updated
oDavModifier.ExProps.Add("DAV:", "contentclass", "urn:content-classes:appointment")
oDavModifier.ExProps.Add("urn:schemas:calendar:", "location", "My Cube")

' Get the response object
oDavResponse = oDavModifier.Update(True, ExModifier.ModifierFlags.AddIfNotExistsOverwriteIfExists)

Thanks in advance for any help you can offer, Smile | :)

Zeb N. Olsen
Senior Systems Integration Analyst
Idaho Department of Labor
GeneralRe: Performance &amp; 403 Forbidden Pin
clementsm21-May-03 13:22
clementsm21-May-03 13:22 
General501 Not implemented Pin
CyberFrog1118-May-03 2:41
CyberFrog1118-May-03 2:41 
GeneralRe: 501 Not implemented Pin
CyberFrog1118-May-03 4:51
CyberFrog1118-May-03 4:51 
GeneralRe: 501 Not implemented Pin
clementsm19-May-03 5:49
clementsm19-May-03 5:49 
GeneralGet e-mail properties Pin
mlew16-May-03 3:43
mlew16-May-03 3:43 
GeneralRe: Get e-mail properties Pin
clementsm19-May-03 5:41
clementsm19-May-03 5:41 
GeneralRe: Get e-mail properties Pin
luzer30-Jun-04 9:25
luzer30-Jun-04 9:25 
GeneralRe: Get e-mail properties Pin
clementsm30-Jun-04 9:38
clementsm30-Jun-04 9:38 
GeneralRe: Get e-mail properties Pin
luzer30-Jun-04 9:45
luzer30-Jun-04 9:45 
GeneralRe: Get e-mail properties Pin
clementsm30-Jun-04 9:49
clementsm30-Jun-04 9:49 
GeneralRe: Get e-mail properties Pin
luzer30-Jun-04 10:08
luzer30-Jun-04 10:08 
GeneralRe: Get e-mail properties Pin
clementsm30-Jun-04 14:13
clementsm30-Jun-04 14:13 
GeneralAuthentication Problems Pin
rohancragg29-Apr-03 3:03
rohancragg29-Apr-03 3:03 
GeneralRe: Authentication Problems Pin
clementsm30-Apr-03 7:08
clementsm30-Apr-03 7:08 
GeneralRe: Authentication Problems Pin
rohancragg30-Apr-03 23:53
rohancragg30-Apr-03 23:53 
GeneralRe: Authentication Problems Pin
rohancragg30-Apr-03 23:57
rohancragg30-Apr-03 23:57 
GeneralRe: Authentication Problems Pin
clementsm1-May-03 5:05
clementsm1-May-03 5:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.