Azure Table Storage Exceptions with Multiple Table Entity Schemas

 

I’ve been messing with Azure Table Storage recently and needed to create a somewhat nontrivial data model to try some things. 

This data model includes patients, patient addresses (email, IM, postal, phone, etc.) and patient events.  I also wanted to store all of this data in the same table so I had table entities with differing schemas in the same table.

I then wrote some code to created an array of patients and for each patient I added the patient record and a random number of different patient addresses to the table. 

The more I work with Windows Azure the more I come to the conclusion that debugging Windows Azure code is like a Doctor treating a patient, i.e. make random changes and see if that fixes the problem.  Trying to figure out what the problem is from the exception information is like looking in a crystal ball in that it just doesn’t show anything other than what you can imagine.

Executing the code results in what appears to be one of the most common exceptions there is when working with Windows Azure:

{Microsoft.WindowsAzure.StorageClient.StorageExtendedErrorInformation}
    AdditionalDetails: null
    ErrorCode: "InvalidInput"
    ErrorMessage: "0:One of the request inputs is not valid."

 

So my next issue is that I had added multiple records so which one was causing the problem?  To figure that out you need to enumerate the DataServiceRequestException.Response property.  You get an IEnumerable<ChangeOperationResponse> collection from this property.  From my experience so far there only ever appears to ever be one entity in this collection no matter how many records you create that would have had problems.  What you look for is a header by the name of “Content-ID” on the OperationResponse.  The value is the 1 based index of the records that were added before calling TableContext.SaveChangesWithRetries(SaveChangesOptions.Batch).

You can see this a lot better if you use Fiddler to view the input/output of the batch request.  See http://learningbyfailing.com/2009/12/using-fiddler-with-azure-devstorage/ to see how to get Fiddler to display the output.

In my case it kept pointing to the second record that I added (the first was the patient record and the second was an address record).  If I saved the patient record by itself it worked and if in a separate batch I saved the address records it worked.  So in spite of the exception pretty much not telling me what the problem was I came to the conclusion that I can’t mix table entity schemas in a single batch.  Apparently this is a restriction for development storage but will work when using cloud storage.

It’s too bad the exception didn’t tell me this.

Remember that Azure Tables have limited property datatype support

Recently I threw some code together to add objects into an Azure table.  I used the class:

	[DataServiceKey("PartitionKey", "RowKey")]
	public class OrderMessage : TableServiceEntity
	{
		public DateTime OrderDate { get; set; }
		public string CustomerName { get; set; }
		public string CreditCard { get; set; }
		public int Quantity { get; set; }
		public decimal CostEach { get; set; }
	}

Upon adding the data to the table using:

	TableServiceContext tableContext = connection.TableClient.GetDataServiceContext();
	tableContext.AddObject(connection.OrderTableName, message);
	DataServiceResponse response = tableContext.SaveChangesWithRetries();

I received the error:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>

<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">

<code>InvalidInput</code>

<message xml:lang="en-US">One of the request inputs is not valid.</message>

</error>

 

After wasting some time looking at help and Googling I was skimming across some documentation on tables and it happened to list the supported property types for Azure tables.  I knew that they had limited support but not till I looked at that list did it occur to me that I was using the unsupported datatype ‘decimal’.  Modifying the class so that CostEach was of type ‘double’ resolved my problem. 

It sure would be nice if the error was a little more explicit.  I’m sure that somewhere in the Azure code it knows what happened.  I also find it interesting that rather than returning information in the DataServiceResponse it throws an exception.  I don’t see this ability to throw exceptions in the documentation and in fact the documentation says that the return value is:

A DataServiceResponse that contains status, headers, and errors that result from the call to SaveChanges.

On well I guess somebody kinda forgot to update their XML comments on the method with:

/// <exception cref="System.Data.Services.Client.DataServiceClientException">A stealth exception that we won't tell anybody about</exception>

More than once I’ve seen a reminder on blogs to make sure you only use the supported data types on your table entities.   Here’s another reminder for you and *bonk* me!