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 DataSet
s, 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#).
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'
):
...
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).
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.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.
...
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.)
...
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.
ExDAV.ExModifier modifier = new
ExDav.ExModifier("http://mystore.edu/myitem.eml");
modifier.Depth = ExDAV.ExRequest.ExRequestDepths.NullDepth;
modifier.Credential = new System.Net.NetworkCredential("myUserName",
"myPassword", "myDomain");
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).
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 Not arrData Is Nothing Then
Dim oStreamOut As Stream = HTTPRequest.GetRequestStream()
oStreamOut.Write(arrData, 0, arrData.Length)
oStreamOut.Close()
End If
oResponse = HTTPRequest.GetResponse()
oHTTPResp = oResponse
oStreamIn = oResponse.GetResponseStream()
oStreamRead = New StreamReader(oStreamIn)
sResp = oStreamRead.ReadToEnd()
If sResp ="" Then
sResp = "<?xml version=""1.0""?><NoResponse></NoResponse>"
End If
object that we will be returning
oExResp = New ExResponse(m_URI, sResp, oHTTPResp.StatusCode, _
oHTTPResp.StatusDescription, oHTTPResp)
Catch myWebEx As WebException
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
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
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:
Dim oHTTP As WebRequest = CType(WebRequest.Create(MyBase.uURI.ToString),
WebRequest)
Dim bytes() As Byte = Encoding.ASCII.GetBytes(sXML)
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 DataSet
s 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.