Partager via


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

  • Anonymous
    January 25, 2010
    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.
  • Anonymous
    January 26, 2010
    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
  • Anonymous
    January 26, 2010
    @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
  • Anonymous
    January 31, 2010
    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!
  • Anonymous
    January 31, 2010
    @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 Queryablewhere x.Key == 1select x;Astoria has to do something like this (psuedo code):from x in Queryablewhere GetValue(x, "Key") == 1select xBecause 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 Queryablewhere x["Key"] == 1select x;Hope this helpsAlex
  • Anonymous
    January 31, 2010
    @Lee,Oh yes and you can expect a sample on this soon.Alex
  • Anonymous
    February 13, 2010
    @AlexI 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().
  • Anonymous
    February 13, 2010
    @Avinash.Absolutely, if you have a resourceset backed by an in memory dictionary then this:GET ~/ResourceSetshould 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
  • Anonymous
    February 25, 2010
    Waiting for IQueryable over list of Diction<String,object> sample.
  • Anonymous
    June 24, 2010
    chinese versionwww.cnblogs.com/.../DSP7.html
  • Anonymous
    August 03, 2010
    Can any one help me how to create the Service operation with custom Data Service Provider.