Published
Wednesday, December 19, 2007 5:19 AM
by
mtaulty
The bits for ADO.NET Data Services ("Astoria") primarily seem to live in an assembly Microsoft.Web.Data.dll (which I'm referencing from c:\program files\reference assemblies).
I'd say that the key class in there is WebDataService<T> which implements pretty much the lowest level of WCF ServiceContract that you can implement (Christian pointed this out the other day) in that it looks like this;
[ServiceContract]
public interface IRequestHandler
{
// Methods
[WebInvoke(UriTemplate="*", Method="*"), OperationContract]
Message ProcessRequestForMessage(Stream messageBody);
}
Which says (to me) - "give me a message and I'll give you one back and I'll answer a call for any URI and any HTTP method if invoked over the web". It also implements IWebDataService which is an internal interface which brings together;
internal interface IWebDataService
{
// Properties
WebDataServiceConfiguration Configuration { get; }
IWebDataServiceHost Host { get; }
IWebDataServiceProvider Provider { get; }
}
Of these, for me, the IWebDataServiceProvider is the most interesting thing in that this looks to abstract what it means to be a "provider" that plugs into the RESTful front end provided by "Data Services" and that interface is implemented by two concrete types inside the framework (in the Microsoft.Data.Web.Providers namespace);
- ObjectContextServiceProvider - uses the Entity Framework in order to implement the requirements of an IWebDataServiceProvider
- ReflectionServiceProvider - uses reflection to implement the requirements of an IWebDataServiceProvider.
I don't think that you can extend that list in the sense that it looks like when you use WebDataService<T> the framework (at one point) tries to create an instance of T by checking to see whether your T derives from ObjectContext and, if so, you get an ObjectContextServiceProvider and, if not, you get a ReflectionServiceProvider.
There's no constraint on <T> in WebDataService<T> but one thing <T> will be checked for (at certain points) in the case of ReflectionServiceProvider is whether <T> is IUpdateable.
It's been said that you can use "any old IQueryable" in the latest preview of "Data Services" rather than having to use Entity Framework and (from experimenting with LINQ to SQL) that looks to be true but IQueryable doesn't deal with the business of inserts/updates/deletes and so if you want your "Data Services" data to do more than just Query then your T needs to implement IUpdateable which LINQ to SQL doesn't (more on that in a later post).
One of the first things that looks to happen when we construct WebDataService<T> is a call to IWebDataServiceProvider.PopulateMetadata. This works two ways;
- ObjectContextServiceProvider - reaches into the MetadataWorkspace of the Entity Data Model and uses that to build up metadata.
- ReflectionServiceProvider - reflects at all the public, instance, non-index properties of your T and looks to apply a few heuristics (e.g. are they IQueryable?) to determine whether that property becomes exposed over the data interface or not (i.e. I haven't read all the code there - perhaps it's doc'd somewhere?).
What about hosting up the WebDataService<T>?
The code from the template in VS, puts in a call to use a factory class called DataServiceHostFactory in order to create your ServiceHost-derived class as implementation for the service. All this looks to do is to return instances of DataServiceHost for the service type.
DataServiceHost derives from the .NET V3.5 WebServiceHost and doesn't look to add anything much to it.
So, there doesn't seem to be anything too mystical in there. It doesn't feel very different to use writing code such as;
[ServiceContract]
public interface IRequestHandler
{
// Methods
[WebInvoke(UriTemplate = "*", Method = "*"), OperationContract]
Message ProcessRequestForMessage(Stream messageBody);
}
public class MyService : IRequestHandler
{
public Message ProcessRequestForMessage(Stream messageBody)
{
throw new NotImplementedException();
}
}
class Program
{
static void Main(string[] args)
{
WebServiceHost host = new WebServiceHost(typeof(MyService),
new Uri("http://localhost:9091/myService"));
host.Open();
Console.ReadLine();
}
}
Now, one of the things that "Data Services" does for you is to make access from the client relatively easy in that it gives you a Microsoft.Data.WebClient assembly and a tool webdatagen.exe to point at one of these services in order to generate client side classes. This gives you a code-gen'd "client side DataContext" to query against (WebDataContext) and a bunch of types that match up to what you're sending back from your service.
One of the things that puzzled me here was how this worked given that the ServiceContract above isn't particularly descriptive and so generating a WSDL document there isn't really going to help.
As far as I can tell, you request metadata from one of these services using a URI such as;
http://localhost/myService.svc/$metadata
which looks to return the EDM schema (regardless of whether you're exposing an Entity Framework model or whether you've plugged in something else) along with some help info for LINQ (not fathomed that out yet). In the example I'm playing with my EDM seemed to get returned in 2 pieces, one with the EntityTypes and another with the EntitySets - whether that's generally the case, I don't know.
So, I'm guessing that this is what webdatagen.exe does in order to fathom out how to generate code on the client side.
So, this is all pretty understandable stuff once you're at .NET V3.5 and you've got Entity Framework and/or some other IQueryable source to put into "Data Services".
There are serializers within Microsoft.Data.Web.Serializers so that part is probably not too hard to understand and the majority of the work (presumably) goes into the other part of pulling apart the URI passed and turning it into something useful before generating a response for serialization - that's the bit that I'm not even attempting to look at here right now :-)