Some SQL Server Data Services Coding Examples
Since my last post (just after we released the service) several others have written some really great stuff on SSDS. I've listed these below for reference.
Overview of the ACE model from Ryan
SSDS Query Syntax (also from Ryan)
However, most of the content has been focused on either conceptual material or our basic query syntax. So, I wanted this post to be more on how to use our service from C# (I'll do a Java example next time around). I'm only going to cover the REST head in this example but future examples will illustrate the use of our service from the SOAP head.
In this example, I'll construct a general purpose class to generate a WebRequest in C# and also another to read in the value. Once this is complete we'll attempt to use these to do something interesting.
Knowing your verbs
The first thing we're going to do in this example is to define an enumeration which we we can use with our utility class to specify the HTTP verb that we want to use. Listed below is a enumeration which we can use to do this.
public enum HttpMethods
{
GET = 0,
PUT,
POST,
DELETE,
HEAD,
OPTIONS,
LIST,
UNKNOWN
}
Generating Requests
So, now that we have our HTTP vocabulary down let's proceed in writing a simple method that we can use to create our WebRequest objects for us. This should take care of setting things like the encoding, content-length and these sorts of details for us automatically so that we don't have to worry about this.
/// <summary>
/// This helper method can be used to create WebRequests for the caller.
/// It will populate the ContentType, ContentLength and Body for the caller.
/// </summary>
/// <param name="uri">Target URI of the request.</param>
/// <param name="method">The HTTP method to use.</param>
/// <param name="data">The body of the request.</param>
/// <param name="contentType">The content type.</param>
/// <returns>The created WebRequest.</returns>
public static HttpWebRequest CreateRequest(string uri,
HttpMethods method,
string data,
string contentType)
{
// Begin by validating our inbound parameters.
if (String.IsNullOrEmpty(uri))
{
throw new ArgumentOutOfRangeException("uri", "Value cannot be null or empty.");
}
// Then, go ahead and create the request using the parameters that were passed in.
WebRequest request = HttpWebRequest.Create(uri);
request.Method = Enum.GetName(typeof(HttpMethods), method);
request.Credentials = new NetworkCredential("Your User Id", "Your Password");
if (method != HttpMethods.GET &&
method != HttpMethods.DELETE)
{
// Next, based on the encoding calculate the
// content length, type and populate the body of the request.
Encoding encoding = Encoding.UTF8;
request.ContentLength = encoding.GetByteCount(data);
request.ContentType = contentType;
request.GetRequestStream().Write(
encoding.GetBytes(data), 0, (int)request.ContentLength);
request.GetRequestStream().Close();
}
else
{
// If we're doing a GET or DELETE don't bother with this as all
// the interesting bits will be on the response.
request.ContentLength = 0;
}
// Finally, return the newly created request to the caller.
return request as HttpWebRequest;
}
Reading Responses
Now that we can create requests easily enough let's go ahead and create something that will read them in just as easily for us. I'm going to make a simplifying assumption here to make this a bit easier. I'm always just going to read in the response into a string. Now, there are cases where you might not want to do this (say in the case of chunked responses) but this should work fine for what we want to do.
/// <summary>
/// This method can be used to read the response body from a given response.
/// </summary>
/// <param name="response">The response to read.</param>
/// <returns>String representation of the HTTP response.</returns>
public string ReadResponse(HttpWebResponse response)
{
// Begin by validating our inbound parameters.
if (response == null)
{
throw new ArgumentNullException("response", "Value cannot be null");
}
// Then, open up a reader to the response and read the contents to a string
// and return that to the caller.
string responseBody = String.Empty;
using (Stream stm = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(stm))
{
// Simply read in the entire response to our string.
responseBody = reader.ReadToEnd();
reader.Close();
}
}
return responseBody;
}
Putting it together
Okay, now that we have these simple tools in place let's try to construct something that's a bit interesting. In this case a simple ASP.NET page which has several data bound controls (where the data comes from SSDS).
Now, I've created several containers which logically represent customers in my application (recall that a container is our partitioning unit so this data will now reside in a different partition from other customers). Then, within each of these containers are entities of type Customer, Order and finally OrderDetail (You might recognize some of these from the Northwind DB).
Now, there is only 1 customer entity within any Customer Container (and this simply contains more detailed information about that customer). The Order and OrderDetails entities capture information about the orders that this customer currently has. The image below illustrates what this looks like conceptually.
So, the first I thing I'm going to write is a bit of code which simply retrieves the total set of customers that I have. We'll use this information to bind the customer id's to a drop down list so that we can easily select the customer whose orders we would like to view.
try
{
const String XmlContentType = @"application/xml";
// Read in authority ID from web.config.
String authId = WebConfigurationManager.AppSettings["AuthId"];
// Then, formulate our URI using the authority and the service
// address we read in.
String allCustomersQuery = String.Format("https://{0}.data.sitka.microsoft.com/v1/?q=''",
m_authId);
// Next, create the request object using the utility method we defined earlier.
HttpWebRequest request = CreateRequest(allCustomersQuery, HttpMethods.GET,
String.Empty, XmlContentType);
// Now, attempt to read in the data.
using(HttpWebResponse response = (HttpWebResponse) request.GetResponse())
{
string responseXml = ReadResponse(response);
// After reading in the response parse the response into a XDocument.
XDocument customerDoc = XDocument.Parse(responseXml);
XNamespace ssdsNamespace = "https://schemas.microsoft.com/sitka/2008/03/";
// Then, using XLinq, select all the customer containers projecting out their Id
// to a new object which has a single property value called, "CustomerId"
var customerIdList =
from c in customerDoc.Descendants(ssdsNamespace + "Container")
select new
{
CustomerId = c.Element(ssdsNamespace + "Id").Value
};
// Then, bind the datasource to our drop down list.
this.ddlCustomers.DataTextField = "CustomerId";
this.ddlCustomers.DataSource = customerIdList;
this.DataBind();
}
}
catch(WebException ex)
{
if (ex.Response != null)
{
// Alert the user that something happened bad happened here.
// I'm just capturing the string here for debugging purposes.
string errorMsg = ReadResponse((HttpWebResponse)ex.Response);
}
}
Before we move on you may be wondering what this actually looks like on the wire so here it is.
<s:EntitySet xmlns:s="https://schemas.microsoft.com/sitka/2008/03/" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:x="https://www.w3.org/2001/XMLSchema">
<s:Container>
<s:Id>Customer000000</s:Id>
<s:Version>1</s:Version>
</s:Container>
<s:Container>
<s:Id>Customer000001</s:Id>
<s:Version>1</s:Version>
</s:Container>
<s:Container>
<s:Id>Customer000002</s:Id>
<s:Version>1</s:Version>
</s:Container>
<s:Container>
<s:Id>Customer000003</s:Id>
<s:Version>1</s:Version>
</s:Container>
<!-- more data here but omitted... -->
</s:EntitySet>
In the prior example we operated exclusively over the containers in the service. This time we're going to query within a particular container for Order information and project out a new class which can be used for data binding. Now, this is only one way we could approach the problem. Another, and one that I'll post on as well soon, would be to use XmlSerializer (or something like JAXB if you're using Java) and create a general purpose object to represent orders which we could use all over our application.
However, in this case I'm really interested in focusing in on using XLinq with our service so I'm going to stay with the use of Anonymous classes as they are a nice for these sort of build to order classes that one sometimes needs for these kinds of DataBinding scenarios.
try
{
// First, read in the authority id to use from our web.config.
String authId = WebConfigurationManager.AppSettings["AuthId"];
const String XmlContentType = @"application/xml";
// Next, define the query that will select only those entities which have a kind value
// of Order.
String query = String.Format(@"from e in entities where e.Kind == ""Order"" select e");
// Then, construct the URI that we'll use to make the request. In this case, I've retrieved
// the container id to use from a drop down list (this list was actually the one populated
// from our last code sample).
String allOrdersQuery =
String.Format(@"https://{0}.data.sitka.microsoft.com/v1/{1}?q='{2}'",
authId, ddlCustomers.SelectedValue, query);
HttpWebRequest request = CreateRequest(allOrdersQuery, HttpMethods.GET,
String.Empty, XmlContentType);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
string responseXml = ReadResponse(response);
// After reading in the response parse the response into a XDocument.
XDocument ordersDoc = XDocument.Parse(responseXml);
XNamespace ssdsNamespace = "https://schemas.microsoft.com/sitka/2008/03/";
// Then, select all the customer containers projecting out their Id
// to a new object which has a single property value called, "CustomerId"
var orderList =
from o in ordersDoc.Descendants("Order")
select new
{
OrderId = o.Element(ssdsNamespace + "Id").Value,
DateOrdered = Convert.ToDateTime(o.Element("OrderDate").Value),
Total = Convert.ToDecimal(o.Element("Total").Value)
};
// Then, bind the grid to the dataset values.
this.grdDataBinding.DataSource = orderList;
this.grdDataBinding.DataBind();
}
}
}
catch (WebException ex)
{
if(ex.Response != null)
{
// Read in the error message and alert the user.
// For now, I'm just keeping this here for debugging purposes.
string errorMsg = ReadResponse((HttpWebResponse) ex.Response);
}
}
In this last code example we've constructed a query over a specific container. This query places a restriction over the output such that only entities that match the Kind predicate that we've setup are returned to us. Listed below is an example of what this looks like on the wire.
<s:EntitySet xmlns:s="https://schemas.microsoft.com/sitka/2008/03/" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:x="https://www.w3.org/2001/XMLSchema">
<Order>
<s:Id>2001-11-02T01:01:01Z-O044501</s:Id>
<s:Version>5</s:Version>
<HighPriority xsi:type="x:boolean">true</HighPriority>
<OrderId xsi:type="x:string">O044501</OrderId>
<OrderDate xsi:type="x:dateTime">2001-11-02T01:01:01 </OrderDate>
<Total xsi:type="x:decimal">26128.8674</Total>
</Order>
<Order>
<s:Id>2002-02-02T01:01:01Z-O045283</s:Id>
<s:Version>5</s:Version>
<HighPriority xsi:type="x:boolean">true</HighPriority>
<OrderId xsi:type="x:string">O045283</OrderId>
<OrderDate xsi:type="x:dateTime">2002-02-02T01:01:01</OrderDate>
<Total xsi:type="x:decimal">37643.1378</Total>
</Order>
<Order>
<s:Id>2002-05-02T01:01:01Z-O046042</s:Id>
<s:Version>5</s:Version>
<HighPriority xsi:type="x:boolean">true</HighPriority>
<OrderId xsi:type="x:string">O046042</OrderId>
<OrderDate xsi:type="x:dateTime">2002-05-02T01:01:01</OrderDate>
<Total xsi:type="x:decimal">34722.9906</Total>
</Order>
<!-- Omitting other orders for simplicity. -->
</s:EntitySet>
Now, we have a very simple data bound ASP.NET application which shows you can do data binding very simply using SSDS.
--Jeff--
Comments
Anonymous
March 17, 2008
Jeff is one of the developers on the SSDS project. He just sent me a note indicating that he has postedAnonymous
March 17, 2008
Technorati Tags: SSDS , SQL Server Data Services , SQL Server , Web Service , REST , Master , DetailAnonymous
March 18, 2008
In my last post I covered a simple example of querying SSDS using C#, HttpWebRequest and XLinq. Anonymous
April 02, 2008
In one of my prior posts I promised some SOAP examples to better illustrate how you can use SOAP to interactAnonymous
April 12, 2008
We've begun working with some external customers (and a considerable number of internal ones) on the