RIA services exposing & consuming queries


Published on

Slides from week 8 of the Inland Empire .NET User's Group Silverlight 4 class

Published in: Technology
1 Like
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide
  • Once the query methods are created in the domain service, let’s see what happens at build time.The build process extension provided by the framework goes through the assemblies in the server project and looks at each service derived class to check whether the EnableClientAccess attribute is present. If so it creates a matching domain context in the Silverlight application for each domain service it finds on the server. The domain context has the same namespace as its server counterpart.Then the tooling looks at each publically exposed query methods in the domain service and it creates a matching query factory method in the domain context on the client.Each query operation that is exposed is examined for the entity type that it returns. As we mentioned before, entity types are concrete classes that have one or more properties annotated with a key attribute and these classes may be generated automatically by the data layer depending what you choose to use.The tooling creates an entity client proxy type for each entity type exposed on the server.The client type has the same namespace and class name as the server entity and exposes the same public members.Code generation provides the client=side proxy with data binding capability through the INotifyPropertyChanged interface and validation capability through INotifyDataErrorInfo. We’ll look at validation in two weeks.Custom attributes that are applied server-side are reflected on the client provided that the appropriate assemblies are made available to the client.Server-side metadata is merged with the entity type and so it is reflected directly in the client-side entity proxies.
  • Probably the most important class generated for us by the build process is the typed domain context. The domain context is the chief orchestrator on the client.Among its generated members we find a series of constructors that allow us to pass various pieces of information to the class such as WCF service uri information, or a domain client instance which is a client side base class that is used to communicate with the server using a specific communication channelThe domain context also contains a set of query factory methods which return query proxies that match the server-side query operations that we’ve defined on the server.Also generated for us is a set of properties that give us access to client-side entity proxies.We also have an entity container factory method which creates an entity container instance
  • Let’s examine how the querying mechanism actually works.When we need to retrieve data from the server what we do is make an asynchronous call from the client using one of the load methods that are exposed by the domain context base class.First we get an instance of the generated typed domain context class.Next we grab one of the client-side proxy queries through one of the query factory methods generated for in the domain context class. That proxy class represents one of the server-side queries we want to invoke.The actual type of that query is EntityQuery. The EntityQuery class encapsulates information about the server query we want to call. We can extend that query through Linq composition right here on the client before sending the query back to the server.Once we get a hold of the query proxy, we pass it to one of the load methods provided by the domain context class which will then send the query data across the network through the client-side infrastructure and then through the WCF infrastructure.When the load call is made, the load method immediately returns an instance of the LoadOperation class. The load operation object represents an ongoing asynchronous load operation.After the call returns, we can access the results of the query asynchronously through load operation entities property. In Silverlight we can bind to that property using a datagrid for example.Now asynchronous is the key word here. It is important to remember that, so lets take a closer look at how that works.
  • There are actually 5 overloads of the load method that can be called. All of them take an EntityQuery parameter which represents the query method invocation.Two of them allow us to pass a completion callback and two allow us to specify how we want the results of the query to update the client-side cache through the LoadBehavior parameter.The overloads that allow a callback are very useful for dealing with the asynchronous nature of the load call. We specify a callback method that the load operation will call whenever it returns.There are at least two ways to specify a callback:One is to pass a delegate or lambda expression as a parameter to the load method. We also pass custom state information to the UserState parameter or you can pass null if none is required.Another way to specify callback is the handle to load operation completed event. Note that you can use an anonymous method or lambda expression for this.All these overloads return a LoadOperation Object, so lets zoom in on that object for a second
  • The load operation exposes an Entities and an AllEntities property which we use to retrieve the loaded data. Through the Entities property we can access the top level entities in the return results set, as well as nested entities within those top level ones. Note that when we use this property, the nested entities get loaded on demand from the client-side entity container provided of course that the object tree has previously been loaded from the server. The AllEntities property on the other hand, gives us access to all the entities in the hierarchy in a flat manner.Load operation also has an EntityQuery property which stores the entity query instance that was passed by the load method, and remember that EntityQuery encapsulates the server-side query that we are invoking,Also the result count returned by the server can be retrieved by the load operation TotalEntityCount property.Load operation ultimately derives from OperationBase, which is the base class for all operation types in the framework including submit and invoke operations which we’ll see next week.OperationBase implements INotifyPropertyChanged so it knows how to raise notifications when the operations complete status is updated and that’s what enables us to bind data to a data grid to the load operation object even before the load call has completed.OperationBase also provides operation canceling and error handling capabilities.
  • Once the asynchronous load call returns from the server, the properties of the load operation instance returned by the load method are populated, but there is another important thing that occurs when the server call returns.The domain context passes the data retrieved by the load call to the entity container for caching on the client.You may recall from last week that Ria services architecture defines a stateful client. State is largely maintained through the entity container in collaboration with the domain context and the entity sets.Entity sets are IEnumerable collections of entities in which the entity container caches the results of the instances. This means we can continue to access the loaded data even after the load call is exited and we do that through a bunch of public properties exposed by the domain context that give us access to the entity sets.Each of these properies actually returns a list of entities stored in the client cache which is the entity container.The entity container manages the list and tracks changes in them.Entity sets contain entity instances. Remember the tooling has generated in the domain context some entity proxy classes based on the domain entities returned by the domain service operation.Each of these client-side type are derived from the entity base type which has change tracking, validation, data binding, and updating capabilities.Both entity and entity set implement INotifyPropertyChanged, so just like the load operation object we can bind to our entities even before the data has finished loading from he server and then the UI gets notified when the operation is complete.
  • Let’s take a look at the query proxy that is returned by the query factory method that gets generated by the tooling at build time. Remember, these query proxies are the ones we passed the Load method to invoke the corresponding server side operation.There type is EntityQuery<T>. Now before passing an entity query to the load method, we can actually extend it on the client. We mentioned that the EntityQuery class encapsulates information about the server-side query to invoke. That includes the query name and parameter information. EntityQuery also exposes a query property that can store an Iqueryable client-side query that we apply before invoking load.The entity query class has the capability of taking a client-side Linq query, store it and ultimately send that query across the network through the call stack so that it can be applied on the server.EntityQuery actually acts like an Iqueryable object on the client, and this capability is provided to it by the EntityQueryable extension class. Its extension methods allow us to apply a few specific Linq operators such as … and all this on the client.What this means is that we can filter and shape our query before it is passed on to he server.When the server call is made, along with the server-side name and parameters, the client-side Iqueryable is serialized and transmitted on the wire. The query sent to the DAL then combines both the server part and the client part of our request.This is a powerful part of Ria services as it allows us to shape the data, if we wish to, as close to the client as possible. Even though we can shape the data on the client, execution takes place on the server so we avoid the common pattern of having to haul back heavy loads of data only to discard large amounts of that data through pure client-side filtering.This is an important aspect of the framework as it provides the base capabilities for things such as the domain data source control.As we’ll see later in this course, using the domain data source control we can filter, sort, group and page data in a very simple and powerful way, and these features are enabled by this end-to-end query design that is part of the Ria services framework.
  • Ria services allows us to retrieve object trees containing related entities in our queries. This simplifies classic scenarios such as master-detail scenarios and in general allows us to retrieve related objects together with the root object.There are actually several types of relationships that are facilitated by the framework including more complex ones such as composition relationships that exist between an order and its line items, and inheritance items. We are going to limit the talk to a basic association relationship that exist between two entity types.To enable the client to retrieve related entity types we need to take a couple of steps.First we need to annotate the server side entity types with the include attribute. If the entity types are generated automatically in our project, for example if we use EF or L2S as our data layer, we can do the attribute annotation in a metadata class that is associated with the root type instead of directly in the root type definition.The second thing we need to do is to explicitly include the related entities with the root entities and the results of our query operation in the service. Typically we do this using the Linq operator. As a result, the query operation will return an object tree that contains related entities along with the query and root entities.Once we build the project, the object tree becomes available on the client. The client’s entity proxy now has new properties that return the related entities that were specified on the server and these properties are annotated with the association attribute on the client that allows us to traverse the tree and access related properties client side.
  • Now that we’ve looked at how to create and send queries from our Silverlight client to our domain service, let’s take a quick look at what happens behind the scenes.There are actually a slew of classes that participate in the query call on both sides of the network. On the client side, the domain context is agnostic of the communication stack. It uses a sub-class of domain client to set up the actual asynchronous call through the WCF pipeline. By default, it is the Web Domain Client that is used which knows how to create a WCF client channel using a channel factory and a service contract that was copied to the domain context at build time.On the server side an http module intercepts the query call from the client and dynamically creates an in-memory svc file.The in-memory svc file is based on the service contract that is shared with the client.In a previous step, the contract was generated by our domain service by a domain service host which changed the query operation return type from the one we specified in the domain service to a QueryResult<T> that contains additional information besides the result of the query.The domain service host also hosts the service and by default a binary encoded REST type endpoint is exposed
  • RIA services exposing & consuming queries

    1. 1. Week 7 – WCF RIA Services Overview<br />Jim LaVine<br />jim.lavine @gmail.com<br />
    2. 2. Remaining Schedule<br />1-8 Exposing and consuming querying services<br />1/15 Updating data<br />1/22 Business Logic and validation<br />
    3. 3. Agenda<br />Domain query rules<br />The DomainContext Class<br />Asynchronous loading<br />Client-side data caching<br />Shaping data in the client<br />Retrieving object hierarchies<br />A peak under the hood<br />
    4. 4. Design-time workflow<br />Server project<br />Client project<br />Domain Service<br />CRUD<br />Domain Service<br />Domain Context<br />Load<br />2<br />Invoke<br />Custom <br />logic<br />Build<br />Entity Container<br />Data model<br />Metadata<br />Entity<br />Metadata<br />Entity proxy type<br />3<br />Metadata<br />Entity<br />Metadata<br />Entity proxy type<br />Metadata<br />Entity<br />Metadata<br />Entity proxy type<br />1<br />DAL<br />Bindable<br />UI Views<br />L2S<br />EF<br />REST/SOAP<br />POCO<br />4<br />
    5. 5. Domain query operation rules<br />IEnumerable<br />IQueryable<br /><T><br />    [EnableClientAccess()]    public class ChinookDomainService : LinqToEntitiesDomainService<ChinookEntities>    {                public  IEnumerable<Invoice> GetInvoiceByCustomer(int customerId)        {    return ObjectContext.Invoices.Where(c => c.CustomerId == customerId); }        public Customer GetCustomerById(int customerId)        {    return ObjectContext.Customers.Where(c => c.CustomerId == customerId); }    }<br />public<br />Supported<br />types<br />Entity<br />         public  IEnumerable<Customers> GetCustomersByJoinData(DateTimemindate, DateTimemaxdate)        {    <br />return ObjectContext.Customers.Where(c => c.JoinDate >= mindate && c.JoinDate <= maxdate); <br /> }<br />No<br />Overloads<br />
    6. 6.          public class  Customers        {    <br /> [Key]<br />public int CustomerId { get; set; } <br /> public string FName{ get; set; } <br /> . . .<br /> }<br />Domain query operation rules<br />Identity<br />
    7. 7. Convention vs. configuration<br />Query operation explicit marking : QueryAttribute<br />Signature match : returns IQueryable<T>, IEnumerable<T>, T<br />QueryAttribute parameters<br />IsComposable<br />ResultLimit<br />HasSideEffects<br />         [Query]<br />public Iqueryable<Customers>  GetCustomersByState (BillingStatestate)  { . . . }<br />public Iqueryable<Customers>  GetCustomersByState (BillingStatestate)  { . . . }<br /> [Query (IsComposable=false)]<br />public SalesPerson GrabTopSalesperson (intyear)  { . . . }<br />
    8. 8. Demo<br />Defining domain queries<br />
    9. 9. Code generation<br />l<br />Server assemblies<br />Silverlight Client<br />Silverlight client<br />Namespace<br />DomainContext types<br />Query factory methods<br />DomainService types<br />[EnableClientAccess]<br />Public methods<br />Namespace<br />Class name<br />Public props<br />Entity type<br />Entity type<br />Entity type<br />Entity proxy<br />Entity proxy<br />Entity proxy<br />InotifyPropertyChanged<br />Validation<br />Metadata<br />Custom Attribute<br />Metadata class<br />Custom Attribute<br />
    10. 10. The typed DomainContext class<br />Constructors<br />Query proxy methods<br />Entity proxy properties<br />Entity container factory<br />
    11. 11. Demo<br />Examining generated classes<br />
    12. 12. Asynchronous loading<br />ChinookDomainContext ctx = newChinookDomainContext();<br />varquery = ctx.GetInvoicesByCustomer (1) ;<br />LoadOperation<Invoice>op = ctx.Load(query);<br />EntityQuery<br /><Invoice><br />CustomerGrid.ItemsSource = op.Entities<br />LoadOperation<br /><Invoice><br />IEnumerable<Invoice><br />
    13. 13. The Load method<br />Load method overloads<br />Load(EntityQuery<TEntity>)<br />Load(EntityQuery<TEntity>, LoadBehavior, Callback, UserState)<br />Load(EntityQuery<TEntity>, Callback, UserState)<br />Load(EntityQuery<TEntity>, ThrowOnError)<br />Load(EntityQuery<TEntity>, LoadBehavior, ThrowOnError)<br />LoadOperation<Invoice> op = ctx.Load<Invoice>(query,<br />lo => { /* Check for errors, bind to UI, do work */},<br />null);<br />ctx.Load<Invoice>(query).Completed += newEventHandler(LoadComplete);<br />voidLoadComplete(object sender, EventArgs e)<br />{/* Check for errors, bind to UI, do work */ }<br />
    14. 14. LoadOperation<br />Load Operation:<br /><ul><li>AllEntities
    15. 15. Entities
    16. 16. EntityQuery
    17. 17. TotalEntityCount</li></ul>OperationBase:<br /><ul><li>INotifyPropertyChanged
    18. 18. IsComplete, Completed, event
    19. 19. Cancel(), CanCancel, IsCanceled
    20. 20. Error, HasError</li></li></ul><li>Demo<br />Loading data<br />
    21. 21. Caching query results<br />EntitySet<br />property<br />Load()<br />Entity<br />Container<br />Domain<br />Context<br />Query<br />Results<br />Send<br />query<br />Query<br />Results<br />Entity<br />Set<br />Entity<br />Set<br />Entity<br />Set<br />Domain<br />Service<br />INotifyProperty<br />Changed<br />Change tracking<br />Validation<br />Data binding<br />Updating<br />Entity<br />Entity<br />Entity<br />
    22. 22. Demo<br />Binding to cached data<br />
    23. 23. Load options<br />Changes (local)<br />Changes (remote)<br />Load()<br />LoadBehavior:<br />-KeepCurrent<br />-MergeIntoCurrent<br />-RefreshCurrent<br />Entity Container<br />Entity<br />Set<br />?<br />
    24. 24. Demo<br />Using LoadBehavior<br />
    25. 25. Client side data shaping<br />EntityQueryable<br />(extension)<br />QueryName<br />Parameters<br />Query<br />IsComposable<br />EntityQuery<T><br />e.g. GetProductsQuery()<br />LINQ operators<br />(Where, Skip,Take, OrderBy)<br />LINQ operators<br />(Where, Skip,Take, OrderBy)<br />Client<br />Server<br />Server query +<br />Client-side query<br />combined<br />Domain<br />Service<br />
    26. 26. Demo<br />Filtering data<br />
    27. 27. Retrieving object hierarchies<br />Server<br />Client<br />Server-side<br />Entity type<br />Domain Service<br />IQuerable<foo> GetFoos()<br />Entity<br />Container<br />[IncludeAttribute]<br />Include<br />foo1<br />foo1<br />[Association]<br />new props<br />Metadata<br />Class<br />
    28. 28. Demo<br />Returning related data<br />
    29. 29. A look behind the scenes<br />Domain<br />Context<br />Domain<br />Service<br />BeginQuery()<br />Domain<br />Service<br />HostFactory<br />Web<br />Domain Client<br />Domain<br />ServiceHost<br />Operation Description<br />QueryResult<T><br />WCF Channel Factory<br />WCF Service<br />Contract<br />REST/binary endpoint<br />WCF Service<br />Contract<br />Virtual.SVC file<br /><%@ServiceHost Service=“XYZService Factory=“System.Web.Ria.<br />DomainServiceHostFactory”%><br />WCF Client Channel<br />DomainService<br />HttpModule<br />
    30. 30. Summary<br />Integrated Infrastructure<br />Signature-based conventions<br />Metadata-specific object heirarchies<br />Simplified asynchronous calls<br />Client-side data caching<br />Integrateing data shaping<br />REST-based default configuration<br />