Apr 3 2012

Mvc Controls Toolkit Support to Mvc4 WebApi

Category: Asp.net | MVC | WCF | WebApiFrancesco @ 03:57

The new Mvc 4 ApiController, and in particular the possibility it offers to expose an IQueryable on the web simplifies the exchange of data between server and an Html page, a lot.

Now it is possible to pass a dynamically created query from the client to the server with a simple GET operation. We need just to fill the $filter, $orderby, $skip, and $top query string parameters according to the oData protocol.

The upshot.js library may help us on the client side to write the code to interact with the server. Upshot does actually all the job, and make the developer interacts with the data as they were on the client, hiding all hard plumbing, needed to retrieve them, and to update them on the server.

However, the price we pay, to take advantage of all upshot.js features is that we can’t have substantially any business layer on the server side, whose role becomes a kind of “Web Database”.

What if we need a “robust” Business Layer, that manipulates data coming from several collections and/or objects before passing a transaction to the database?

At moment upshot.js contains DataProviders for interacting with:

  1. DbDataController. That is a particular ApiController specialized in interfacing DataBase data.
  2. oData sources. A complete oData source can be implemented with Wcf Data Services. In fact both a generic ApiController exposing an IQueryable, and the specialized DbDataController doesn’t implement the full oData protocol, but just support the $filter, $orderby, $skip, and $top operations, that are the more useful operations in business applications.
  3. Wcf Ria Services. The famous Ria services we use also with Siverlight.

One might think to build a custom DataProvider, and pass it to Upshot.js….However, a similar solution would never give us the “kind of customization” we need in this case. The point is that we can no more look at the server like a kind of “Web DB”, so we cannot use upshot and the DbDataController at all, and we need to proceed manually by building our custom jQuery ajax calls to generic WebApi methods.

However, there is some “hard plumbing job” we can factor out also in this case, namely:

  1. Building oData queries in a more friendly fashion with a LinQ like fluent syntax
  2. Changes tracking on the data. Recording the initial state of our data, so that we can undo all changes performed on them
  3. Computation of the Change Set to send to the server. This way we can save bandwidth by sending to the server just the modifications performed on data, instead of a whole collection of data
  4. Dispatching the errors returned by the sever in the right place in the User Interface, that is near the UI that displays the “wrong” inputs. Possibly, in the right labels rendered by the ValidationMessageFor helpers, and in the ValidationSummary.
  5. Since Json is not able to encode references, that is several properties pointing to the same instance of an object, but just a flat tree of objects, wee need someway to re-compute references links on the client after different collections containing related data have been sent by the server. Some tool that might help us in performing this job on the client might be very useful

Finally if we would like to take full advantage of WebApi also in business applications that manipulate big quantity of data we need to protect someway the IQueryables that we expose on the web to prevent denial of service attacks.

We can decorate our methods with the ResultLimitAttribute to limit the maximum number of items retrieved by the DB, However this is not enough, since a malicious user might try a denial of service attack by requiring a filtering operation on a column without indices of a big DB table. In this case also if no result is found at all the Data Base might waste a lot of time to discover it.

In the new Mvc4 beta compatible version of the Mvc Controls Toolkit we included tools that face all the problems listed above, namely:

  1. The javascript mvcct.Queryable abstract class allow us to build complex queries with a LinQ like fluent syntax. Moreover, it is able to import sorting, filtering, and paging information from all Mvc Controls Toolkit controls. There are three implementation of this abstract class:
    • The mvcct.oData.Queryable to query remote datasources that implements (also partially) the oData protocol. This is the right tool to use to query WebApi that expose an IQueryable
    • The mvcct.upshotQueryable build queries to be executed by the upsjhot.js library.
    • The localQueryable is able to query data that are already on the client in javascript format. It is useful when we need to work offline
  2. The mvcct.updateManager is able to track the changes of a collection of data, to compute the associated Change Set and to post it to a generic WebApi or standard Mvc Action Method. It is also able to transfer the data to the server with a standard browser submit (it creates dynamically a form that it then submits). Moreover it wait for the result from the server, and process possible errors returned by the server, by dispatching them in the right places of the UI. The developer may also provide two optional callbacks to be called respectively in case of success and in case of error. The error processing can be skipped by setting a property contained the callback arguments.
    Several mvcct.updateManager may cooperate to submit several change sets to the server with an unique post. In this case each mvcct.updateManager prepares the Change Set for the collection it takes care of, and put it in the destination ViewModel. Then, just one of them, the master, posts this ViewModel and wait for the server result . Once the result arrives it notifies all other mvcct.updateManager so that each of them can process its errors, and its optional callbacks.
  3. The addRelated method of the mvcct.updateManager computes automatically all mutual references existing between the items of two related collections by using key and external key information.
  4. The SafeQuery and HttpSafequery implementations of the IQueryable interface work as wrappers to protect any IQueryable from unwished queries. The allowed sorting and filering operations are specified through the CanSort Mvc Controls Toolkit attribute. In case a forbidden operation is detected the SafeQuery throws the adequate subclass of the ForbiddenException, while the HttlpSafeQuery that is specialized for the Web throws an HttpResponseException to set the right Http status code.

But let see how all this works in practice with a simple example. The example I use can be downloaded from the Mvc ControlsToolkit download area here: Mvc4 Client- Filtering -Paging -Sorting-updating. The file AdvancedJSonCommunication contains similar examples that works with standard Mvc3 contollers.

As first step let ‘s run the example to see what happens. We can select two pages in the menu: Index, and IndexEdit.The first one is a query and display only example, while the second one gives also the possibility to update the entities. Let start with the Index page.

ToDoDisplay

There is a pager, we can sort by clicking on the columns, and we can filter data by selecting several filtering options simultaneously and then by clicking Refresh

Let select filter by name, and let choose the Contains operator.

ToDoFiltering

Looking for a string contained in any position of a column is a very inefficient type of search if we have not defined a special index called full text index on that column. So it might be used for a denial of service attack against our website. However, when we  hit refresh we get:

ToDoError

A 403 Http status code: FORBIDDEN! The message explains us that the Contains operation is not among the operation allowed on the colum name.

Let see what happened behind the curtain:

  1. public class ToDoController : ApiController
  2.     {
  3.         // GET /api/todo
  4.         public IQueryable<ToDoView> Get()
  5.         {
  6.             return new HttpSafeQuery<ToDoView>(ToDoViewModel.GetToDoQueryable());
  7.         }

We wrapped our IQueryable within the HttpSafeQuery IQueryable that rejected the Contains clause of our filter.

The data annotations on the ToDoView class are provided through a MetaDataType(to understand why I did ths way, give a look here):

  1. [MetadataType(typeof(MetaToDo))]
  2.     public partial class ToDoView
  3.     {
  4.         public int? id { get; set; }
  5.        
  6.         public string Name { get; set; }
  7.        
  8.         public string Description { set; get; }
  9.  
  10.        
  11.         public DateTime DueDate {set;get;}
  12.  
  13.     }

So Let go to the MetaToDo class:

  1. public class MetaToDo
  2.     {
  3.         
  4.         [Required, CanSort, Display(Name="Name")]
  5.         public object Name { get; set; }
  6.         [Required, Display(ShortName = "Description")]
  7.         public object Description { get; set; }
  8.         [CanSort, Display(ShortName = "Due Date"), Format(DataFormatString="{0:D}")]
  9.         public object DueDate { get; set; }
  10.     }

The Name property is decorated with the CanSortAttribute, but since no argument is passed to specify wich filtering options are allowed, the default settings are taken…and the default setting doesn’t allow the dangerous Contains operator.

Now, let give a look to the way the query is built. We created the mvcct.oDataQueryable  object in the View to take advantage of the @Url.RouteUrl method to conpute the Url of our WebApi:

  1. <script type="text/javascript">
  2.     var query = mvcct.oDataQueryable('@Url.RouteUrl("DefaultApi", new { httproute = "", controller = "ToDo"})', mvcct.$$.and);
  3.     query.importSortingControl('@Html.PrefixedId(m => m.ToDoOrder)');//import default sorting (necessary for paging
  4.  
  5. </script>

The second parameter of the constructor is the logical operator to be used to combine all filter conditions. We might have specified also mvcct.$$.or. There is also a third boolean parameter that we have not used in this example, negate. If true the whole filter is negated.

The first operation we do immediately is importing the initial sorting specified in the control that is bound to the ToDoOrder ViewModel property. It was put there by the action method that took care of creating the Index View:

  1. public const int PageDim=5;//in actual application this should be put in a config file
  2.         public ActionResult Index()
  3.         {
  4.             //define default ordering. It is necessary for paging.
  5.             List<KeyValuePair<LambdaExpression, OrderType>> order = new List<KeyValuePair<LambdaExpression,OrderType>>();
  6.             Expression<Func<ToDoView, DateTime>> defaultOrder = m => m.DueDate;
  7.             order.Add(new KeyValuePair<LambdaExpression, OrderType>(defaultOrder, OrderType.Descending));
  8.  
  9.             ToDoViewModel result = new ToDoViewModel()
  10.                 {
  11.                     DataPage = new ToDoPage {
  12.                         ToDoList=new List<ToDoView>()},
  13.                     ToDoOrder=order
  14.  
  15.                 };
  16.             return this.ClientBlockView(result, "ClientToDoView");
  17.         }

As you can see this action method just creates the substantially empty ViewModel that will be used as page ViewModel by the knockout.js library

The adviced way to use knockout.js with the Mvc Controls Toolkit is through the Client Blocks feature. For more information on the sorting Mvc Controls toolkit controls give a look here.

The remaining code used to build the query is in the WebApiRetrieve.js file:

  1. ClientToDoView.Refresh = function (type, page, sorting, prevPage) {
  2.         if (!type) {
  3.             type = 'filter'; page = 1;
  4.             ClientToDoView.DataPage.CurrPage(1);
  5.             //filter changed, we need reset pager to the first page
  6.         }
  7.         query.setPaging(page, pageSize);
  8.         //sorting changed
  9.         //we don't need to reset pager to the first page, sorting control do it automatically
  10.         if (type == 'sort') query.importSorting(sorting);
  11.         else if (type == 'filter') {
  12.             query.resetFilter()
  13.             .importClauses('ToDoFilter');
  14.         }
  15.         query.execute(function (x) {.......

The Refresh method of the ViewModel is called either when the filter, or the sorting or the page is changed, so the first thing it does is “understanding” what changed. Then it imports into the query the changed information. The pager and the sorting controls triggers an event containing the new page and/or sorting information when something changes, so we need just to register an event to handle sorting and paging:

  1. $('#root').bind('queryChanged', function (e, data) {
  2.         ClientToDoView.Refresh(data.type, data.page, data['sortString'], data['previousPage']);
  3.     });

sortString, and previousPage are put within [‘’] because the sortString property is provided just for the sorting changed event, while the previousPage is provided just for the page changed event, so both properties may be undefined.

There is no event that says us the when filtering condition changes, so the best we can do is binding the refresh method of the ViewModel to the Refresh button through a knockout click binding:

  1. <input id="Button1" type="button" data-bind='click: function(x){x.Refresh();}' value="Refresh" />

The mvcct.Queryable.importClauses imports all filtering conditions by inspecting directly all filtering controls that are bound to the  ToDoFilter ViewModel property. For an introduction to the MvcControls Toolkit Filtering controls give a look here.

The execute method issues the query to the WebApi controller, and receives the results through a callback, whose code is not shown.

Basically the callback inspects the format of the object that is returned to see if the total count of all entities is available (this information may be used to improve the paging experience). WebApi methods doesn’t supplies this information and return just a collection of entities, but full implementation of the oData protocol like the Wcf Data services may return it. Then they add observables and observableArray, as needed by calling the ko.mapping.fromJS function of the knockout mapping plugin:

  1. var newEntities = ko.mapping.fromJS(x.results)();

and put them into the ViewModel:

  1. ClientToDoView.DataPage.ToDoList(newEntities);

The total count of the entiies is updated either with the exact information supplied from the server, if available, or with a smart guess:

  1. ClientToDoView.DataPage.TotalPages(totPages);

The pager is bound to the DataPage.TotalPages ViewModel property, and immediately takes advantage of this new information. In case of errors the pager is reset to its previous page:

  1. ClientToDoView.DataPage.CurrPage(prevPage);

The pager is bound to this ViewModel property, and it updates its state immediately. The ko bindings of the pager are automatically created by the pager Html helper.

In case we would like to use the upshot.js library to query our controller, we can stll import paging, filtering, and sorting infos from the Mvc Controls toolkit controls by using the upshotQueryable in place of the the oDataQueryable, since both objects share the same public interface.

The constructor in this case is: mvcct.upshotQueryable(dataSource, fop, options, negate). Where dataSource is an upshot datasource, fop and negate are completely ignored since upshot allows just the logical and of all filter conditions. Finally the option object contains only the includeTotalCount property whose default value is true;

we can also build our query manually with the help of the whole fluent public interface exposed by all mvcct.Queryable objects:

  • setPaging(page, pageSize); Specifies current page and page size. we already used it in our example.
  • addSort(field, desc, enabled): if enabled is true adds a new orderby clause to the order being built. field is the name (actually a string expression) of the property, and the boolean desc specifies if the order is descending.
  • resetSorting():clears all previously added sorting clauses, so that a completely new sorting can be built.
  • addCondition(operator, value1, value2, enabled): if enabled is true a new filtering condition is added to the filter being built. All filter conditions are combined with the currently active logical operator. The initial logical operator is specified in the constructor of the Queryable. A different logical operator can be specified each time we build a complex condition made of several sub-conditions with the help of the open and close methods (see later), value1 is the property to be constrained by the condition,  value2 the second argument of the comparison , and operator is the condition operator  that is one of:
    • mvcct.$$.eq, mvcct.$$.ne, mvcct.$$.gt, mvcct.$$.ge, mvcct.$$.lt, mvcct.$$.le;
    • mvcct.$$.startswith, mvcct.$$.endswith
    • mvcct.$$.substringofInv; that is the Contains operator.
    • mvcct.$$.substringof; that requires the property value is a substring of the value2 argument
  • resetFilter(): clears all previously added filter clauses, so that a completely new filter can be built.
  • open(logicalOperator, enabled, negate): if enabled is true, it starts a complex condition made of several sub-conditions that will be combined with logicalOperator. If negate is true the whole condition will be negated. Its effect is opening a parenthesys in the filter expression being built.
  • close(enabled): close a previously opened parenthesys. enabled must match the enabled of the matching open otherwise the parenthesys of the resulting expression will be unbalanced.
  • importSorting, importSortingControl, and importClauses are the Mvc Controls Toolkit controls importing methods we have already used in our example. There is also an importPager(pagerId, pageSize) method that imports the paging data contained in the pager whose id is pagerId. we have not used it in the example because we took the all page informations from the event triggered by the pager.
  • get(): returns an object whose nature depends on the implementation of the Queryable: the oDataQueryable returns the complete url to be passed to the server, the localQueryable returns a function that once executed performs all operations specified in the query, and the upshotQueryable returns the upshot DataSource.
  • execute(callback): executes the query and passes the result returned by the server as argument to the callback. The upshotQueryable ignores the callback because the only operation performed is the refresh of the DataSource: this is enough to dispatch the server results in the right place.
  • getState() and setState(x) respectively returns an object that encodes the full state of the Queryable, and set the state of the Queryable by using a previously saved state. They are useful to handle the Back and Forward buttons of the browsers to navigate the history of all query passed to the server.

It is time to understand how to handles data updates with the updateManager class. In our example the creation of the  instance of the updateManager that takes care of our ToDo items is in the IndexEdit view:

  1. ClientToDoView.updater = mvcct.updatesManager(
  2.         '@Url.RouteUrl("DefaultApi", new { httproute = "", controller = "ToDo"})',
  3.          ClientToDoView,
  4.          'DataPage.ToDoList',
  5.          'id', null, null, {isoDate:true, updateCallback: function (e, result, status) { alert("status: " + status); } });

The first argument just computes the Url of our WebApi, the second argument is the javascript variable that contains our page ViewModel, the third argument is a string expression that locates the collection we would like to process. The fourth argument is another string expression that locates the “key” of our items. It doesn’t need to correspond to the actual DB key of the item, but it is just the field that we want our updateManager send to the server when an item is deleted.

The further two arguments that we set to null are respectively, the destination ViewModel and a string expression that locates the place in the destination ViewModel where to put the change set once it has been computed. Since both fields are null there is no destination ViewModel, and the change set will be sent to the server, as it is , without inserting it into another container. The destination ViewModel is useful if we need to send simultaneously several change sets to the server.

The change set is an object with just 3 properties: one containing the array of all modified items, another one containing the array of all newly inserted items, and the last property contains the array of the “keys” of all deleted items. The names of these properties can be gonfigured in the option argument of the constructor of the updateManager (the last argument). The default option object is:

  1. {
  2.     updater: {u: "Modified", i: "Inserted", d: "Deleted"},
  3.     isoDate:false,
  4.     updateCallback: function(e, result, status){},
  5.     updatingCallback: function (changes, modelToPost, expr){return changes;}
  6. }

The first property contains the names of the three fields of the change set object.

The second property specifies if the date format to use when sending the change set to the server is the ISO format or the \/Date(….)\/ format. When sending data to a standard Mvc controller we have to set isoDate to false, while in our case we can’t use the default since WebApi controllers need the ISO format.

The updateCallback is called just before processing possible errors returned by the server, and it receives:

  • e. an object with format:
    { setErrors: true, model: sourceViewModel, expression: sourceExpression, key: keyExpression, success: !result.errors }
    If in our callback we set the setErrors property to false, error processing is not carried out, and we have to process them with our custom logics.
  • result. The result returned by the server, that must  be an object containing an error property with the list of all errors and an optional insertedKeys property with the keys created dynamically when inserting new records in the DB. Both the errors and the keys received from the server are processed automatically. That is, the errors are dispatched in the right places of the UI, and the keys to all newly created items that need them.The result object can contain also further properties that can be processed in a custom way  in the updateCallback.
  • status: the Http status of the server response. In our example we specify a callback that just shows the status with an alert window.

The updatingCallback is invoked just before sending all data to the server. If this callback returns a false, null, or undefined value the update is aborted. The updatingCallback receives three arguments:

  • changes: a boolean that informs us if any change was detected
  • modelToPost: the model that the updateManager is going to post
  • expr: a string expression that locates the just computed changes set within the above model.

Let give a look to our receiving action method:

  1. HttpResponseMessage<ApiServerErrors<int>> Post(Updater<ToDoView, int> model)

The Updater generic class that is defined in the MVCControlsToolkit.Controller namespace is just the server side equivalent of our javascript change set class. It is just an helper class since we can use any class to receive the change set. It is enough to specify the right property names in the updateManager option object.

We can use also a complex object containing several change set objects that matchs our javascript destinationViewModel, as parameter  to deal with several simultaneous collection updates. Moreover, we are not limited to using webApi methods but we can use also standard controller action methods.

The full code of the controller method  is:

  1. public HttpResponseMessage<ApiServerErrors<int>> Post(Updater<ToDoView, int> model)
  2. {
  3.     //uncomment to experiment server side error handling
  4.     //ModelState.AddModelError("Modified[0].Name", "Fake error");
  5.     int[] insertedKeys;
  6.     if (ModelState.IsValid)
  7.     {
  8.         insertedKeys=ToDoViewModel.UpdatePage(model.Inserted, model.Modified, model.Deleted);
  9.         
  10.     }
  11.     else
  12.     {
  13.         insertedKeys = new int[0];
  14.     }
  15.     return new ApiServerErrors<int>(ModelState, insertedKeys).Wrap();
  16. }

If the ModelState is valid we call a business layer method, otherwise we abort business computation and return just the list of errors. It is enough to invoke the constructor of the server side equivalent of our javascript result  object passing it the ModelState, and an array with the keys of all newly created items, to create our result object filled with all errors and/or keys. The call to the Wrap method embeds this object into a response containing an appropriate Http status code. In case we have a complex destinationModel with several change sets, we can use a different overload of the constructor that accepts several arrays of keys, each with a string expressoin that specifies the change set of the destinationViewModel it refers to, Namely:

  1. public ApiServerErrors(ModelStateDictionary origin, ApiKeyInfos<T>[] insertedKeys, string prefix = null)

where:

  1. [DataContract]
  2. public class ApiKeyInfos<T>
  3. {
  4.     [DataMember]
  5.     public string destinationExpression { get; set; }
  6.     [DataMember]
  7.     public T[] keys { get; set; }
  8. }

There is also a not generic version of the ApiServerErrors class that we can use when we don’t need to send keys to the client.

The ApiServerErrors class is contained in the MVCControlsToolkit.Controller namespace together with the analogous ServerErrors class that we can use with standard controllers action methods.

Let uncomment the line that creates a fake error and let see how errors are dispatched to the client:

ToDoEditing

Let modify a couple items, and let add a new one, then let hit submitAllChanges.

since the user input was wrong we get a 400 status code (BAD REQUEST):

error400

Then since the property we added the error is: Modified[0].Name, the error is dispatched to the ValidationMessageFor label next to the name of the first modified item:

ValidationError

Since we had no luck with our changes we can decide to undo everything by hitting the Undo All Changes button: Everything is cleared…changes and errors.

How does Undo and Change Set computation work? How to enable them in our applications?

The first step is to prepare all items that we receive from the server:

  1. var newEntities = ko.mapping.fromJS(x.results)();
  2. if (ClientToDoView['updater']) {//if entities may be modified and sent back to the server, prepare them
  3.     ClientToDoView.updater.prepare(newEntities, true);
  4. }

or that we create on the client:

  1. detailToDo.saveAsNew = function () {
  2.     if (!$('#detailForm').validate().form()) return;
  3.     var item = ko.mapping.fromJS({
  4.         DueDate: this.DueDate(),
  5.         Name: this.Name(),
  6.         Description: this.Description(),
  7.         id: null
  8.     });
  9.     ClientToDoView.updater.prepare(item, true);//newly created entity prepare it
  10.     ClientToDoView.updater.inserted(ClientToDoView.DataPage.ToDoList, item);
  11.     this.reset();
  12. };

Then, we must call the inserted method to insert them in our collection as shown above,  the deleted method to delete them:

  1. detailToDo.remove = function (item) {
  2.     this.resetIfSelected(item);
  3.     ClientToDoView.updater.deleted(ClientToDoView.DataPage.ToDoList, item);
  4. };

and the modified method each time the user manipulate them someway:

  1. detailToDo.save = function () {
  2.     var item = this.DetailOf();
  3.     if (!item) return;
  4.     if (!$('#detailForm').validate().form()) return;
  5.     mvcct.utils.restoreEntity(this, item);
  6.     ClientToDoView.updater.modified(item, true, true);
  7.     this.reset();
  8. };

The second and third argument of the modified method say respectively, to prepare the entity if it is not yet prepared(actually we don’t need it since we know entities have been already prepared), and to do an immediate verification of all changes. Thus all properties of our entity are compared with their old values to verify if an actual change occurred, and only if the entity actually changed it is marked as modified. If the third argument of the modified method is false, our entity is marked as modified without performing any immediate verification; the verification is deferred till the time the updateManager compute the change set.

The mvcct.utils.restoreEntity is an utility method of the Mvc Controls Toolkit  that copies an object into another object having its same structure, properly handlig all ko observables. As a default nested objects are visited and their properties copied but arrays are not. However, if its second optional argument is set to true, references to arrays are copied too.

Preparing a property add it two observable properties: _inserted and _modified, that say us the state of the entity. They are used by the routines that compute the change set, but we can use them also to improve the user experience. In our case we bound them to the enabled status of the undo button:

  1. <input type="button" value="Undo" data-bind='click: function(item){detailToDo.undo(item);}, enable: _inserted() || _modified()'/>

If in the call to the prepare method we pass true as second argument(as we have done), changes tracking is turned on, and the entity will remember its initial state. If changes tracking is off no undo is possible, and an entity is marked modified when we call the modified method without performing any check.

Below how to undo a single entity:

  1. detailToDo.undo = function (item) {
  2.     this.resetIfSelected(item);
  3.     ClientToDoView.updater.reset(item);
  4. };

it is enough to call the reset method of the updateManager. The updateManager has also a resetAll method that restore the initial state of the whole collection:

  1. ClientToDoView.undoAll = function () {
  2.     ClientToDoView.updater.resetAll($('#mainForm'));
  3.  
  4. };

It accepts, as argument, a jQuery object containing a form, that it uses to clear all errors. The state of the errors returned by the server can be also cleared manually each time we need by calling the clearErrors(jForm) method of the updateManager. In our example we do it when we perform a new query to the server:

  1. if (ClientToDoView['updater']) {//clear previous errors, since entities shown on the screen will change
  2.     ClientToDoView.updater.clearErrors($('#mainForm'));
  3. }

we can require manually also the dispatching of errors by calling the refreshErrors(jForm, errorState) where errorState is an object with the same format as the result returned by the server. If errorState is null the last errors returned by the server will be used.

The change set can be submitted to the server either by calling the update(jForm) method:

  1. ClientToDoView.save = function () {
  2.     ClientToDoView.updater.update($('#mainForm'));
  3. };

or by calling the submit(jForm) method, in which case a not-ajax normal browser submit is triggered. The form submitted is dynamically created and contains just the destinationViewModel data.Examples showing the use of the submit method and the use of the updateManager with standard controllers action methods are contained in the file Advanced JSon Communication in the download area of the Mvc Controls Toolkit

Both the update and the submit methods have a second argument: isDependent. It is used when submitting simultaneously multiple change sets to the server into an unique destinationViewModem. When isDependent is set to true the updateManager just compute the change set and insert it in the right place in the destinationViewModel without performing any operation on the server. The operation on the server is performed by an unique updateManager, called with isDependent set to false, that acts as Master. When we call the update method the Master dispatches automatically the results returned by the server to the Slave updateManagers, thus causing they update their, entities, their errors, and call their updateCallBack. The array of all Slaves to notify is passed as third argument of the update method.

The addRelated method of the updateManager can help us in handling multiple related collection on entities:

  1. addRelated(collectionExpression, entities, entitiesExternalExpression, inverseCollectionExpression, overrideKeyExpression)

It computes all mutual references existing between the items of two related collections by using key and external key information:

  • entities is the array(or ko observable array) with the collection we would like to relate with the collection handled by the current instance of the updateManager.
  • collectionExpression is a string expression specifying the array contained in each entity of the collection handled by the current instance of the updateManager,  where the pointers to the related entities will be pushed. If the array doesn’t exists it is created, it it already exists it is not cleared.
  • entitiesExternalexpression is a string expression that locates the external key within each entity of the entities collection.
  • inverseCollectionExpression is a string expression specifying the array contained in each entity of the entities collection where the pointers to the related entities will be pushed. If the array doesn’t exists it is created, if it already exists it is not cleared.
  • overrideKeyexpression, if provided, is used in place of the key defined in the current instance of the updateManager.

In a short time I will write a more detailed blog post on how to handle related collections.

All examples on the js Queryable and on the updateManager are contained in the files Mvc4 Client- Filtering -Paging -Sorting-updating and Advanced JSon Communication in the download area of the Mvc Controls Toolkit

In a short time the team of the jsAction project will give us a very easy way to use the updateManager, by inspecting all controllers and providing automatically the right instances of the updateManager already configured to work with each specific controller. For the moment enjoy the post of my Friend Vincenzo of the jsAction team.

Tags: , , , , , , ,

Dec 11 2010

Silverlight…without Ria Services

Data Validation 3: Silverlight, INotifyDataErrors, Ria Services and The Validation Toolkit for WPF & Silverlight

Something More about Ria Services

Ria Services offer an easy solution to the main problems of a typical Business Silverlight application, specifically:

  1. Data Validation. As discussed in a previous post, Ria Services offer support for a data validation based on Data Annotations defined on the server side classes.
  2. Communication with the server and remote validation. Several configuration details of the web service definitions are automatically handled by Ria Services. Moreover, as discussed previously, if the definition of some custom validation attribute is not available on the client side, validation is performed remotely on the server (please refer to the above post for more information).
  3. Ria services automatically set up Asp.net Authentication, Authorization and User Profile Web Services for the Silverlight client.

Unluckily, it is not always possible to use Ria Services, because the Web Services created by Ria Services are specific for Silverlight. Therefore, if one needs a interoperable general purpose service layer that is compatible with various technologies Ria services can not be used.

In such a case the only acceptable option should be the configuration of a Silverlight specific endpoint in a general purpose Web Services. However, often I see developers that do a partial duplication of code implementing a parallel Ria Services Layer specific for the Silverlight clients just to take advantage of the opportunities  offered by Ria Services.

In this post I will show a simple alternative way to obtain the same advantages offered by Ria Services with general purpose Web Services.

Let start with the support for Data Annotations.We can proceed as follows ( I assume  knowledge of the Data Annotations issues discussed in my previous posts here and here.) :

  1. When we define the meta classes of our business classes we  furnish a complete implementation of those classes with the right property types. Moreover, in order to make them serializable we decorate them with the [DataContract] attribute and their properties with the [DataMember ] attribute, since we want they be compatible with Silverlight that has no implementation of the [Serializable] attribute.
  2. We use the above meta classes as View Models or as part of the View Models, both in the server and in Silverlight projects. I advice just linking the same code to both the Silverlight  and  the server projects.
  3. We define our Web Services according to general design constraints, and not only according to Silverlight needs.
  4. We generate the Silverlight proxy, with the option to use already existing classes. This way, we use the original View Models we have linked to the Silverlight project instead of View Models provided automatically by Visual studio during proxy generation.
  5. Once in Silverlight, we wrap the View Model with the Validation Toolkit for Wpf & Silverlight wrapper, in order to furnish it support for both Data Annotations and INotifyPropertyChanged.

With the above procedure we have View Models with Data Validation support without duplicating code, and with the same effort required by the definition of a Ria Domain Service.

The Validation Toolkit for Wpf & Silverlight offers also all tools needed to perform the Remote Validation. On the server we pack all validation errors into an IEnumerable<ValidationErrorInfo> and then we throw a fault Exception: FaultException<IEnumerable<ValidationErrorInfo>> containing the above IEnumerable.

The exception is passed to the client if we decorate the Wcf interface definition with the FaultContract<IEnumerable<ValidationErrorInfo>>] attribute.This is enough for all clients but Silverlight to receive the validation errors. In fact Silverlight is a browser plug-in and as all browser plug-ins it can receive the details of a response only if the status code of the response is either 200(OK) or 404(Not Found), and the status code associated with a fault error normally is not one of them. To overcome this problem we apply the SilverlightFaultBehavior defined in the BindToolkit.Behaviors namespace of the Validation Toolkit for Wpf & Silverlight to the endpoint specific for Silverlight. This endpoint behavior change the status code of the Wcf error response from whatever it is into a 200 status code, thus allowing Silverlight to receive the FaultException.

Once the FaultException is available to the silverlight client,we need just to call the AddValidationFaultExceptionContent(IEnumerable<ValidationErrorInfo> errors) method of the top level wrapper, to send all error messages in their adequate places in the User Interface.

The binary distribution of the Validation Toolkit for Wpf & Silverlight contains a Silverlight example that shows both how to use the wrapper and how to handle remote validation.

Finally, the set up of  Asp.net Authentication, Authorization and User Profile Web Services for the Silverlight client is easy because Asp.net has standard implementations for these Web Service.

For the authentication service we just need to add to our web project an .svc file containing just:

<%@ ServiceHost 
  Language="C#"
  Service="System.Web.ApplicationServices.AuthenticationService" 
  Factory="System.Web.ApplicationServices.ApplicationServicesHostFactory" %>

No code behind or explicit implementation needs to be furnished since the Asp.net class  System.Web.ApplicationServices.AuthenticationService offers a standard implementation of the Authentication Service. However, we need to configure a Membership provider on our Web Site.

In the configuration file we must enable the Authentication Service:

<system.web.extensions>
  <scripting>
    <webServices>
      <authenticationService enabled="true" 
         requireSSL = "true"/>
    </webServices>
  </scripting>
</system.web.extensions>

and we must configure it to use basicHttpBinding(for Silverlight compatibility) and Https:

<services>
    <service name="System.Web.ApplicationServices.AuthenticationService"
        behaviorConfiguration="AuthenticationServiceTypeBehaviors">
      <endpoint contract=
        "System.Web.ApplicationServices.AuthenticationService"
        binding="basicHttpBinding"
        bindingConfiguration="userHttps" 
        bindingNamespace="http://asp.net/ApplicationServices/v200"/>
      </service>
  </services>
  <bindings>
        <basicHttpBinding>
            <binding name="userHttps">
                <security mode="Transport" />
            </binding>
        </basicHttpBinding>
  </bindings>

A detailed reference is here.

Analogously, for the Authorization service we have:

<%@ ServiceHost 
  Language="C#"
  Service="System.Web.ApplicationServices.RoleService" 
  Factory="System.Web.ApplicationServices.ApplicationServicesHostFactory" %>
<system.web.extensions>
  <scripting>
    <webServices>
      <roleService enabled="true"/>
    </webServices>
  </scripting>
</system.web.extensions>
<services>
    <service name="System.Web.ApplicationServices.RoleService"
        behaviorConfiguration="ApplicationServiceTypeBehaviors">
      <endpoint contract=
        "System.Web.ApplicationServices.RoleService"
        binding="basicHttpBinding"
        bindingConfiguration="userHttps" 
        bindingNamespace="http://asp.net/ApplicationServices/v200"/>
    </service>
  </services>
  <bindings>
<basicHttpBinding>
<binding name="userHttps">
<security mode="Transport" />
</binding>
</basicHttpBinding>

a Detailed reference is here.

While for the User Profile we have:

<%@ ServiceHost Language="C#"
Service="System.Web.ApplicationServices.ProfileService" 
Factory="System.Web.ApplicationServices.ApplicationServicesHostFactory" %>
<system.web.extensions>
  <scripting>
    <webServices>
      <profileService enabled="true"
        readAccessProperties="Birthday, FavoriteColor"
        writeAccessProperties="Birthday, FavoriteColor" >
    </webServices>
  </scripting>
</system.web.extensions>

As you can see, while enabling the profile service we need to specify read and write permissions for each property.

The service configuration is quite standard:

<services>
    <service name="System.Web.ApplicationServices.ProfileService"
      behaviorConfiguration="MyServiceTypeBehaviors">
      <endpoint contract=
        "System.Web.ApplicationServices.ProfileService"
        binding="basicHttpBinding" 
        bindingNamespace="http://asp.net/ApplicationServices/v200"/>
    </service>

A detailed Reference is here.

Well. As you can see, the set up of a Silverlight application with all features described in points 1), 2), and 3) is quite easy and can be carried out quickly also without the use of Ria Services. Therefore, there is no need to force the use of Ria Services also when a different solutions should be more adequate.

That’s all for now!. But…..

                                                                                  Stay Tuned !

                                                                                  Francesco

For more information or consulences feel free to contact me

Tags: , , , , , , , ,

Oct 21 2010

…Something More about Ria Services

Data Validation 3: Silverlight, INotifyDataErrors, Ria Services and The Validation Toolkit for WPF & Silverlight

Silverlight…without Ria Services

In my previous post about data validation I spoke quite enough about Ria Services. I spoke about its kind of validation and how it is achieved, and also how to share classes among a Silverlight project and its hosting web site project (it is enough to put the share.cs or share.vb extension to a file on the server).

Discussing with people I discovered that there are a lot of  false beliefs about Ria services. For instance, a lot of developers think they can use Ria services only in conjunction with Entity Framework classes. This is simply false. Ria services allow you to define a context similar to the one of the EF entities also with general classes provided by the programmers: just follows the wizard that let you define your services…and will see it give you also this option.

If your classes are interconnected, you can benefit of Linq to Objects for your queries in a way completely analogous to EF objects.

With Ria services you can benefit of some pre-defined endpoints that have been adequately configured to fit well the needs of Silverlight users: you have a binary endpoint for intranet applications (its default), a soap endpoint for Internet applications and also a JSon endpoint. Moreover, you have also the option to expose OData (there is a check box in the wizard for the service definition). Instructions on how to define the endpoints in the configuration file are reported here. The bad news are that you cannot configure the features of the endpoints like in normal WCF services using the configuration file, but if you need features that are not contained in the standard configuration of the standard endpoints you have to define new endpoints in code.

Requiring a secure session is quite easy and can be done just by putting this attribute on the class that defines the service: [EnableClientAccess(RequiresSecureEndpoint = true)].Secure messages protocols are not supported by Silverlight. Don’t forget to give an https Url to a secured soap endpoint!

Requiring authentication and Role based authorizations are both easily achieved with attributes as explained here: http://msdn.microsoft.com/it-it/library/ee707361(en-us,VS.91).aspx. The above link explain also how to login and how to handle login errors.

Another, important thing that is worth to mention about Ria services concerns the return value of the Query methods of the service. According to the official documentation Query methods may return an IEnumerable<T>, an IQueryable<T> or a single object. However, what is not reported in the documentation is that only by returning an IQueryable one can  have an efficient filtering of data based on criteria passed by the Silverlight client to the server. For instance, suppose the Silverlight client needs to filter the customers returned by a Query method .GetCustomersQuery() defined on the server according to a user provided name. One can achieve this with the instruction:

 domainContext.Load(domainContext.GetCustomersQuery().Where(c => c.Name == "John").OrderBy(c => c.Age))

One could thing that all data returned by the server are then filtered by the Silverlight client. NO: behind the scene the Silverlight client build an IQueryable<T> and pass it to the server.Now, if also the server side method GetCustomerQuery() returns an IQueryable<T> the two queries are combined and either passed to the the Database or to Linq to Object to get the needed results efficiently! Therefore,  if you need to perform filtering based on client side defined criteria, please, always return an IQueryable<T>!

I thanks Gius that helped me clarifying well this point by speaking directly with a member of the WCF data services team.

Before concluding this short review post about Ria Services I would like to mention two more alternatives well suited for Silverlight clients: WCF Data Services, and WCF Rest Services. The WCF Rest Service Application template is not available in Visual Studio 2010 but it can be downloaded directly from Visual Studio 2010 by searching it in the Extension Manager(tool menu).

WCF Data Services are very similar to Ria services, The way you use it from Silverlight is quite the same. The main difference is that they don’t have any simple mechanism to share code with Silverlight and to handle validation. Moreover, you have to generate the proxy in Silverlight with Visual Studio and your context need to be specified by code(you need just to substitute a generic with an actual class…), and not with a simple wizard. The context may involve either EF entities or normal classes as in the case of Ria Services. This means some more job to be done but more flexibility because all things that Ria Services do in a standard way can now be customized! The only big loss is validation, but you can use my Validation Toolkit for WPF & Silverlight that is more flexible than the standard mechanism of Ria Services. However, it is important to mention that WCF Data Services just have one type of endpoint! The Rest endpoint! With a Rest endpoint queries are specified by adequately writing an URL, and the results are returned in the OData format that is a generalization of the Atom feed format. This means, you can send query not only with Silverlight but also with a simple browser! This is a very useful help in debugging the application. Obviously if you work with Silverlight all this details are hidden to you that just may use data contexts and IQueryables like with Ria Services.

WCF rest services use the same Rest protocol as the WCF Data Services but they are not connected to EF or classes, they are generic WCF services based on the Rest protocol. Parameters for the services are specified by the structure of the Url with the help of Routing tables defined in the Global.asax in a similar way to MVC Web applications. They are an interesting option to be considered for complex services that do more sophisticated computations than simply retrieving data from a Database.

Next post of this series will be again on validation, but on the server side…

 

                                                                                  Stay Tuned !

                                                                                  Francesco

For more information or consulences feel free to contact me

Tags: , ,

Oct 6 2010

Data Validation 3: Silverlight, INotifyDataErrors, Ria Services and The Validation Toolkit for WPF & Silverlight

Category: WCF | WPF | Silverlight | Entity Frameworkfrancesco @ 01:06

Data Validation 1

Data Validation 2: Enriching WPF with Data Annotations Support

Something More about Ria Services

Silverlight…without Ria Services

In my previous post I discussed how to enrich WPF with support for Data Annotations and for receiving asynchronous validation error from web services. What about Silverlight? In my first post of this series I already said that Silverlight already offers some support for Data Annotations. However, this support is not native of the silverlight presentation layer but is conveyed by other tools.

In Silverlight data controls, such as the Data Form, the support for data annotations has been injected through the specific way this controls are implemented: the Data Control evaluates itself  the data annotations once it receives input from the user. However this doesn’t solve the general problem.

A better solution is offered by the Ria services that offer their support for Data Annotation through the INotifyDataError interface whose support is native in the Silverlight presentation layer: this way if you use Ria services you have support for Data Annotations for ALL Silverlight controls.

Ria services use an approach that is similar to the wrapper approach of the  Validation Toolkit for WPF & Silverlight. However, they don’t need a wrapper around the class for supplying it an adequate implementation of the INotifyDataError interface: when you define a class to be used with a Ria Web Service, Visual Studio automatically creates an analogous class in the Silverlight project with the same data annotations and put inside it the needed implementation of the INotifyDataError interface.

Moreover, Visual Studio provides also automatic support for asynchronous errors coming from Web Services after the class has been submitted to the server. Such asynchronous errors come from those validation attribute that are defined on the server side version of the class but are not defined on the client side version of the class…..Yes, there might be attributes that are not copied on the client side version of the class! In fact, an attribute is copied on the client side only if the code that defines that attribute is available on the client side. This happens only if the attribute has been defined on the server side in a file with the .shared.cs (or .shared.vb)  extension: only in this case visual studio creates automatically a client side version of the attribute!

Why should one create an attribute definition without the .shared. extension? There are two possible reasons:

  1. The attribute definition requires libraries that are available in .Net but are not available in Silverlight.
  2. However, the most important reason is that you DON’T WANT that attribute be used on the client side because the validation it performs requires data that are available only on the sever such as data coming from a database.

Now it is clear that Ria services obtain the same objective of the Validation Toolkit for WPF & Silverlight with a slightly different technique.

Then, why should one use use the Validation Toolkit for WPF & Silverlight? For two main reasons:

  1. The Web Services cannot be implemented as a Ria Web Services(also called Domain Web Services)
  2. To keep the compatibility with WPF that cannot take advantage of the same type of support offered by Ria services to Silverlight.

 

The INotifyDataErrors interface and its advantages on the IDataErrorInfo interface

The INotifyDataError interface is defined as:

public interface INotifyDataErrorInfo

{

      bool HasErrors { get; }

     event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

     IEnumerable GetErrors(string propertyName);

}

GetErrors has essentially the same role that the indexer in the IDataErrorInfo interface, however it returns a list of errors instead that a single error. This is more correct because there might be several errors for each Control: since WPF only supports the IDataErrorInfo one is forced to concatenate all errors into a single string! Moreover, if one pass to GetErrors the empty string it is supposed to return the object level errors. Also the HasErros property represents an evolution: one is not forced to count the errors by firing the validation routed event to verify if the View Model is valid.

However, the really cool new feature is the ErrorsChanged event. Silverlight UI subscribes to this event and is able to update the error status of the interface also asynchronously when new errors are returned as a result of asynchronous call to web services. This behavior is not possible with WPF since WPF only supports the IDataErrorInfo interface.

The Validation Toolkit for WPF & Silverlight is able to update the error status of the UI in response to asynchronous events just because it renounces to delivery the error to its exact target control, but it just renders asynchronous errors as if they all were object level errors. It is worth to point out that this doesn’t mean all asynchronous errors are delivered to the root of the view model but only that they are delivered to the father object of the property they refers to. Since most of remote errors are actually object level errors this behavior is acceptable…but in any case the right behavior…is better. The examples you find in the binary distribution of the Validation Toolkit for WPF and Silverlight shows this difference in the behavior between the Silverlight and the WPF versions.

How to use the Validation Toolkit for WPF & Silverlight with Silverlight

The use of the Silverlight version of the  Validation Toolkit for WPF and Silverlight is almost identical to use of the WPF version:

  1. First, you have to enable ValidatesOnNotifyDataErrors in Silverlight for the bindings you want to apply validation to. 
  2. Then, you need to wrap your View Model into the dynamic object BindWrapper with the instruction: new BindWrapper(ViewModel, true, true); Setting the second parameter to true causes all son objects of the View Model be recursively wrapped, too. Recursive wrapping will continue also through the boundaries of IEnumerables if the third parameter is set to true .
  3. If  there is no top level View Model class but your top level structure is either a simple enumerable or a dictionary you can wrap recursively through them by calling respectively:
    static ObservableCollection<BindWrapper> WrapEnumerable(IEnumerable source, bool wrapDeep = false, bool wrapEnumerables=false)
    or
    public static ObservableDictionary<string, BindWrapper> WrapDictionary<TValue>(IDictionary<string, TValue> source, bool wrapDeep = false, bool wrapEnumerables = false)
    The result is respectively either an observable collection or an observable dictionary(the observable dictionary type is implemented in the toolkit). The meaning of the parameters is the same as  the ones of the BindWrapper constructor.
  4. Use the wrapper in place of your original object. You can get or set a  property of your original View Model by getting or setting a property of the wrapper with the same name: It is a dynamic object it will accept it, and it will retrieve or update the original property of your View Model while triggering the adequate events to update the interface and to perform validation.
  5. Bind the wrapper to your user interface. In the Silverlight version you need to enclose in square brackets the names of the properties in your Bindings, because Silverlight doesn't support dynamic object and I was forced to use a dictionary. For instance, [Age] instead of Age.
  6. Validation of the simple properties is done automatically. When you want to trigger object level validation on the whole View Model or on a part of it, you call theValidateWholeObject method of the relative wrapper. If Some Validation Exceptions are already available you can pass them to ValidateWholeObject as a parameter.
  7. Each time Validation Exceptions comes from a web service you can call AddRemoteErrors(IEnumerable<ValidationException> remoteErrors)  to update the interface.
  8. If for some reason you need to reset the interface from object level validation errors you can call ResetGlobalEvaluation(), but normally you don't need to do it.

In the next post I will say something more on how to handle validation errors on the server side.

                                     Stay Tuned!

                                      Francesco

For more information or consulences feel free to contact me

Tags: , , , , , , ,