In part 1 we looked into some of the plumbing under Ria Services, mostly around the serialization and service routing. We used a “ServiceOperation” as the example in that case. In this part, we’ll look into “Query” type operations under the hood. We won’t rehash overlapping information, so if this information interests you, you might consider reading Part 1 if you have not done so already. The source code for all of these posts can be found on my sky drive location.
Our sample will use this simple DomainService method:
[Query] public IEnumerable<Person> SnagAllPeeps() { return FakeDataAccessLayer.GetAllPeeps(); }
…consumed by our Silverlight client like so:
<data:DataGrid x:Name="grdPeeps"KeyDown="grdPeeps_KeyDown"/>
private void CallQueryOperation(object sender, RoutedEventArgs e) { this.grdPeeps.ItemsSource = _ds.Persons; _ds.Load(_ds.SnagAllPeepsQuery().Where(i=> i.Age>35).OrderBy(i=>i.Name), (LoadOperation<Person> loadOperation) => { // Do whatever here. }, null); }
While the flow under the hood for Query methods is similar to what we discovered when looking at ServiceOperation(s), with some exceptions around passing Linq query information. The generated client-side DomainService (which is referred to as the DomainContext btw), contains a generated method that looks like this:
/// <summary> /// Returns an EntityQuery for query operation 'SnagAllPeeps'. /// </summary> public EntityQuery<Person> SnagAllPeepsQuery() { return base.CreateQuery<Person>("SnagAllPeeps", null, false, true); }
You’ll note that a Systems.Windows.Ria.Data.EntityQuery<Person> is being returned by the base DomainContext.CreateQuery<T> method. An EntityQuery object wraps up information about the query, including the System.Linq.IQueryable query. In this case, we have a predefined query named “SnagAllPeeps”, with no [null] parameters, has No Side effects and IsComposable. HasSideEffects, according to the documentation, will allow a Query operation to use an HTTP Post, while IsComposable determines whether the user can specify Linq operations on the client to apply on the server (hold that thought, you’ll see more about that in a second). Lets see what this Http request looks like:
———————————————————————————————————————————————
GET /ClientBin/DataService.axd/SilverlightApplication1-Web-DomainService1/SnagAllPeeps?$where=(Age%3e35)&$orderby=Name HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618; .NET CLR 1.1.4322; OfficeLiveConnector.1.3; OfficeLivePatch.0.0; msn OptimizedIE8;ENUS)
Host: localhost.:53937
Connection: Keep-Alive
———————————————————————————————————————————————
You’ll notice that the EntityQuery where we restricted our age to be greater than 35 and ordering the persons by name was sent in the path of the HTTP Get (“?$where=(Age%3e35)&$orderby=Name“). Pretty slick. Let’s take a closer look. Within the DomainContext.Load method some housekeeping is conducted and the call is handed off to the configured DomainClient, which is our good friend (see Part one) the HttpDomainClient, to the BeginQuery method. The parameters and/or IQueryable query are serialized into a string in the HttpDomainClient.GetOperationData method. If the method does not have side effects then an HTTP GET will be used and the operation data (parameters and serialized IQueryable) are packed onto the HTTP Url(which we can see in the HTTP Get dump above). So, the call is made, and we’re off to the server…..
On the server-side, the same (see Part 1) DataServiceFactory we’ve seen is fired up and a DataService is retrieved and initialized. The parameters are pulled out of the querystring for HTTP GET(s), or the Form collection for HTTP POST(s). Internally a class of type DataServiceQueryRequest is used to invoke the request. That class will orchestrate deserializing QueryParts from the HTTP request (if present), and constructs an IQueryable by rebuilding the Linq expressions on the server side (it appears that Ria Services supports “where”, “orderby”, “skip”, and “take” Linq operators currently). This guy builds up a System.Web.DomainServices.QueryDescription object which packages up a pointer to the method, the [optional] parameters along with the [optional] IQueryable query and hands that off to the DomainService.Query method. Inside this method, the target operation is executed, which in this case is our “SnagAllPeeps” method. If an IQueryable is present, then it is applied to the result. In our sample, the SnagAllPeeps method returns 4 results, but after our client-provided IQueryable passes over the wire (to filter by age), then only 2 results are returned from the server, which you can see below in the Http response:
———————————————————————————————————————————————
HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Sun, 27 Sep 2009 16:19:25 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: text/json; charset=utf-8
Content-Length: 430
Connection: Close
{"__type":"DataServiceResult:DomainServices","IsDomainServiceException":false,"Results":[{"__type":"Person:http://schemas.datacontract.org/2004/07/SilverlightApplication1.Web","Id":"951b0038-be14-4338-bb81-f6fa68b315e9","Name":"Bob","Age":41},{"__type":"Person:http://schemas.datacontract.org/2004/07/SilverlightApplication1.Web","Id":"4df1648d-eff8-433f-a2e4-2186ca24ba78","Name":"Joe","Age":38}],"TotalCount":-2,"ResultCount":2}
———————————————————————————————————————————————
You’ll see that in the default DomainService class generated by Visual Studio, there are //TODO tokens indicating the need to add additional queries with parameters. Given what you just learned above, you’d want to refine the original query against a database in the DomainService query, rather than rely on client-side Linq query operators because the latter would retrieve all the rows and filter in the middle tier, whereas the former would allow relational databases to do what they do best (and most efficiently).
Summary
We’ve seen that Query operations are use much of the same plumbing we saw with service operations. Additionally, Query operations bring Linq elements along with serializing IQueryable across the wire, and applying queries against the source retrieved from service operations. In the next post, we’ll dig into the final operation type, the “Submit” operation, along with the C_UD portion of CRUD :) Perhaps others will find this interesting.
This article is really helpfull , could you please let me know how can i make Insert/delete /update also….because in proxy class nothinf is creating for this ……
I plan to do a part 3 on write operations, but in the meantime, to do Inserts, deletes, updates, you make your changes through the various ObservableCollection collections which hang off your client-side Domain Service object(s), and if that Domain Service object’s HasChanged property is true, call the Domain Services’ SubmitChanges() method:
private YourDomainService _ds = new YourDomainService();
if (_ds.HasChanges)
{
_ds.SubmitChanges();
}
Hi Team, i added couple of lines in service method EchoString
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Person));
System.IO.TextWriter writer = new System.IO.StreamWriter(@”C:\Test\entity.xml”, false);
serializer.Serialize(writer, FakeDataAccessLayer.GetAllPeeps());
writer.Close();
return input + ” ” + DateTime.Now.ToString();
i am getting an error value canot be Null , i tried to write the object in to a file….
Hello Roopesh, can you clarify where you are getting the error? Additionally, I’m not sure this relates to Ria Services, and likely speaks more to the process of object serializatio
Great Article. I am curious how to set the HasSideEffects. Do you have an example of that?
Excellent article!
Are the results cached on the client side?
results of queries and what-not are stored in client-side state. That’s one of the “Nice” things about SL applications; statefull coding again:)
I have a very small table (maybe 5 entries) with names in it, and I notice that every time I as for it, a request goes across the wire. Why should that be if results are cached? Is it because I’m in debug mode. I’m using SL 4, EF and VS 2010.
There would be nothing browser-specific here; if you are making a async request to the server, then the request will go over the wire. You could lazy-load a class-level member to store the results of the query such that the request is made 1 time. I don’t recall the specifics off-hand, but Silverlight is leveraging the browser’s networking stack and I believe (although I think some changes were coming for this in SL) the same HTTP semantics [and http caching] apply.
Jason Harper jharpo_revision@hotmail.com blog: http://feeds.feedburner.com/JasonHarpersBlog site: http://www.hollaifyouhearme.com/ twitter: harperj +1 336 391 6093
Thank you so much for answering. What I think I understand is that each explicit request will go across the wire, and if I want to take advantage of client-side caching, I need to save the entity object on the client and use lazy loading. Then maybe I can use top-level members to access related objects that will be loaded automatically as needed, or something like that. I’ll play with it and figure it out.
Thanks again.
Pingback: WCF RIA Services – “The maximum uri length of 2083 was exceeded.” « Matt Duffield