Creating a Data Service Provider – Part 6 – Query Interactions


Whenever I find myself implementing a series of interfaces to plug into a framework or server, I always find myself wondering how the server will call my implementations.


For me this is about forming a mental model to simplify how I think about what I’m doing.


In fact as I’ve been doing this DSP series I’ve come up with a sort of mental model for how Queries are handled in a Data Service.


Here is some PSEUDO CODE that captures my mental model of how these interfaces interact.


Scenario – Basic Query


Imagine a client issues a GET request for /Sample.svc/Products(1)


The first thing that happens (in my model) is that the data service is initialized.


// Locate the IDSP interfaces
var dataservice = …;
IServiceProvider sp = dataservice as IServiceProvider;
if (sp == null)
{
   // some other code I’ll go into another day !!!
}


// Get the various DSP interface implementations
IDataServiceMetadataProvider mdp =
   sp.GetService(typeof(IDataServiceMetadataProvider));
IDataServiceQueryProvider qp =
   sp.GetService(typeof(IDataServiceQueryProvider));

// Set the CurrentDataSource (if necessary)
if (qp.CurrentDataSource == null)
   qp.CurrentDataSource = dataservice.CreateDataService();

// Find the Products resourceSet
// (note we actually try for ServiceOperations first
// but lets keep this simple)

var resourceSet = null;
if (!mdp.TryResolveResourceSet(“Products”, out resourceSet))
   throw new Exception(“404”);


// Get the queryable for the Products resourceSet
IQueryable queryRoot = qp.GetQueryRootForResourceSet(resourceSet);


// Compose expressions onto the IQueryable to represent the
// options ($filter/$select etc) specified in the URL
queryRoot = Compose(options, queryRoot);


// Start writing response
WriteStartODataFeed();

// Enumerate results
foreach (object resource in queryRoot)
{
    // Get the ResourceType for resource 
    // NOTE: because of inheritance it might be a resourceType
    // derived from resourceSet.ResourceType

    ResourceType type = qp.GetResourceType(type);
    WriteResource(resource,type);     
}
WriteEndODataFeed();


That’s it take it or leave it.


Hopefully you found this ‘completely made up’ code useful for forming your own mental model of how your DSP will fit into the Data Services framework.


Next time


There are some things I’ve left out of the above ‘mental model’ for now, like how ServiceOperations and QueryInterceptors complicate things.


We’ll flesh more of those complications out in future posts.


Next time though it’s time to take our Read/Only strongly typed ResourceSet and make it Read/Write.

Comments (11)

  1. Avinash says:

    I have been looking for details about implementing Custom Data Provider from the time I read Pablo Castro’s <a href="http://blogs.msdn.com/pablo/archive/2008/11/01/ado-net-data-services-in-windows-azure-pushing-scalability-to-the-next-level.aspx">ADO.NET Data Services in Windows Azure: pushing scalability to the next level</a>.

    Thank you for the series.

    I am looking forward for the "Loosely Typed" entry.

  2. Lee says:

    Hi,I have metadata of entities that stored in database,but I do not have CLR classes of these entities.Can I expose these data with Custom Data Service Provider,and how? Thank you

  3. Alex D James says:

    @Lee,

    Yes you can, expect a post within a week or so, the key to this is a having generic structure backing your ResourceType like say Dictionary<string,object>, and telling Astoria not to use PropertyAccessors… the way you do this is set ResourceType.CanReflectOnResourceType to false and ResourceType.InstanceType to typeof(Dictionary<string,object>) and also set ResourceProperty.CanReflectOnResourceProperty to false. Then Astoria will make GetValue(…) calls in your IQueryable expression instead of direct property accessors, and will use IDataServiceQueryProvider.GetValue(…) when serializing your objects.

    Anyway expect more soon

  4. Lee says:

    Hi,Thanks for your help,I can query data which doesn’t have CLR Class,like "http://localhost:1406/WebDataService1.svc/Users",but”>http://localhost:1406/WebDataService1.svc/Users",but when I try to get a single result "http://localhost:1406/WebDataService1.svc/Users(1)",it will call the method "System.Data.Services.Providers.DataServiceProviderMethods.GetValue(Object value, ResourceProperty property)",which throws a NotImplementedException.It won’t happen when it has a real CLR Class.Do you know how to resolve this problem?Thank you very match!

  5. Alex D James says:

    @Lee,

    I’m guessing you have an AsQueryable() call in there somewhere (i.e. LINQ to Objects). The problem is that rather than doing something like this:

    from x in Queryable

    where x.Key == 1

    select x;

    Astoria has to do something like this (psuedo code):

    from x in Queryable

    where GetValue(x, "Key") == 1

    select x

    Because the Key property doesn’t actually exist.

    So you need a visitor to replace that with whatever actually gets the value, so LINQ to Objects can handle the request….

    i.e. if your type is backed by a dictionary, you’d re-write to something like this:

    from x in Queryable

    where x["Key"] == 1

    select x;

    Hope this helps

    Alex

  6. Alex D James says:

    @Lee,

    Oh yes and you can expect a sample on this soon.

    Alex

  7. Avinash says:

    @Alex

    I tried the same thing as Lee had done and I too faces same problem. There is no code in my project that is performing something what you have mentioned above. Could it be Astoria? I am returning List<Dictionary<string,object>> and same .AsQueryable().

  8. Alex D James says:

    @Avinash.

    Absolutely, if you have a resourceset backed by an in memory dictionary then this:

    GET ~/ResourceSet

    should work, But this:

    GET ~/ResourceSet(Key)

    or this:

    GET ~/ResourceSet/$filter=…

    will both fail because LINQ to Objects doesn’t know how to handle the GetValue call. You can expect to see a sample soon.

    If you can’t wait, in the meantime you need to write an IQueryable that wraps the list and before executing the expression on the underlying list, visits the expression to replace GetValue(x, "key") with x["key"]

    Alex

  9. Avinash says:

    Waiting for IQueryable over list of Diction<String,object> sample.

  10. Santosh Kumar says:

    Can any one help me how to create the Service operation with custom Data Service Provider.