views:

127

answers:

1

Problem

I would like to trace the Uri that will be generated by a LINQ query executed against a Microsoft.WindowsAzure.StorageClient.TableServiceContext object. TableServiceContext just extends System.Data.Services.Client.DataServiceContext with a couple of properties.

The issue I am having is that the query executes fine against our Azure Table Storage instance when we run the web role on a dev machine in debug mode (we are connecting to Azure storage in the cloud not using Dev Storage). I can get the resulting query Uri using Fiddler or just hovering over the statement in the debugger.

However, when we deploy the web role to Azure the query fails against the exact same Azure Table Storage source with a ResourceNotFound DataServiceClientException. We have had ResoureNotFound errors before that dealt with the behavior of FirstOrDefault() on empty tables. This is not the problem here.

As one approach to the problem, I wanted to compare the query Uri that is being generated when the web role is deployed versus when it is running on a dev machine.

Question

Does anyone know a way to get the query Uri for the query that will be sent when the FirstOrDefault() method is called. I know that you can call ToString() on the IQueryable returned from the TableServiceContext but my concern is that when FirstOrDefault() is called the Uri might be further optimized and ToString() on IQueryable might not be what is ultimately sent to the server when FirstOrDefault() is called.

If someone has another approach to the problem I am open to suggestions. It seems to be a general problem with LINQ when trying to determine what will happen when the expression tree is finally evaluated. I am open to suggestions here as well because my LINQ skills could use some improvement.

Sample Code

public void AddSomething(string ProjectID, string Username) {
    TableServiceContext context = new TableServiceContext();

    var qry = context.Somethings.Where(m => m.RowKey == Username
        && m.PartitionKey == ProjectID);

    System.Diagnostics.Trace.TraceInformation(qry.ToString());
    // ^ Here I would like to trace the Uri that will be generated
    // and sent to the server when the qry.FirstOrDefault() call below is executed.

    if (qry.FirstOrDefault() == null) {
        // ^ This statement generates an error when the web role is running
        // in the fabric
        ...
    }
}

Edit Update and Answer

Steve provided the write answer. Our problem was as exactly described in this post which describes an issue with PartitionKey/RowKey ordering in Single Entity query which was fixed with an update to the Azure OS. This explains the discrepancy between our dev machines and when the web role was deployed to Azure.

When I indicated we had dealt with the ResourceNotFound issue before in our existence checks, we had dealt with it in two ways in our code. One way was using exception handling to deal with the ResourceNotFound error the other way was to put the RowKey first in the LINQ query (as some MS people had indicated was appropriate).

It turns out we have several places where the RowKey was first instead of using the exception handling. We will address this by refactoring our code to target .NET 4 and using the .IgnoreResourceNotFoundException = true property of theTableServiceContext .

Lesson learned (more than once): Don't depend on quirky undocumented behavior.

Aside

We were able to get the query Uri's. They did turn out to be different (as indicated they would be in the blog post). Here are the results:

Query Uri from Dev Fabric

`https://ourproject.table.core.windows.net/Somethings()?$filter=(RowKey eq '[email protected]') and (PartitionKey eq '41e0c1ae-e74d-458e-8a93-d2972d9ea53c')

Query Uri from Azure Fabric

`https://ourproject.table.core.windows.net/Somethings(RowKey='[email protected]',PartitionKey='41e0c1ae-e74d-458e-8a93-d2972d9ea53c')

+2  A: 

I can do one better... I think I know what the problem is. :)

See http://blogs.msdn.com/b/windowsazurestorage/archive/2010/07/26/how-wcf-data-service-changes-in-os-1-4-affects-windows-azure-table-clients.aspx.

Specifically, it used to be the case (in previous Guest OS builds) that if you wrote the query as you did (with the RowKey predicate before the PartitionKey predicate), it resulted in a filter query (while the reverse, PartitionKey preceding RowKey) resulted in the kind of query that raises an exception if the result set is empty.

I think the right fix for you (as indicated in the above blog post) is to set the IgnoreResourceNotFoundException to true on your context.

smarx
BTW, I suspect qry.Take(1).ToString() should give you the same URI as is sent on the wire when you do qry.FirstOrDefault().
smarx
Thanks Steve. That was the right answer. I will edit my question to provide more details. P.S. It would still be nice if LINQ had some sort of debug mechanism where you could get the "query plan" without actually having to execute it.
Matt Spradley