Apr 1 2022

Software Architecture with C# 10 and .NET 6 is out!

The third edition of my book is out! You can buy it on Amazon

imagebook3_thumb[1]

If you are a C# developer wishing to jump into the world of enterprice applications and cloud, this is the right book for you!

From how to collect requirements, and how to master DevOps, selecting the right cloud resources, Web API, front-end frameworks (ASP.Net MVC and Blazor) to microservices design principles and practice,. This new edition updates all subjects to the last cloud and .Net features and adds new chapters:

  • A detailed description of gRPC and of how to use it from .NET
  • A new chapter that explains in detail how to implement a worker microservice with  ASP.NET + gRPC, and .NET hosted services + RabbitMQ
  • An introduction to Artificial Intelligence and Machine Learning
  • An introduction to native clients (including a short review of .NET MAUI)

Most of chapters give enough details to cover 90% of all practical applications and give all links and pointers to get more details.The only exceptions are the chapters about artificial intelligence and native clients that are just introductions to big subjects. However,  also there you will find complete learning paths to follow to become an expert.

The first three chapters describe modern development processes, and how to collect and document functional and not-functional requirements. Example of requirement collection and management with Azure DevOps are given.

Then the books moves to the basic cloud concepts and describes how to select the right cloud resources for each application.

Chapter 5 explains the whole therory behind Microservices design, and lists .NET resources that plays a foundamental role in the .NET implementation of Microservices. A self-contained description of Docker, is given, too.

Chapter 6 is dedicated to Kubernetes. There you will find all basic concepts and enough details to cover 90% of all practial applications.

Chapter 7 and 8 are dedicated to data storage and how to interact with them with Entity Framework Core and other clients. There, you will find .the whole theory behind distributed databases, how to maximize read and write parallelism and how to choose between SQL and Not-SQL databases.

Chapter 9 is about serverless and Azure functions. There, you  will find enough details to cover simple-middle complexity functions, and pointers on how to implement more complex functions.

Chapter 10 is dedicated to  the concept of pattern and describes various patterns used throghout the book.

Chapter 11 describes Domain Driven Design, that is the most used design methodology for microservices. Related patterns and their practical usage in .NET layered applications are given, too.

Chapter 12 describes typical patterns of code reusability used in .NET applications.

Chapter 14 gives a detailed description of gRPC and of its usage in .NET applications. Then, a complete implementation of a worker microservice with gRPC and ASP.NET CORE is given. Finally the same example is implemented with a .NET worker service and RabbitMQ.

Further chapters describe SOA architectures  and their implementation with ASP-NET Core (13), ASP.NET Core and ASP.NET Core MVC(15) and Blazor (17).

Chapter 16 puts in practice all concepts learned with ASP.NET Core MVC and Domain Driven Design with the implementation of a front-end microservice.

Chapter 18 is a introduction to native .NET clients that includes also a first look to .NET MAUI. The description is not detailed since a detailed description would require another complete book, but native clients are compared with browser-based clients and frameworks (like Blazor) and complete learning paths are given.

Chapter 19 is an introduction to artificial intelligence and machine learning. The basic principles of the main AI techniques are described, without going deep into the implementation details.Also. the basic theory about machine learning is given. Here the focus is on understanding which problems can be solved with machine learning and how many examples they require. A practical example of supervised learning is given.

Chapter 20 is dedicated to best practices and code metrics.

Chapter 21 and 22 are dedicated to DevOps and to the usage of Azure DevOps and GitHub actions.

Finally chapter 23 is dedicated to testing, test-driven design, unit-tests and functional/acceptance tests.The chapter gives the complete theory and describes in detail xUnit, and Moq.Practical examples of functional tests based on AngleSharp and Selenium are given, too.

DO NOT MISS IT!

Francesco

Tags:

Feb 5 2017

Don’t miss MVP led TechDays Online!

600x600-TechDaysOnline-Claire-Smyth-Illustration-V2

Hear from leading MVPs and technical experts talking about:

20 February 2017

  • 10.00am: Data, data, data – How and Where to store it on Azure?
  • 11.00am: Conversational UI using the Microsoft Bot Framework
  • 12.00pm:Microsoft Bot Framework and Cognitive Services: Make your bot smarter!
  • 1.00pm: The best kept secret, Document DB
  • 2.00pm: Let’s discuss Server-less
  • 3.00pm: What is an Azure Data Lake?
  • 4.00pm Close

21 February 2017

  • 10.00am: Creating a PHP-MySQL web app in Azure App Service and deploying using FTP. MVP Rik Hepworth
  • 11.00am: Gain profit from Azure app service tooling as an OSS developer. MVP Mike Martin
  • 12.00pm: Dockerizing Your Cross-Plat .NET Development. MVP Rainer Stropek
  • 1.00pm: Communication Driven Development. Jessica Rose
  • 2.00pm:  Monitoring Linux in Azure with Microsoft Operations Management Suite Log Analytics. MVP Gordon McKenna
  • 3.00pm: The Open Source World of Xamarin. MVP Garry Whittaker
  • 4.00pm Close

22 February 2017

  • 10.00am: Bootstrapping blockchain. Microsoft’s Mike Ormond and Jonathan Collinge 
  • 11.00am:  How IOT and data is changing lives. Haiyan Zhang from Microsoft Research
  • 12.00pm: An introduction to Quantum Computing. Ilyas Khan from Quantum Computing
  • 1.00pm: Social Scientist Professor Bradley Love from University College London and the Alan Turing Institute
  • 2.00pm: Microsoft Regional Directors panel. Richard Conway, Andy Cross, Steve Thair and Simon Sabin
  • 3.00pm: Close

Tags: , , ,

Jul 17 2016

Available Asp.net core version of the Mvc Controls Toolkit Live Examples!

New 1.0.1 bugs fix release of the Mvc Controls toolkit! Available also Live Examples at this link!

Enjoy!  & Stay tuned

In a short time ajax update server grid and batch update server grid

Francesco

Tags: , , , ,

Apr 11 2012

Mvc Controls Toolkit Support to Mvc4 WebApi 2: Handling Relations

Category: WebApi | MVC | Entity Framework | Asp.netFrancesco @ 21:51

In this post we will see how to handle one-to-many relations among entities with the advanced tools introduced in the last 2.1 release of the Mvc Controls Toolkit. We will give also some hints on how to handle some types of many-to-many relations that one might face in practical applications. I assume that everyone already read my previous post: Mvc Controls Toolkit Support to Mvc4 WebApi. The code of this example together with the code of my previous post is available in the  Mvc4 Client-Filtering -Paging-Sorting-updating file in the download area of the Mvc Controls Toolkit web site. Since, the update features we are going to discuss are not specific for ApiControllers but they are available also to standard Mvc 3 controllers, I provided also similar code examples that works with Mvc 3 controllers. They are in the AdvancedJSonCommunication file in the download area of the Mvc Controls Toolkit web site

As first step let modify our IQueryable to include also child entities of our ToDo items:

  1. return context.ToDo.Include("SubTasks").Select(item =>
  2.             new ToDoView()
  3.             {
  4.                 Name = item.Name,
  5.                 Description = item.Description,
  6.                 DueDate = item.DueDate,
  7.                 id = item.id,
  8.                 Tasks = item.SubTasks.Select(x => new SubTasksView { Name = x.Name, WorkingDays = x.WorkingDays, Key = x.code, FatherId = x.idTask })
  9.             });

We added a SubTasks table to our DB that is related in a one-to-many fashion to our ToDo table, and we are asking LinQ to add the children entities of each ToDo item through the Include clause.

We changed also the code of the WebApi method that exposes the IQueryable on the web:

  1. public IQueryable<ToDoView> Get()
  2. {
  3.     return new HttpSafeQuery<ToDoView>(ToDoViewModel.GetToDoQueryable(), true);
  4. }

We pass true as second argument of the constructor of our HttpSafeQuery. This way we instruct it to accept filtering criteria that are the logical and of clauses only. In fact, our application only needs such criteria, so by blocking all other kinds of queries we have a better protection against malicious users.

 

We show the children entities just in the detail window of our application:

Children

To achieve this result we can use just a simple client side  for statement:

  1. @{var h=item._foreach(m => m.Tasks, ExternalContainerType.tbody);}
  2. @h._begin()
  3. <tr>
  4.      <td>@h.TextBoxFor(m => m.Name)</td>
  5.      <td>@h.TypedTextBoxFor(m => m.WorkingDays, new { @class = "smallNumbers" })</td>
  6.      <td><input id="btnDetailDelete" type="button" value="Delete" data-bind='click: function(item){detailToDo.removeTask(item);}'/></td>
  7.      <td><input id="btnDetailUndo" type="button" value="Undo" data-bind='click: function(item){detailToDo.undoTask(item);}, enable: _inserted() || _modified()'/></td>
  8. </tr>
  9. @h._end()

 

The above code renders our children entities inside a table tbody(ExternalContainerType.tbody). The remainder of the table is written in Html.

Displaying the children entities was easy. The difficult part is handling their updates. The good news is that we don’t have code too much: the Mvc Controls Toolkit takes care of everything.

We need just to add a new ChangeSet property to the action method that receives the updates:

  1. public HttpResponseMessage<ApiServerErrors<int>> Post(UpdateViewModel model)

 

  1. public class UpdateViewModel
  2. {
  3.     public Updater<ToDoView, Int32> ToDoCS { get; set; }
  4.     public ChildUpdater<SubTasksView, Int32> TaskCS { get; set; }
  5. }

ChildUpdater is a subclass of the base Updater class we use to receive a change set that contains a property more. we will speak about it later onin this post. We are not forced to use the ChildUpdater and/or the Updater classes. we can use any class containing property to receive inserted, deleted,  modified…etc,  entities () by adequately declaring the names of such properties in our UpdateManagers.

We need also a another UpdateManager that takes care of the SubTasks entities and a destinationViewModel that is the  javascript equivalent of our UpdateViewModel. The destinationViewModel will be filled automatically by our UpdateManagers and submitted to the Post method of our controller.

We can declare the destinationViewModel as an empty object because the needed properties will be created automatically by the UpdateManagers that fill them:

  1. var DestinationViewModel = {};

You can find the above instruction in the EditDisplayToDo.js file while the definition of the UpdateManagers is contained in the IndexEdit.cshtml class to take advantage of the Url.RouteUrl method to compute the Url of the receiving action method:

  1. ClientToDoView.childUpdater = mvcct.updatesManager(
  2.     '@Url.RouteUrl("DefaultApi", new { httproute = "", controller = "ToDo"})',
  3.      ClientToDoView,
  4.      'DataPage.TasksList',
  5.      'Key', DestinationViewModel, "TaskCS");
  6.  
  7. ClientToDoView.updater = mvcct.updatesManager(
  8.     '@Url.RouteUrl("DefaultApi", new { httproute = "", controller = "ToDo"})',
  9.      ClientToDoView,
  10.      'DataPage.ToDoList',
  11.      'id', DestinationViewModel, "ToDoCS");

Each UpdateManager just specifies:

  1. The Url where to submit the change sets
  2. The source Client ViewModel that contains the entities
  3. A string expression that locates the property that contains the entities to handle within the source Client ViewModel.
  4. A string expression that identifies the principal key of each entity.
  5. The destination ViewModel
  6. A string expression that locates the change set within the destination ViewModel.

When the UpdateManagers compute the change sets they creates automatically the TaskCS and ToDoCS properties if they are not already defined in the destination ViewModel. Actually there is no DataPage.TasksList property in the source ViewModel since the SubTasks entities are contained in their father ToDo entities. The DataPage.TasksList is automaically created and filled with all the SubTasks entities just before computing the change sets.

 

Now we declare that the ClientToDoView.childUpdater works as a child of the main ClientToDoView.updater:

  1. ClientToDoView.updater.addChildUpdateManager({ expression: 'Tasks', external: 'FatherId', updater: ClientToDoView.childUpdater });

The object passed as argument of the call contains:

  1. expression: a string expressions locating the collection that contains the children entities within each ToDo item. In our case: Tasks.
  2. external: the external key of the child entities.
  3. updater: the updateManager to add as a child.

Newly created ToDo items need to be prepared and inserted with the Insert method:

  1. var item = {
  2.     DueDate: ko.observable(this.DueDate()),
  3.     Name: ko.observable(this.Name()),
  4.     Description: ko.observable(this.Description()),
  5.     id: ko.observable(null),
  6.     Tasks: ko.observableArray(this.Tasks())
  7. };
  8. ClientToDoView.updater.prepare(item, true); //newly created entity prepare it
  9. ClientToDoView.updater.inserted(ClientToDoView.DataPage.ToDoList, item);

Where the second argument of the prepare method ask to start changes tracking immediately.

While newly created SubTasks are added to the collection of their father entities with the addChild method of the father updateManager:

  1. detailToDo.createTask = function () {
  2.     var item = {
  3.         Name: ko.observable(''),
  4.         WorkingDays: ko.observable(0),
  5.         Key: ko.observable(null),
  6.         FatherId: ko.observable(null)
  7.     };
  8.     //newly created entity preparation is done when adding to father with addChild
  9.     ClientToDoView.updater.addChild(this, 'Tasks', item, true);
  10. };

 

Where:

  • this, denotes the father ToDo item
  • Tasks is the collection where to add the newly created item
  • item, is the newly created item
  • true, starts changes tracking immediately.

As already discussed in my previous post, deletes are performed by calling the deleted method:

 

  1. ClientToDoView.updater.deleted(ClientToDoView.DataPage.ToDoList, item);

 

  1. ClientToDoView.updater.deleted(detailToDo.Tasks, item);

As discussed in my previous post each time the user performs some modifications on an entity we must call the modified method that verifies if the entity actually changed and mark it as modified. For the ToDo items we do this in the save method of our detail window:

  1. detailToDo._save = function () {
  2.     var item = this.DetailOf();
  3.     if (!item) return true;
  4.     if (!$('#detailForm').validate().form()) return false;
  5.     mvcct.utils.restoreEntity(this, item, true);
  6.     ClientToDoView.updater.modified(item, true, true);
  7.     if ((!item._modified()) && (!item.tasksChanged()))
  8.         ClientToDoView.childUpdater.refreshErrors($('#mainForm'), null, item);
  9.     return true;
  10. };

For a description of the arguments of the modified method, please refer to my previous post.

For the SubTasks we have no Done button that is clicked after the user finished modifications, so we have to call automatically the modified function each time a property is modified. We can do this by attaching a function to each observable poperty of the SubTasks through the knockout subscribe method:

  1. ClientToDoView.childUpdater.options({
  2.     isoDate: true,
  3.     prepareCallback: function (item) {
  4.         var prev = false;
  5.         function subscription() {
  6.             ClientToDoView.childUpdater.modified(item, true, true);
  7.             if (prev && !item._modified())
  8.                 detailToDo.undoTask(item);
  9.             prev = item._modified();
  10.         };
  11.         item.Name.subscribe(subscription);
  12.         item.WorkingDays.subscribe(subscription)
  13.     }
  14. });

We have done this job in the prepareCallback function that its automatically called immediately after an entity is prepared. The preparedCallback can be declared in the updateManager options that are passed to the updateManager either as last argument of the constructor or through the options method.

We define a prepareCallback also for the ToDo entities:

  1. ClientToDoView.updater.options({
  2.     isoDate: true,
  3.     updateCallback: function (e, result, status) { alert("status: " + status); },
  4.     prepareCallback: function (item) { item.tasksChanged = ClientToDoView.updater.arrayChanged(item.Tasks); }
  5. });

It attaches a knockout computed to the taskChanged property of each ToDo item. This computed returns true if any child SubTask of the Tasks collection has been marked as modified. The knockout computed is returned by calling the arrayChanded method of the updateManager and passing it the collection we would like to check. Each time the state of a child entity changes the taskChanged property is recomputed automatically by the knockout engine. We can use the arraChanged property together with the standard _modified and _inserted properties, added to each entity, to enable the undo button if and only if there are changes to undo:

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

The Undo method calls the reset method of the main update manager that undoes all changes performed to the ToDo item and to all its children entities:

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

The reset method accepts a form as second argument, because when changes are undone possible server errors associted to thel undone entities are cancelled.

That is enough! Now when we submit the ClientToDoView.updater main updateManager the receiving controller receives the change set of the SubTasks entities, together with the change set of the ToDo entities automatically. Moreover, possible updateCallback and updatingCallback associated with the children updateManagers are called as appropriate. Summing up all we need to do is just:

  1. ClientToDoView.updater.update($('#mainForm'));

Once the change sets reach the controller they can be processed separately as needed by the business layer…However, we are committed to return to the client the newly created keys of the Inserted records. Thus our business methods must return them in the same order of the inserted entiies received in the change set.

There is another complication: inserted records that are children of inserted records! This records need the external key from their father entities before being processed and stored in the DB. Therefore we process the fater entity to get their principal keys:

  1. ToDoKeys = ToDoViewModel.UpdatePage(model.ToDoCS.Inserted, model.ToDoCS.Modified, model.ToDoCS.Deleted);

and then we pass them to possible children entities that might need them by calling the ImportExternals method of the ChildUpdater class:

  1. //imports the external keys of the newly created father entities into their children
  2. if (model.TaskCS != null) model.TaskCS.ImportExternals(ToDoKeys, m => m.FatherId)

Now we can process the children entities:

  1. if (model != null && model.TaskCS != null)
  2. {
  3.     TaskKeys=ToDoViewModel.UpdatePageTasks(model.TaskCS.Inserted, model.TaskCS.Modified, model.TaskCS.Deleted);
  4. }

everything is enclosed into a single transaction:

  1. try
  2. {
  3.     using (var t=new TransactionScope())
  4.     {
  5.         if (model != null && model.ToDoCS != null)
  6.         {
  7.             ToDoKeys = ToDoViewModel.UpdatePage(model.ToDoCS.Inserted, model.ToDoCS.Modified, model.ToDoCS.Deleted);
  8.  
  9.             //imports the external keys of the newly created father entities into their children
  10.             if (model.TaskCS != null) model.TaskCS.ImportExternals(ToDoKeys, m => m.FatherId);
  11.             //here the same for other children collections
  12.         }
  13.         if (model != null && model.TaskCS != null)
  14.         {
  15.             TaskKeys=ToDoViewModel.UpdatePageTasks(model.TaskCS.Inserted, model.TaskCS.Modified, model.TaskCS.Deleted);
  16.         }
  17.         t.Complete();
  18.     }
  19.     
  20. }
  21. catch (Exception ex)
  22. {
  23.     ModelState.AddModelError("", ex.Message);
  24.     return
  25.         new HttpResponseMessage<ApiServerErrors<int>>(
  26.             new ApiServerErrors<int>(ModelState, new ApiKeyInfos<int>[0]), System.Net.HttpStatusCode.InternalServerError);
  27. }

All new keys must be returned to the client to keep the client synchronized with the server:

  1. // if keys have different simple types such as one is int, and one other is string, use ApiServerErrors<object>
  2. return new ApiServerErrors<int>(ModelState,
  3.     new ApiKeyInfos<int>[] {
  4.         new ApiKeyInfos<int>{destinationExpression="ToDoCS", keys=ToDoKeys},
  5.         new ApiKeyInfos<int>{destinationExpression="TaskCS", keys=TaskKeys}
  6.     }).Wrap();

The keys handling algorithm complicates the interaction between the presentation layer and the business layer. This “too strong” interaction between layers maybe avoided by using Guids as keys. In fact, in this case all keys can be computed by the controller, so the whole key handling can be performed by the controller avoiding the need of  a “too strong“ cooperations between the presentation and the business layer. It is worth to point out that, due to security problems, Guids cannot be computed in the browser, so we are forced to compute them in the controller and to return them to the client.

For a better user experience the 2.1 release of the Mvc Controls Toolkit introduces two new features to handle the errors returned by the server:

  1. Automatic errors delete
  2. Errors bubbling

When an entity is undone, someway, either by pressing the undo button or by undoing manually all modifications, or by doing everytning else that might set its _modified status to false, all errors associated with it are deleted by the list of all errors returned by the server. This way, the next time the refreshErrors method of the updateManager is called they are deleted from the UI. The reset method of the updateManager calls automatically the refreshErrors method after having performed the undo, passing it the form that it receives as argument. This way all errors are cancelled from that form immediately. If the same error is displayed in several forms we need to call manually the refreshErrors method for all other forms:

  1. detailToDo.undoTask = function (item) {
  2.     ClientToDoView.childUpdater.reset(item, $('#mainForm'), this.Tasks);
  3.     ClientToDoView.childUpdater.refreshErrors($('#detailForm'));
  4. };

The first argument of the reset method is the item to undo, the second argument the form to refresh, and finally the third argument is the collection the item belongs to. The third argument is needed only in case of children entities, because not-children entities are not contained in any father collection.

Due to error bubbling, errors in children entities affect also the UI of their father entities, thus we have to refresh two forms: one is refreshed automatically by the reset method, and the other is refreshed by calling manually the refreshErrors method.

The errors associated with an entity can be deleted also manually by calling the refreshErrors method and passing it: the form as first argument, a null second argument, and the item we would like to delete the errors of as third argument. By passing null as second argument we ask the refreshErrors method to use the errors returned by the last call to the server.

Errors of any child entity are bubbled up to the property collection of the father entity that the children entity belongs to. However, just the fact that there was at least one error in the collection is bubbled up, not all error messages. This means that a ValidationMessageFor helper for the collection property will display just the error message passed as second argument. In our example we placed a ValidationMessageFor with an * near the edit button:

  1. <td>
  2.     <input type="button" value="Edit" data-bind='click: function(item){detailToDo.edit(item);}'/>
  3.     @item.ValidationMessageFor(m => m.Tasks, "*")
  4. </td>

Let see how errors work in practice. As first step let uncomment the error messages in the action method:

  1. //uncomment to experiment server side error handling
  2. //ModelState.AddModelError("ToDoCS.Modified[0].Name", "Fake error");
  3. //ModelState.AddModelError("TaskCS.Modified[0].Name", "Fake error1");

Then let start the application and let modify a ToDo item together with one of its children entities:

ErrorBubblingStart

Then let it Submit All Changes:

ErrorBubbligEnd

As you can see the error in the child entity has been bubbled up near to the edit button.

Now if we click the edit button, in the detail window we will just see the error of the child entity.

Why? Simple, the ToDo entity is copied into another ToDo entity that is bound to the detail window with the mvcct.utils.restoreEntity(x, y, visitRelations), while its children entities are used directly without creating a copy of them since setting visitRelations to true causes all children entity to be copied by reference into a new observableArray without being cloned. Since the errors are tied to the entity they belong to, just the errors of the child entity is shown. This was decided by design, to avoid duplication of the errors in the UI:

errorBubblingDetail

If we would like to show the ToDo entitiy errors also in the detail window we need to istantiate a template on the original ToDo entity by means of the _with helper instead of copying its data into another object that is already bound to UI elements.

Now let click the undo button of the children entity, or better simply delete the “modified” word we added previously. “Fake error 1” disappears from both the main window and from the detail window:

ErrorDeleteDetail

Now, if we click the undo button of the father entity, also “Fake error” completely disappears from the UI!

 

What if we have a many-to-many relation?

In the case of the one-to-many relation we just query our controller to get the “father entities” we need to process. As result we get the “father entities” and their related children entities “attached” to them. In the case, of a many-to-many relation we have no easy way to decide which entities of the second endpoint of the relation to move to the client. For this reason typically many-to-many relations cannot be processed in a batch fashion but they require a continuous interaction with the server. However, there is a common pattern that allows their batch processing, namely when one of the two endpoints of the relation is small enough to be transferred completely to the client.

This happens, for instance, when we have a collection containing all US States that we can select and attach to other entities. In other words, when one of the two related collections is used just for selection with a dropdown, or a multiselect or with some more complex UI. In this case the second collection of entities is used for display only. So we basically process just the collection R that represents the relation by means key pairs .

We can easily handle the situation depicted above with the same techniques we used for the one-to-many relations as follows:

  1. We move completely the second entity set to the client.
  2. We move to the client the entities of the first entity set we would like to process
  3. We move the entitis of R related to the entities at point 2 to the client. This can be accomplished in two ways
    • Add the entities of R as children of the entities of the first entity set they are related to,
    • Move all the entities of R we need as a separate collection and then connect them to the related entities of the first set of entities on the client with the help of the addRelated method of their updateManager (see my previous post).
  4. Define the updateManager of R as child of the updateManager of the first entity set.
  5. Connect the enties of R with the entities of the second entity set with the help of the addRelated method.
  6. To Add a new element to R, add it to the adequate entity of the first entity set, then set the external key of the second entity set, and add to it the pointer to the related entity of the second entity set.
  7. Process the entities of the first entity set + the entities of R with substantially the same techniques we used for the one-to-many relations, by accessing the pointer from each entity in R to the related entity of the second entity set when we need to display the related data.

That’s all for now! 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.

                                               Stay Tuned

                                               Francesco

Tags: , , , , , , ,

Nov 29 2011

Advanced Data Filtering Techniques in the Mvc Controls Toolkit

Category: MVC | Entity Framework | Asp.netFrancesco @ 04:04

Mvc Controls Toolkit Datagrid Updated Tutorial

Data Filtering, in the New Mvc 3 Version of the Mvc Controls Toolkit 

The Mvc Controls Toolkit has the ability to bind a ViewModel property to a LinQ expression that defines a Data Filtering criterion. As already pointed out in a previous post this capability enhances modularity by allowing the developer to change the filtering options without modifying the ViewModel or the controller. In fact the whole filtering logic can be encapsulated in a single module that communicates with the remainder of the system just through that LinQ expression.

In this post I show how this feature may improve also the user experience by enabling the web site user to define his filtering criteria at run-.time. Better options are available in the commercial version of the Mvc Control Toolkit

The DataFilterClauseFor HtmlHelper extension method defines a single clause of a filering LinQ expression, where a clause is a simple constraint (Less Than, Equal, StartSWith, and so on) on a column. The Web Site user can enable or disable each clause,  can choose the operator(Less Than, Equal, StartSWith, and so on) to apply and can supply the required parameters with customizable UI. All enabled clauses defined by several DataFilterClausesFor helpers are put together with a logical and, thus defining a complex filtering criterion.

The DataFilterClauseFor extension method has the following parameters:

this HtmlHelper<VM> htmlHelper,
            Expression<Func<VM, Expression<Func<T, bool>>>> filter,
            Expression<Func<T, F>> field,
            string filterName="",
            FilterCondition initialCondition = FilterCondition.Equal

Where:

  • filter is the lambda expression defining the property of the ViewModel that receives the final LinQ expression.
  • field is the lambda expression defining the data item column to be used in the filter clause. The data item type T determines the IQueryable<T> or IEnumerable<T> that filter can be applied to. Please note, T is not necesarilly a DTO type! Infact, if the application is well Layered any IQueryable coming from the DataLayer should have been converted into an IQueryable based on a Business Layer or Presentation Layer class.before reaching the controller. We can convert an IQueryable<S> OriginalQ into an IQueryable<T> ModifiedIQ  with a simple LinQ statement like this one:  from x in OriginalIQ select new T {Property1=x.Property1,……}.
  • filterName, is a name that is needed to identify univocally a clause in case there are several clauses involving the same column.
  • initilalCondition is the filtering opearion that is initially chosen for the clause. If needed this value can be changed by the user through customizable UI.

The DataFilterClauseFor extension method returns an HtmlHelper object we can use to render the UI that allows the user to select both the operation of the filter clause, and its parameter.

For instance if we would like to apply a filtering on the Name property of the ToDoView class of my previous post example Mvc Controls Toolkit Datagrid Updated Tutorial, we can write:

<div >
            @{var hName = Html.DataFilterClauseFor(
                  m => m.ToDoFilter, f => f.Name,
                  "byNameFilter",
                  MVCControlsToolkit.Linq.FilterCondition.StartsWith);}
            @hName.CheckBoxFor(m => m.Selected, new { @class = "byNameFilter_checkbox" })
             &nbsp; Filter by name
            <span  class="byNameFilter">
                @hName.FilterClauseSelect(
                    hName.ViewData.Model.Condition,
                    (Expression<Func<ToDoView, string>>)(m=>m.Name))
                @hName.TypedTextBoxFor(m => m.Search, watermarkCss: "watermark")
            </span>  
            @hName.ViewsOnOff("byNameFilter", false)
        </div>

First we call the DataFilterClauseFor extension method to get the HtmlHelper to render our UI. Then  we render a checkbox that enables/disables the filter clause by changing the value of the Selected property.

The Condition property of the hName HtmlHelper model contains the operation selected by the user. Initially it is set to StartsWith since we passed MVCControlsToolkit.Linq.FilterCondition.StartsWith as last parameter of the DataFilterClauseFor extension method. We render this property with the FilterClauseSelect extension method whose oiutput is a select that lists all filtering operations. The names of all operations can be localized and customized with the help of resources files as detailed in the documantation. The FilterClauseSelect extension method has also an optional parameter to specify which filtering operations to list in the select as shown in the example below:

@hName.FilterClauseSelect(
                    hName.ViewData.Model.Condition,
             (Expression<Func<ToDoView, string>>)(m => m.Name),
             conditions: FilterCondition.StartsWith | FilterCondition.Equal)

Finally, the Search property contains the clause filter parameter. We render this property with a TypedTextBox.

The ViewOnOff extension method makes the content of the span with Css class “ByNameFilter” to appears just when the CheckBox is checked. The initial state of the content (visible or not) is defined by the second parameter of the ViewOnOff extension method, whose value is false in our case . When the content is not visible it is completely detached from the DOM to avoid that client side validation logics might prevent the submit of the form (invisible fields are validated, while fileds detached from the DOM are not).

Further clauses may be added in a similar way. For instance, if we would like to add two more optional clauses do define a range of dates we can write:

<div>
            @{var hFromDate = Html.DataFilterClauseFor(m => m.ToDoFilter, f => f.DueDate, "fromDateFilter");}
            @hFromDate.CheckBoxFor(m => m.Selected, new { @class = "fromDateFilter_checkbox" })
             &nbsp; From:
            <span class="fromDateFilter">
                @{hFromDate.ViewData.Model.Condition = MVCControlsToolkit.Linq.FilterCondition.GreaterThanOrEqual;}
                @hFromDate.HiddenFor(m => m.Condition)
                @hFromDate.DateTimeFor(m => m.Search, DateTime.Today.AddMonths(-6), true)
                    .DateCalendar(inLine: false, calendarOptions: new CalendarOptions
                       {
                           ChangeYear = true,
                           ChangeMonth = true,
                       })
               
            </span>
            @hFromDate.ViewsOnOff("fromDateFilter", false)
        </div>
        <div>
            @{var hToDate = Html.DataFilterClauseFor(m => m.ToDoFilter, f => f.DueDate, "toDateFilter");}
            @hToDate.CheckBoxFor(m => m.Selected, new { @class = "toDateFilter_checkbox" })
             &nbsp; To:
            <span class="toDateFilter">
                @{hToDate.ViewData.Model.Condition = MVCControlsToolkit.Linq.FilterCondition.LessThanOrEqual;}
                @hToDate.HiddenFor(m => m.Condition)
                @hToDate.DateTimeFor(m => m.Search, DateTime.Today.AddMonths(6), true)
                    .DateCalendar(inLine: false, calendarOptions: new CalendarOptions
                       {
                           ChangeYear = true,
                           ChangeMonth = true,
                       })
            </span>
             @hToDate.ViewsOnOff("toDateFilter", false)
        </div>

Since in this case the user cannot choose the filtering operations on the DueDate field we store it the pre-defined values  MVCControlsToolkit.Linq.FilterCondition.GreaterThanOrEqual and MVCControlsToolkit.Linq.FilterCondition.LessThanOrEqual are stored into hidden fields.

It is worth pointing out that all filtering information come from the client, therefore a malicious user might try a denial of service attack by sending a manipulated filtering request involving  columns that are too difficult to filter (columns with no indexes defined on them, for instance). In order to defend ourselves from such an attack the Transformation Handler that receives the filtering data automatically discards columns that are not decorated with the CanSortAttribute that is defined in theMVCControlsToolkit.DataAnnotations namespace.

If we would like to allow just some filtering operation on a column, we can specify them in the CanSortAttribute as detailed in the documentation. We can use the CanSortAttribute.AllowedForProperty static method to get all filtering operations allowed on a property as shown in the examples below:

@hName.FilterClauseSelect(
                    hName.ViewData.Model.Condition,
             (Expression<Func<ToDoView, string>>)(m => m.Name),
             conditions: CanSortAttribute.AllowedForProperty(typeof(ToDoView), "Name"))

 

@hName.FilterClauseSelect(
     hName.ViewData.Model.Condition,
     (Expression<Func<ToDoView, string>>)(m => m.Name),
     conditions: CanSortAttribute.AllowedForProperty(
        FilterCondition.Equal |
        FilterCondition.StartsWith |
        FilterCondition.GreaterThanOrEqual |  
        FilterCondition.LessThanOrEqual,                             
        typeof(ToDoView), "Name"))

Summing up, with the DataFilterClauseFor extension method we can define filtering criteria as the logical and of simple clauses allowing the web site user to choose the operation to perform in each clause.

When the filtering criterion cannot be expressed as a logical and of clauses, or when the operation to be performed by each clause is pre-defined we can use the DataFilterBuilder extension method.

The DataFilterBuilder extension method returns an HtmlHelper to build the whole filter. THe definition of the filter expression is provided by passing it an implementation of the IFilterDescription<T> interface:

public interface IFilterDescription<TData>
{
    Expression<Func<TData, bool>> GetExpression();
}

The only member to implent is GetExpression that returns the LinQ expression already filled with the user input. Below an implementation of IFilterDescription<ToDoView> interface for the same example we already implemented with the DataFilterClauseFor extension method:

public class ToDoGeneralFilter :
        IFilterDescription<ToDoView>
    {
        [Display(Prompt = "chars the name of item starts with")]
        public string Name { get; set; }
        public bool NameEnabled { get; set; }
        [Display(Name = "From Date")]
        public DateTime FromDate { get; set; }
        public bool FromDateEnabled { get; set; }
        [Display(Name = "To Date")]
        public DateTime ToDate { get; set; }
        public bool ToDateEnabled { get; set; }
        public FilterCondition NameContition { get; set; }
        public ToDoGeneralFilter()
        {
            NameContition=FilterCondition.StartsWith;
        }
        public System.Linq.Expressions.Expression<Func<ToDoView, bool>> GetExpression()
        {
            System.Linq.Expressions.Expression<Func<ToDoView, bool>> res = new FilterBuilder<ToDoView>(FilterLogicalOperator.And)
                .Add(NameEnabled && (!string.IsNullOrWhiteSpace(Name)), NameContition, m => m.Name, Name)
                .Add(FromDateEnabled, m => m.DueDate >= FromDate)
                .Add(ToDateEnabled, m => m.DueDate <= ToDate)
                .Get();
            return res;
        }
    }

The public properties of the ToDoGeneralFilter class are filled by the user with the input fileds rendered by the HtmlHelper returned by the DataFilterBuilder extension method. Then, they are used to build the LinQ expression with the help of the FilterBuilder class. The first Add method adds the Name filter by taking the filter condition from the NameCondition public property. Then, the other two clauses are added with a different overload of the Add method that accepts a LinQ expression since the data filter operation is predefined.

All LinQ expressions supplied by the three Add calls are combined with a logical and, since we specified a logical and in as value of the parameter of the FilterBuilder constructor.

Below the code to render the input fields of the ToDoGeneralFilter class:

@{var filter = Html.DataFilterBuilder(m => m.ToDoFilter, new ToDoGeneralFilter());}
             <div>
              
              @filter.CheckBoxFor(m => m.NameEnabled,
                new Dictionary<string, object>() { { "class", "NameGroup_checkbox" } })
              @filter.LabelFor(m => m.Name)
              @filter.FilterClauseSelect(
                filter.ViewData.Model.NameContition,
                     (Expression<Func<ToDoView, string>>)(m => m.Name),
                     conditions: CanSortAttribute.AllowedForProperty(
                        FilterCondition.Equal |
                        FilterCondition.StartsWith |
                        FilterCondition.GreaterThanOrEqual |  
                        FilterCondition.LessThanOrEqual,                             
                        typeof(ToDoView), "Name"))
              @filter.TypedTextBoxFor(m => m.Name,
                new Dictionary<string, object> (){{"class", "NameGroup"}},
                watermarkCss: "watermark")
                @filter.ViewsOnOff("NameGroup", false)
             </div>
             <div >
                
                @filter.CheckBoxFor(m => m.FromDateEnabled,
                    new Dictionary<string, object>() { { "class", "FromGroup_checkbox" } })
                @filter.LabelFor(m => m.FromDate)
                
             
                 <span class ="FromGroup" >
                        @filter.DateTimeFor(m => m.FromDate,
                            DateTime.Now.AddMonths(-6),
                            true).DateCalendar(inLine:false, calendarOptions:
                                new CalendarOptions{
                                       ChangeYear = true,
                                       ChangeMonth =true
                                })
                 </span>     
                 @filter.ViewsOnOff("FromGroup", false)
             </div>
             
             <div>
                 
                 @filter.CheckBoxFor(m => m.ToDateEnabled,
                    new Dictionary<string, object>() { { "class", "ToGroup_checkbox" } })
                 @filter.LabelFor(m => m.ToDate)
                 
             
                <span class ="ToGroup" >
                         @filter.DateTimeFor(m => m.ToDate,
                                DateTime.Now.AddMonths(6),
                                true).DateCalendar(inLine: false, calendarOptions:
                                    new CalendarOptions{
                                           ChangeYear = true,
                                           ChangeMonth =true
                                    })
                    
                 </span>  
                 @filter.ViewsOnOff("ToGroup", false)
             </div>

Complex expression trees can be obtained by using the Open and Close methods of the FilterBuilder object that open and close brackets in the expression being built. A parameter of the Open method specifies the logical operator for combining all expressions within the brackets. Both the Open and the Close methods have a boolean parameter to enable or disable them. Since brackets must balance two corresponding brackets either are both enabled or both disabled. When both are disabled the expression they enclose is not inserted in the whole expression being built.

In the previous example suppose that we would like to provide two date ranges in logical or. Our GetExpression() method becomes:

public System.Linq.Expressions.Expression<Func<ToDoView, bool>> GetExpression()
        {
            System.Linq.Expressions.Expression<Func<ToDoView, bool>> res =
                new FilterBuilder<ToDoView>(FilterLogicalOperator.And)
                .Add(NameEnabled && (!string.IsNullOrWhiteSpace(Name)), NameContition, m => m.Name, Name)
                .Open(true, FilterLogicalOperator.Or)
                    .Open(true, FilterLogicalOperator.And)
                        .Add(FromDate1Enabled, m => m.DueDate >= FromDate1)
                        .Add(ToDate1Enabled, m => m.DueDate <= ToDate1)
                    .Close(true)
                    .Open(true, FilterLogicalOperator.And)
                        .Add(FromDate2Enabled, m => m.DueDate >= FromDate2)
                        .Add(ToDate2Enabled, m => m.DueDate <= ToDate2)
                    .Close(true)
                .Close(true)
                .Get();
            return res;
        }

While the code in the View becomes:

@{var filter = Html.DataFilterBuilder(m => m.ToDoFilter, new ToDoTwoDateRangesFilter());}
             <div>
              
              @filter.CheckBoxFor(m => m.NameEnabled,
                new Dictionary<string, object>() { { "class", "NameGroup_checkbox" } })
              @filter.LabelFor(m => m.Name)
              @filter.FilterClauseSelect(
                filter.ViewData.Model.NameContition,
                     (Expression<Func<ToDoView, string>>)(m => m.Name),
                     conditions: CanSortAttribute.AllowedForProperty(
                        FilterCondition.Equal |
                        FilterCondition.StartsWith |
                        FilterCondition.GreaterThanOrEqual |  
                        FilterCondition.LessThanOrEqual,                             
                        typeof(ToDoView), "Name"))
              @filter.TypedTextBoxFor(m => m.Name,
                new Dictionary<string, object> (){{"class", "NameGroup"}},
                watermarkCss: "watermark")
                @filter.ViewsOnOff("NameGroup", false)
             </div>
             <div >
                
                @filter.CheckBoxFor(m => m.FromDate1Enabled,
                    new Dictionary<string, object>() { { "class", "FromGroup1_checkbox" } })
                @filter.LabelFor(m => m.FromDate1)
                
             
                 <span class ="FromGroup1" >
                        @filter.DateTimeFor(m => m.FromDate1,
                            DateTime.Now.AddMonths(-6),
                            true).DateCalendar(inLine:false, calendarOptions:
                                new CalendarOptions{
                                       ChangeYear = true,
                                       ChangeMonth =true
                                })
                 </span>     
                 @filter.ViewsOnOff("FromGroup1", false)
             </div>
             
             <div>
                 
                 @filter.CheckBoxFor(m => m.ToDate1Enabled,
                    new Dictionary<string, object>() { { "class", "ToGroup1_checkbox" } })
                 @filter.LabelFor(m => m.ToDate1)
                 
             
                <span class ="ToGroup1" >
                         @filter.DateTimeFor(m => m.ToDate1,
                                DateTime.Now.AddMonths(6),
                                true).DateCalendar(inLine: false, calendarOptions:
                                    new CalendarOptions{
                                           ChangeYear = true,
                                           ChangeMonth =true
                                    })
                    
                 </span>  
                 @filter.ViewsOnOff("ToGroup1", false)
             </div>
             <div >
                
                @filter.CheckBoxFor(m => m.FromDate2Enabled,
                    new Dictionary<string, object>() { { "class", "FromGroup2_checkbox" } })
                @filter.LabelFor(m => m.FromDate2)
                
             
                 <span class ="FromGroup2" >
                        @filter.DateTimeFor(m => m.FromDate2,
                            DateTime.Now.AddMonths(-6),
                            true).DateCalendar(inLine:false, calendarOptions:
                                new CalendarOptions{
                                       ChangeYear = true,
                                       ChangeMonth =true
                                })
                 </span>     
                 @filter.ViewsOnOff("FromGroup2", false)
             </div>
             
             <div>
                 
                 @filter.CheckBoxFor(m => m.ToDate2Enabled,
                    new Dictionary<string, object>() { { "class", "ToGroup2_checkbox" } })
                 @filter.LabelFor(m => m.ToDate2)
                 
             
                <span class ="ToGroup2" >
                         @filter.DateTimeFor(m => m.ToDate2,
                                DateTime.Now.AddMonths(6),
                                true).DateCalendar(inLine: false, calendarOptions:
                                    new CalendarOptions{
                                           ChangeYear = true,
                                           ChangeMonth =true
                                    })
                    
                 </span>  
                 @filter.ViewsOnOff("ToGroup2", false)
             </div>

We can use simultaneously the DataFilterClauseFor and DataFilterBuilder extension methods. In such a case the clauses provided by all DataFilterClauseFor calls are added with a logical and to the single LinQ expression provided by the DataFilterBuilder call:

@{var filter = Html.DataFilterBuilder(m => m.ToDoFilter, new ToDoByDateFilter());}
     
      <div >
         
         @filter.CheckBoxFor(m => m.FromDateEnabled,
             new Dictionary<string, object>() { { "class", "FromGroup_checkbox" } })
         @filter.LabelFor(m => m.FromDate)
         
      
          <span class ="FromGroup" >
                 @filter.DateTimeFor(m => m.FromDate,
                     DateTime.Now.AddMonths(-6),
                     true).DateCalendar(inLine:false, calendarOptions:
                         new CalendarOptions{
                                ChangeYear = true,
                                ChangeMonth =true
                         })
          </span>     
          @filter.ViewsOnOff("FromGroup", false)
      </div>
      
      <div>
          
          @filter.CheckBoxFor(m => m.ToDateEnabled,
             new Dictionary<string, object>() { { "class", "ToGroup_checkbox" } })
         @filter.LabelFor(m => m.ToDate)
          
      
         <span class ="ToGroup" >
                  @filter.DateTimeFor(m => m.ToDate,
                         DateTime.Now.AddMonths(6),
                         true).DateCalendar(inLine: false, calendarOptions:
                             new CalendarOptions{
                                    ChangeYear = true,
                                    ChangeMonth =true
                             })
             
          </span>  
          @filter.ViewsOnOff("ToGroup", false)
      </div>

That’s all! The full code used in this tutorial can be found in the Mvc3 Razor – Filtering folder of the zipped file BasicTutorialsCode here 

Stay Tuned !

Francesco

Tags: , , , , ,

Nov 15 2011

Mvc Controls Toolkit Datagrid Updated Tutorial

Category: MVC | Entity Framework | Asp.netFrancesco @ 23:42

 

This post is an updated  tutorial on how to use the Update/Delete/Insert Templated datagrid of the MVC Controls Toolkit. It contains information already published in previous tutorials updated to the new versions of the MVC Controls Toolkit. The full code used in this tutorial can be found in the Mvc3 Razor folder of the zipped file BasicTutorialsCode  here

The commercial version of the toolkit contains datagrids with several more options, and that may be configured and styled more quickly with a fluent interface.

Our Data model is simply a Todo List with four fields: Name(varchar), Description(varchar), DueDate(date), and the principal key id of type identity. First, we define the Database, and a table named ToDo with the previous four fields.

Next, we create an Entity Framework(EF) model based on this database. We then create a MetaClass to handle the constraints on our ToDo items. Let say that the DueDate can range from 3  months before today to 6 months after today. Since today is not an absolute value such a constraint cannot be interpreted as a Data Layer constraint, therefore it sholdn’t be put with the other data constraints of the data layer. We will take care of it in a few minutes ! First lets take care of the other simpler constraints, and add to our project a new file named Todo.cs just under the Models folder. What we put in this file is quite standard:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using MVCControlsToolkit.DataAnnotations;

namespace Mvc_Examples.Models
{
    [MetadataType(typeof(MetaToDo))]
    public partial class ToDo
    {

    }
    public class MetaToDo
    {
        
        [Required, CanSort, Display(Name="Name")]
        public object Name { get; set; }
        [Required, Display(ShortName = "Description")]
        public object Description { get; set; }
        [CanSort, Display(ShortName = "Due Date"), Format(DataFormatString="{0:D}")]
        public object DueDate { get; set; }
    }
}

The Display attribute is  a standard DataAnnotation attribute and it just declares the string to be used in the labels for our properties. The CanSort attribute comes from the MVC Controls Toolkit and it declares that the field can be used in sorting operations. We will come back on this later on, when we will speak about the sorting helpers of the MVC Controls Toolkit.

Now let go to the View Model! What do we need to put into our View Model? For sure the ToDo items extracted from the database. However, we would like the items to be pageable, so we have to insert at least a Page property containing the current page. A single Page int is enough for the MVC Controls Toolkit pager to work; for a better paging experience you can supply the total number of pages.

Now…the user may edit some fields and then he may change page. What happens if there are validation errors in his editing? A good option is to go back to the previous page and force him to correct the errors. Therefore, we have to remember what page we came from. Thus we need also a previous page property in our View Model. Luckily we don’t need to put it in a hidden field because our pager already offers this type of service when a “previous page” property is available.

Summing Up, our View Model has the following properties: CurrPage, PrevPage, the optional TotalPages, plus a ToDoList property containing all ToDo items.

Now we have to decide how to handle the strange constraint on the DueDate. One way to handle it is to define a View Model version of the ToDo class where we can apply this Presentation Layer specific constraint. We can use the Mvc Controls Toolkit DateRangeAttribute, that offers the ability to define constraints based on expressions containing “Today” or “Now”:

[MetadataType(typeof(MetaToDo))]
    public partial class ToDoView
    {
        public int? id { get; set; }

        public string Name { get; set; }

        public string Description { set; get; }

        [DateRange(SMinimum = "Today-3M", SMaximum = "Today+6M")]
        public DateTime DueDate {set;get;}

       

    }

Note that we have used the same MetaData class of the original ToDo class. This is important in order to avoid code duplications! As a general rule when we build a View Model either we use  the original data classes, or for  each of the original data classes we define a ViewModel version of it and use it as a child of the page ViewModel. This way, we can use the same metaclasses. You should avoid putting fields from more than one data class into a single View Model Class. An exception to this rule is if data are someway aggregated and transformed into other properties. It is preferred to keep a one to one correspondence between the original data classes and the classes used in the View Model; this way we increase modularity and reuse the metadata classes of the original data classes(duplicating code often causes a lot of problems…).

Our next step is the design of the data access procedures. A best practices is to not insert them into the controller methods, because they might be useful to more than one controller, and because it is always better to keep the data layer separate from the controller layer. We create them as static methods of our View Model. This approach is the standard for accessing ToDo items (we will use it each time paged ToDo items are needed). This choice is acceptable for this simple application, but in different situations we might have introduced a new Repository class.

Summing up our View Model contains:

public int TotalPages { get; set; }
public int CurrPage { get; set; }
public int PrevPage { get; set; }
public List<Tracker<ToDoView>> ToDoList {get; set;}

Plus two static methods: GetToDoPage and UpdatePage, for retrieving one page of data, and for passing to the database the updates made by the user to one page of data.

In order to help passing changes to the database the MVC Controls Toolkit provides the class Tacker<T> that is a wrapper put around a data item. It maintains two versions of the data item, the original version and the one with the changes applied by the user. It also has a Boolean value Changed to signal that the two versions are different.

Comparison between the two versions yields the operation to be done on the database:

  • Old version null and new version non-null: Insert
  • Old version non-null and new version null: Delete
  • Both versions non-null: we have an update

Lets analyze the GetToDoPage method:

public static List<Tracker<ToDoView>> GetToDoPage(int pageDim, out int totalPages, ref List<KeyValuePair<LambdaExpression, OrderType>> order, int page = 1)
{
    List<Tracker<ToDoView>> result;
    if (order == null)
    {
        order = new List<KeyValuePair<LambdaExpression, OrderType>>();
       
    }
    if (order.Count == 0)//paging require ordering! Therefore we always need to add a default oredering
    {
        Expression<Func<ToDoView, DateTime>> defaultOrder = m => m.DueDate;

        order.Add(new KeyValuePair<LambdaExpression, OrderType>(defaultOrder, OrderType.Descending));
    }
    using (SiteDbEntities context = new SiteDbEntities())
    {
        int rowCount = context.ToDo.Count();
        
        if (rowCount == 0)
        {
            totalPages=0;
            return new List<Tracker<ToDoView>>();
        }
        totalPages = rowCount / pageDim;
        if (rowCount % pageDim > 0) totalPages++;
        if (page > totalPages) page = totalPages;
        if (page < 1) page = 1;
        int toSkip = (page-1) * pageDim;

        
            result = context.ToDo.Select(item =>
                new ToDoView() { Name = item.Name, Description = item.Description, DueDate = item.DueDate, id = item.id }).ApplyOrder(order).Select(viewItem =>
                new Tracker<ToDoView>
                        {
                            Value = viewItem,
                            OldValue = viewItem,
                            Changed = false
                        }).Skip(toSkip).Take(pageDim).ToList();
        
    }
    return result;
}

The return is of type List<Tracker<ToDoView>> since we have put our wrapper around each data item. We create a context, with the using keyword to guarantee it will be disposed at the end of the operation (because it contains a connection to the database that is not a managed object).

As a first operation we count the total rows, then we do some mathematics to compute the pages: the remainder operation % is needed to take into account partially filled pages. In order to reach the request page we use the Skip and Take methods. In the select construct we create our wrapper and fill it with two copies of the same data item; one for the previous and one for the updated version of the data item. Note we transfer data from the ToDo object into a fresh ToDoView object.

The order parameter contains field sorting information that are passed as a list of couples. Each couple contains a lambda expression that specifies a field, and an OrderType value that specifies the kind of sorting (ascending or descending). The sorting information is passed to the controller by the EnableSortingFor helper (discussed later in in this post). The MVC Controls Toolkit defines extension methods to apply the sorting information to any IEnumerable or IQueryable. You only need to include the namespace: MVCControlsToolkit.Linq and any IEnumerable or IQueryable will be enriched with the method ApplyOrder that accepts the above list as argument. If an ordering is already defined on either the IEnumerable or the IQueryable the new sorting will be chained Lexicographically with it.

The full code contains also the possibility for filter data. The interested reader may refer to my new tutorial on Data Filtering: Advanced Data Filtering Techniques in the Mvc Controls Toolkit and to my previous tutorial: Data Filtering in the New Mvc 3 Version of The Mvc Controls Toolkit

The UpdatePage method is a little bit more complex, but quite easy, too:

public static void UpdatePage(List<Tracker<ToDoView>> items)
{
    if (items == null) return;
    using (SiteDbEntities context = new SiteDbEntities())
    {
        bool aChange = false;
        foreach (Tracker<ToDoView> item in items)
        {
            if (item.Changed)
            {
                if (item.OldValue == null) //insertion
                {
                    if (item.Value != null)
                    {
                        ToDo curr=new ToDo()
                                 { Name = item.Value.Name, Description = item.Value.Description, DueDate = item.Value.DueDate };
                        aChange = true;
                        context.ToDo.AddObject(curr);
                    }
                }
                else if (item.Value == null) //deletion
                {
                    ToDo curr=new ToDo() { Name = item.OldValue.Name, Description = item.OldValue.Description, DueDate = item.OldValue.DueDate, id=item.OldValue.id.Value };
                    context.ToDo.Attach(curr);
                    context.ObjectStateManager.ChangeObjectState(curr, System.Data.EntityState.Deleted);
                    aChange = true;
                }
                else//update
                {
                    ToDo curr = new ToDo() { Name = item.Value.Name, Description = item.Value.Description, DueDate = item.Value.DueDate, id=item.Value.id.Value };
                    context.ToDo.Attach(curr);
                    context.ObjectStateManager.ChangeObjectState(curr, System.Data.EntityState.Modified);
                    aChange = true;
                }
            }
        }
        if (aChange)
        {
            try
            {
                context.SaveChanges();
                items.ForEach((item) => { item.Confirm(); });//confirm changes have been passed
            }
            catch
            {
            }
        }
    }
}

We have a loop on all modified items where we verify if the item has changed, and if it has changed we analyze what operation needs to be passed to the database as previously.explained. Note the use of the ObjectStateManger to set the correct state of the various objects in the different cases. Insertion is the only case that doesn’t require manually setting the state of the object.

If at least a data item has changed we do a SubmitChanges() that passes all changes in a single transaction to the database. If no exception occurs we call the Confirm() method of the Tracker<T> wrapper that sets the old version of the data item  equal to the new version of it since all changes have been persisted in the database. In case of exceptions an error message should be returned to the controller that instructs the user to make corrections and retry the post. We have not handled this to keep the sample simple.

Now we can go to the controller that has just two Action methods, one for handling the initial HttpGet and the second for handling the subsequent HttpPost:

public const int PageDim=5;//in actual application this should be put in a config file
public ActionResult Index()
{
    int totalPages;
    List<KeyValuePair<LambdaExpression, OrderType>> order = null;
    ToDoViewModel result = new ToDoViewModel()
        {
            ToDoList = ToDoViewModel.GetToDoPage(PageDim, out totalPages, ref order),
            TotalPages = totalPages,
            CurrPage=1,
            PrevPage=1,
            ToDoOrder=order
        };
    return View(result);
}

[HttpPost, System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult Index(ToDoViewModel model)
{
    if (!ModelState.IsValid)
    {
        
        model.CurrPage=model.PrevPage; //cancel possible page change and force correcting errors
        return View(model);
    }
    else
    {
        ModelState.Clear();
        ToDoViewModel.UpdatePage(model.ToDoList);
        int totalPages;
        if (model.CurrPage < 1) model.CurrPage = 1;
        List<KeyValuePair<LambdaExpression, OrderType>> order = model.ToDoOrder;
        ToDoViewModel result = new ToDoViewModel()
        {
            ToDoList = ToDoViewModel.GetToDoPage(PageDim, out totalPages, ref order, model.CurrPage, model.ToDoFilter),
            TotalPages = totalPages,
            CurrPage = Math.Min(model.CurrPage, totalPages),
            PrevPage = Math.Min(model.CurrPage, totalPages),
            ToDoFilter = model.ToDoFilter,
            ToDoOrder=order
        };
        return View(result);
    }
}

The first method just displays the first page and it is quite trivial. The second method handles validation errors. If there are validation errors it cancels the page change by resetting the current page to the previous page, and then returns the same View Model it received in order to let the user corrects the errors. If everything is ok it passes the changes to the database, and retrieves the new page requested by the user. That’s all!

We finally arrived to the datagrid. In order to have the datagrid working we need to prepare 4 templates:

  1. displayTemplate: it displays a row of data when the grid is in display mode
  2. editTemplate: it displays a row of data when the grid is in edit mode
  3. addDisplayTemplate: it defines the look of the insert new row component, normally it just displays an insert button.
  4. gridTemplate: it displays the general container where all data items will be inserted. In our case it is just a table.

As discussed in the documentation about templates, Templates can be passed both as: Partial Views names, Razor Templates, Razor in-line templates, or Lambda Expressions. Here, we will use Razor in-line templates. The reader interested in Partial Views may refer to my previous post: Defining Mvc Controls 2: Using the DataGrid.

Razor in-line templates can be passed directly in the DatagridFor call as arguments. They receive an HtmlHelper<ToDoView> object as the value of a standard parameter called item. We can use this item  variable in exactly the same way we use the Html variable in a standard View.

Lets see in detail each template.

displayTemplate
@<text>         
    <td class="ToDo">
        @item.ValidationMessageFor(m => m.Name, "*")
        @item.DisplayField(m => m.Name)
    </td>
    <td class="editor-field">
        @item.ValidationMessageFor(m => m.DueDate, "*")
        @item.DisplayField(m => m.DueDate)
    </td>
    <td class="ToDo">
        @item.ValidationMessageFor(m => m.Description, "*")
        @item.DisplayField(m => m.Description)
    </td>
    <td class="ToDoTool">
        @item.DetailLink(Ajax, "Edit Details", DetailType.Edit, "ToDoSubTasks", "Home",
            new {
                id = item.ViewData.Model.id},
                                null)
        @item.ImgDataButton(DataButtonType.Edit, "../../Content/edit.jpg", null)
    
    </td>
    <td class="ToDoTool">
        @item.ImgDataButton(DataButtonType.Delete, "../../Content/delete.jpg", null)
    </td>
</text>
)

The display item View Model is just a data item, it is not a Tracker<T> wrapper. The wrapper is handled automatically by the DataGrid.

We don’t need to put the <tr> tag in each data item: the container tag for each item is defined in the DataGrid helper and it is automatically inserted by the DataGrid. We have also the option to supply a delegate that returns a different item container as a function of the data item and of its position in the DataGrid.

It is worth discussing the two data buttons: the first one switches the row to edit mode, while the second one just deletes the row. In the example below an image button is used, but you can also use link and button helpers.

editTemplate
_S.H<ToDoView>(
@<text>         
     <td class="ToDo">
        @item.ValidationMessageFor(m => m.Name, "*")
        @item.TextBoxFor(m => m.Name)
     </td>
     <td class="ToDo">
        @item.ValidationMessageFor(m => m.DueDate, "*")
        @item.DateTimeFor(m => m.DueDate, DateTime.Today).Date()
     </td>
     <td class="ToDo">
        @item.ValidationMessageFor(m => m.Description, "*")
        @item.TextBoxFor(m => m.Description)
     </td>
     <td class="ToDoTool" colspan="2">
        @item.HiddenFor(m => m.id)
        @item.ImgDataButton(DataButtonType.Cancel, "../../Content/undo.jpg", null)
     </td>
</text>
)

The edit template is completely analogous to the display template. The only difference being that it contains input field allowing you to edit fields.

Please, notice the Hidden field containing the key! It is necessary! Here we have a cancel button that undoes all changes done to the data item and put the row in display mode again.

Last thing worth pointing out is the DateTimeFor helper that is able to take as input, date, time or date and time. It is able to read the DateRange attribute and to enforce its constraints. This means the user is allowed to insert just dates that conforms with the constraints. More information about the DateTimeFor Helper can be found here.

addDisplayTemplate
_S.H<ToDoView>(
               @<td colspan="5" class="ToDo">@item.ImgDataButton(DataButtonType.Insert, "../../Content/add.jpg", null)</td>
                  )

The insert template just contains an insert button that when clicked causes a new row to appear in edit mode.

gridTemplate
   _S.H<ToDoView>(
     @<table class="ToDo" >
       <tr>
       <td class="ToDoHeader"><strong>@item.SortButtonFor(m => m.Name, sortButtonStyle: SortButtonStyle.Button)</strong></td>
       <td class="ToDoHeader"><strong>@item.SortButtonFor(m => m.DueDate, sortButtonStyle: SortButtonStyle.Button)</strong></td>
       <td class="ToDoHeader"><strong>@item.ColumnNameFor(m => m.Description)</strong></td>
       <td class="ToDoHeader"><strong></strong></td>
       <td class="ToDoHeader"><strong></strong></td>
       </tr>
       @item.ViewData["Content"]
   </table>
)

It defines the look of the container of all items. In our case it just displays the <table> tag and the header of the table. The ColumnNameFor helper displays the name of a column, taking it from the Display attribute(ShortName if available, otherwise Name).

The SortButtonFor helper renders sort buttons on the columns where sorting is allowed. They can be used only together with the EnableSortingFor helper that we will discuss below.

The @item.ViewData[“Content”] construct  defines where all data items have to be inserted. It is a kind of placeholder and it needs to be inserted “as it is” in any template that describes a datagrid container.

It is worth pointing out that this template is passed an empty data item object to help the automatic construction of the container. Specifically, we can use reflection to extract all columns and we can also automatically decide some facts about the look of the container by extracting the attributes of each data item property (as for instance the display attribute).

The Datagrid

Summing up the whole call to the DataGridFor helper is:

@Html.DataGridFor(m => m.ToDoList, ItemContainerType.tr,
            _S.H<ToDoView>(
            @<text>         
                 <td class="ToDo">
                    @item.ValidationMessageFor(m => m.Name, "*")
                    @item.TextBoxFor(m => m.Name)
                 </td>
                 <td class="ToDo">
                    @item.ValidationMessageFor(m => m.DueDate, "*")
                    @item.DateTimeFor(m => m.DueDate, DateTime.Today).Date()
                 </td>
                 <td class="ToDo">
                    @item.ValidationMessageFor(m => m.Description, "*")
                    @item.TextBoxFor(m => m.Description)
                 </td>
                 <td class="ToDoTool" colspan="2">
                    @item.HiddenFor(m => m.id)
                    @item.ImgDataButton(DataButtonType.Cancel, "../../Content/undo.jpg", null)
                 </td>
            </text>
            ),
             _S.H<ToDoView>(
            @<text>         
                <td class="ToDo">
                    @item.ValidationMessageFor(m => m.Name, "*")
                    @item.DisplayField(m => m.Name)
                </td>
                <td class="editor-field">
                    @item.ValidationMessageFor(m => m.DueDate, "*")
                    @item.DisplayField(m => m.DueDate)
                </td>
                <td class="ToDo">
                    @item.ValidationMessageFor(m => m.Description, "*")
                    @item.DisplayField(m => m.Description)
                </td>
                <td class="ToDoTool">
                    @item.DetailLink(Ajax, "Edit Details", DetailType.Edit, "ToDoSubTasks", "Home",
                        new {
                            id = item.ViewData.Model.id},
                                            null)
                    @item.ImgDataButton(DataButtonType.Edit, "../../Content/edit.jpg", null)
                
                </td>
                <td class="ToDoTool">
                    @item.ImgDataButton(DataButtonType.Delete, "../../Content/delete.jpg", null)
                </td>
            </text>
            ),
            _S.H<ToDoView>(
              @<table class="ToDo" >
                <tr>
                <td class="ToDoHeader"><strong>@item.SortButtonFor(m => m.Name, sortButtonStyle: SortButtonStyle.Button)</strong></td>
                <td class="ToDoHeader"><strong>@item.SortButtonFor(m => m.DueDate, sortButtonStyle: SortButtonStyle.Button)</strong></td>
                <td class="ToDoHeader"><strong>@item.ColumnNameFor(m => m.Description)</strong></td>
                <td class="ToDoHeader"><strong></strong></td>
                <td class="ToDoHeader"><strong></strong></td>
                </tr>
                @item.ViewData["Content"]
            </table>
         ),
         _S.H<ToDoView>(
               @<td colspan="5" class="ToDo">@item.ImgDataButton(DataButtonType.Insert, "../../Content/add.jpg", null)</td>
                  ))

The first argument of the helper, as usual, defines the property to display in the DataGrid. The second property defines the kind of item container to be used (in our case a <tr>), and the last ones are the templates we discussed before.

There are also optional arguments to define html attributes(also as a function of the data item) and to pass a function to change dynamically the item container.

Finally, the pager is composed of various parts that can be used also  separately. Here I used link buttons but one can use also image or normal buttons:

  <div class="ToDoPager">
             @{ var pager = Html.PagerFor(m => m.CurrPage, m => m.PrevPage, m => m.TotalPages);}
             @pager.PageButton("<<", PageButtonType.First, PageButtonStyle.Link)
             @pager.PageButton("<", PageButtonType.Previous, PageButtonStyle.Link)
             @pager.PageChoice(5)
             @pager.PageButton(">", PageButtonType.Next, PageButtonStyle.Link)
             @pager.PageButton(">>", PageButtonType.Last, PageButtonStyle.Link)
             @pager.PageButton("Go To", PageButtonType.GoTo, PageButtonStyle.Button)
             @pager.GoToText(new { style = "width:50px;" })
</div>

The first call gets an object to be used to render all parts. The PageChoice method displays five page links before the current page, 5 page links after the current page and the number of the current page. There are buttons for the next page, last page, etc., and also a button plus a textbox to jump to any page.

Sorting

The first step to enable sorting with a mouse click on the columns is the insertion of a new property in our ViewModel to receive sorting information:

public List<KeyValuePair<LambdaExpression, OrderType>> ToDoOrder { get; set; }

The toolkit datagrid is able to exchange directly the lambda expressions to be used in LINQ queries with the Controller. Thus, we use a list of couples LambdaExpression OrderType. Where OrderType is an enumeration that specifies ascending or descending order. The whole list specifies a lexicographic sorting that can be based also on several columns. As previously discussed, sorting information can be used directly with LINQ expressions by means of the IEnumerable and IQueryable extension ApplyOrder defined in the namespace MVCControlsToolkit.Linq , .

It is worth pointing out that the sorting information come from the client, therefore a malicious user might try a denial of service attack by sending a manipulated sorting request involving  columns that are too difficult to sort (columns with no indexes defined on them, for instance). In order to defend ourselves from such an attack the Transformation Handler that receives the data from the DataGrid automatically discards columns that are not decorated with the CanSortAttribute that is defined in the MVCControlsToolkit.DataAnnotations namespace.

Sorting is defined by the user through the column sort buttons we have already seen in the grid header. By default, sort buttons do not cause a post. However they have a parameter to require an immediate post. When a new sorting is applied if there is a pager operating on the grid the page is automatically reset to the first page.

The up and down arrows of the sort buttons are defined in CSS classes specified in the sorting helper:

@Html.ManipulationButton(ManipulationButtonType.ResetGrid, "Reset", m => m.ToDoList, null, ManipulationButtonStyle.Button)
        @Html.EnableSortingFor(m => m.ToDoList, m => m.ToDoOrder, "NormalHeaderToDo", "AscendingHeaderToDo", "DescendingHeaderToDo", page: m => m.CurrPage)

In order to do its job the EnableSortingFor helper needs the collection to be sorted, the property that will contain all sorting information and also the page property. As the sort behavior of the column changes, the three CSS classes specified in the EnableSortingFor helper are applied to the buttons in order to change their look.

Another interesting feature is the ManipulationButtonType.ResetGrid that undoes all changes done to the datagrid that are not yet committed. Here commitment means commitment to a database or to any other storage, not simply a post: the grid remembers its previous values through several posts! We declare that commitment took place either by calling the Confirm method of a Tracker<Item> associated to a row or by simply reloading the grid with fresh data.

In order to undo a single row delete we have introduced the undelete data button and a template to substitute deleted rows. The undelete is easy: just put the undelete button in this template! However I prefer seeing my rows disappear completely! Therefore I have not used this feature in this example (no panic….in case of errors we have the reset button..).

That’s all! The deveoper’s that don’t want to add each time, pagers, sort buttons, filters, etc to their DataGrids can use Theming to define once and for all the look and features of their DataGrids,…or they can simply modify the DataGrid Theme I already defined in the RazorThemedGrid file in the download area. A tutorial on theming is here.

Stay Tuned !

Francesco

Tags: , , , , , , ,

Feb 6 2011

Data Filtering, in the New Mvc 3 Version of the Mvc Controls Toolkit

Category: Entity Framework | MVC | Asp.netFrancesco @ 05:11

See also an updated version of this post:  Advanced Data Filtering Techniques in the Mvc Controls Toolkit

Mvc Controls Toolkit Datagrid Updated Tutorial (updated version of Defining MVC Controls 2: Using the DataGrid)

Defining MVC Controls 3: Datagrid, Sorting, and Master-Detail Views

The Mvc Controls Toolkit offers an interesting data filtering feature: the controller can receive directly a LinQ expression defining the filtering criteria chosen by the user when a view is posted. This way the user can choose among several filtering options by clicking buttons, or selecting checkboxes and then filling the input fields of the selected filtering options. No need to define ViewModel properties for each of the available options, the action method of the controller receives always the same data structure: a LinQ Expression. New filtering options can be easily added modularly with the no need to modify the controller and the code behind it. This increases modularity and makes easier  software maintenance.

Let see how this works in practice with the help of the same example used in my previous posts about the Mvc Controls Toolkit library(see the links next to the title). The whole code used in this example can be downloaded here(the file named BasicTutorialsCode), while screenshots of the software running are here. .

First of all we add the property destined to contain the LinQ expression to the ViewModel:

         public int TotalPages { get; set; }
        public int CurrPage { get; set; }
        public int PrevPage { get; set; }
        public List<Tracker<ToDoView>> ToDoList {get; set;}
        public List<KeyValuePair<LambdaExpression, OrderType>> ToDoOrder { get; set; }
        public System.Linq.Expressions.Expression<Func<ToDoView, bool>> ToDoFilter { get; set; }

The LinQ Expression is a map from ToDoView, that is the class the filter will be applied to, and bool, since formally it defines a function that verifies if an instance of ToDoView satisfies the criterium of the filter.

Now we can use this expression directly in the method that retrieves the items:

if (filter == null)
                {
                    result = context.ToDo.Select(item =>
                        new ToDoView() { Name = item.Name, Description = item.Description, DueDate = item.DueDate, id = item.id }).ApplyOrder(order).Select(viewItem =>
                        new Tracker<ToDoView>
                                {
                                    Value = viewItem,
                                    OldValue = viewItem,
                                    Changed = false
                                }).Skip(toSkip).Take(pageDim).ToList();
                }
                else
                {
                    result = context.ToDo.Select(item =>
                        new ToDoView() { Name = item.Name, Description = item.Description, DueDate = item.DueDate, id = item.id }).Where(filter).ApplyOrder(order).Select(viewItem =>
                        new Tracker<ToDoView>
                        {
                            Value = viewItem,
                            OldValue = viewItem,
                            Changed = false
                        }).Skip(toSkip).Take(pageDim).ToList();
                }

The code above shows that it was enough to add a Where(filter) instruction to our original code to get the filtered data.

Now we need to define a class for each filtering option. For sake of semplicity let just define a single filtering option. Our class needs just to implement an interface:

public interface IFilterDescription<TData>
    {
        Expression<Func<TData, bool>> GetExpression();
    }

The GetExpression function returns the actual Filter. The input fields that the user is required to fill are represented by properties of this class, and are used in the implementation of GetExpression():

public class ToDoItemByNameFilter: 
        IFilterDescription<ToDoView>
    {
        [Required]
        [Display(Prompt="chars the name of item starts with")]
        public string Name {get; set;}
        public System.Linq.Expressions.Expression<Func<ToDoView, bool>> GetExpression()
        {
            System.Linq.Expressions.Expression<Func<ToDoView, bool>> res = null;
            Name=Name.Trim();
            if (!string.IsNullOrEmpty(Name))
            {
                Name=Name.Trim();
                res= m => (m.Name.StartsWith(Name));
                
            }
            return res;
        }
    }

In our case we would like to select all items whose Name field starts with the characters inserted in the Name property defined in our ToDoByNameFilter class.

We just Trim our input and then we use it in the LinQ expression returned by GetExpression.

Our ToDoItemByNameFilter plays also the role of ViewModel for a partial view that takes care of the user interface for our filter, so we can decorate its Name property with attributes defining both validation and appearance constraints. In our case we defined a WatermarK through the Prompt property of the Display attribute.

In order to make the watermark actually apppears and the textbox add some adequate formatting we can use the TypedTextBox of the Mvc Controls Toolkit in the partial view that defines the graphical appearance of our filter:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Mvc_Examples.Controls.ToDoItemByNameFilter>" %>
<%@ Import Namespace=" MVCControlsToolkit.Core" %>
<%@ Import Namespace=" MVCControlsToolkit.Controls" %>
<%: Html.TypedTextBoxFor(m => m.Name, watermarkCss:"watermark") %>

Now it remains just to insert the helper of the filter in our View. In order to let the user selects among several filters we can use the ViewList defined in the Mvc Controls Toolkit. This way either by clicking some mutual exclusive checkboxes or by clicking some button, the user let the Partial View associate to a Filter to appear. In our case for sake of simplicity we give to the user just the choice between our previous Name filter and no filter at all:

<div >
    <%: Html.ViewList("ToDoFilter", "ToDoFilterSelected") %> 
        <input id="Checkbox1" type="checkbox" class = 'ToDoFilter_checkbox ByNameFilter_checkbox'/>  &nbsp; Filter by name 
        <span id='ByNameFilter' class='ToDoFilter'><%:Html.DataFilter(m => m.ToDoFilter, 
                                new Mvc_Examples.Controls.ToDoItemByNameFilter(),
                                           "ToDoFilterByName")%></span>  
    </div>
    <div >
    <input id="Checkbox2" type="checkbox" class = 'ToDoFilter_checkbox NoFilter_checkbox'/>  &nbsp; NoFilter
    <span id='NoFilter' class='ToDoFilter'></span>
    </div>

The arguments of our DataFilter helper are just an expression that selects the ViewModel property to fill with the filtering expression, an instance of the class that defines the filter, and the name of the Partial View that we defined before.

The ViewList helper selects one of two spans, one contains the filter helper and the other is simply empty. The selection is done with two checkboxes.Details on how to use of the VieList can be found in the documentation here.

By adding a new argument to our DetailView Helper we can make also our detail view appears in a jQuery UI dialog:

<% Html.DetailFormFor(Ajax, m => m.ToDoList, ExternalContainerType.div,
           "ToDoSubTasks", "Home", null, "isChangedToDo", "isDeletedToDo", detailDialog:
           new MVCControlsToolkit.Controls.DataGrid.Dialog
           {
               Title = "prova dialogo",
               Show = "slide",
               Hide = "slide",
               MinWidth=800

           });%>
</asp:Content>

THAT’S ALL!  Download the code and enjoy!

                             

                                       Stay Tuned!

                                               Francesco

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: , , , , , , , ,

Nov 15 2010

Defining MVC Controls 3: Datagrid, Sorting, and Master-Detail Views

Category: Asp.net | Entity Framework | MVCFrancesco @ 00:15

Defining MVC Controls 1

Mvc Controls Toolkit Datagrid Updated Tutorial

Advanced Data Filtering Techniques in the Mvc Controls Toolkit

I am back with the second tutorial about using the Datagrid of the  MVC Controls Toolkit . We are going to modify the the example of my previous post to include sorting on a mouse click on the columns.

The code of this tutorial is containe in the download page of MVC Controls Toolkit, that is here and is named BasicTutorialsCode.

As a first step we need a new parameter in our View Model to exchange sorting information with the View:

public List<KeyValuePair<LambdaExpression, OrderType>> ToDoOrder { get; set; }

The toolkit datagrid is able to exchange directly the lambda expressions to be used in the Linq queries with the Controller. Thus, we use a list of couples Lambda Expression OrderType. Where OrderType is an enumeration that specifies ascending or descending order. The whole list specifies a lexicographic sorting that is possibly based on several columns.

MVC Controls Toolkit defines extension methods to apply directly the above list either to  an IEnumerable or to an IQueryable.It is just to include the namespace: MVCControlsToolkit.Linq and any IEnumerable or IQueryable will be enriched with the method ApplyOrder that accepts the above list as argument. If an ordering is already defined on either the IEnumerable or the IQueryable the new sorting will be chained Lexicographically with it.  

Now our paged search query becomes:

result = context.ToDo.Select(item =>
                    new ToDoView() { Name = item.Name, Description = item.Description, DueDate = item.DueDate, id = item.id }).ApplyOrder(order).Select(viewItem =>
                    new Tracker<ToDoView>
                            {
                                Value = viewItem,
                                OldValue = viewItem,
                                Changed = false
                            }).Skip(toSkip).Take(pageDim).ToList();

It is worth to point out that the sorting information comes from the client, therefore a malicious user might try a denial of service attack by sending a manipulated request of sorting on a column that is too  difficult to sort (a column with no index defined on it, for instance). In order to defend ourselves from a similar attack the Transformation Handler that receives the data from the DataGrid automatically discard columns that are not decorated with the CanSortAttribute that is defined in the MVCControlsToolkit.DataAnnotations namespace. In our case we have the following Data annotations:

        [Required, CanSort, Display(Name="Name")]
        public object Name { get; set; }
        [Required, Display(ShortName = "Description")]
        public object Description { get; set; }
        [CanSort, Display(ShortName = "Due Date"), Format(DataFormatString="{0:D}")]
        public object DueDate { get; set; }

The DisplayAttribute is a standard .Net. Our DataGrid uses ShortName or Name (if ShortName is not specified) for the header of both the unsortable and the sortable columns.The FormatAttribute  inherits from the standard .Net DisplayAttribute and extends it with some properties that we will use in the 0.8 release of the MVC Controls Toolkit, where we will define typed input fields. At moment it has the same behavior of its parent. In this example we use it to specify a nice format for the date.

Now our datagrid template becomes:

<table class="ToDo" >
<tr>
<td class="ToDoHeader"><strong><%:Html.SortButtonFor(m => m.Name, sortButtonStyle: SortButtonStyle.Button) %></strong></td>
<td class="ToDoHeader"><strong><%:Html.SortButtonFor(m => m.DueDate, sortButtonStyle: SortButtonStyle.Button) %></strong></td>
<td class="ToDoHeader"><strong><%: Html.ColumnNameFor(m => m.Description) %></strong></td>
<td class="ToDoHeader"><strong></strong></td>
<td class="ToDoHeader"><strong></strong></td>
</tr>
<%:ViewData["Content"] as MvcHtmlString %>
</table>

Where we can see the use of the sort buttons, and of the ColumnNameFor helper. As default, sort buttons do not  cause post-backs, however they have a parameter to require immediate post-back. Personally I prefer avoiding immediate post-back because this way I can set up my complete lexicographic sorting and only then I require a post-back to update my grid. In any case if there is a pager operating on the grid the change of the sorting causes the page to be reset to the first page automatically.

The up and down arrows of the sort buttons are defined in CSS classes specified in the sorting helper:

  <%:Html.DataGridFor(m => m.ToDoList, ItemContainerType.tr,  "ToDoEditItem",  "ToDoDisplayItem", "ToDoGrid", "ToDoInsertItem")%>
    </div>
    <div class="ToDoPager">
                    <% var pager = Html.PagerFor(m => m.CurrPage, m => m.PrevPage, m => m.TotalPages); %>
                    <%:pager.PageButton("<<", PageButtonType.First, PageButtonStyle.Link) %>
                    <%:pager.PageButton("<", PageButtonType.Previous, PageButtonStyle.Link) %>
                    <%:pager.PageChoice(5) %>
                    <%:pager.PageButton(">", PageButtonType.Next, PageButtonStyle.Link) %>
                    <%:pager.PageButton(">>", PageButtonType.Last, PageButtonStyle.Link) %>
                </div>
    <div>
    <input type="submit" value="Save" />
    <%:Html.ManipulationButton(ManipulationButtonType.ResetGrid, "Reset", m => m.ToDoList, null, ManipulationButtonStyle.Button) %>
    <%: Html.EnableSortingFor(m => m.ToDoList, m => m.ToDoOrder, "NormalHeaderToDo", "AscendingHeaderToDo", "DescendingHeaderToDo", page: m => m.CurrPage) %>
    <%:Html.HiddenFor(m => m.TotalPages) %>
To do its job the EnableSortingFor helper needs the collection, the sorting property and also the page property. As the sort behavior of the column changes, the three CSS classes specified in the EnableSortingFor helper are applied to the buttons in order to change their look.

Another new feature is the ManipulationButtonType.ResetGrid that undos all changes done to the datagrid and not yet committed. Here commitment means commitment to a database or to any other structure, not simply a post-back: the grid remembers its previous values through post-backs! We declare that commitment took place either by calling the Confirm method of a Tracker<Item> associated to a row or by simply reloading the grid with fresh data.

In order to undo a single row delete we have introduced the undelete data button and a template to substitute deleted rows. The undelete is easy: just put the undelete button in this template! However I prefer seeing my rows disappear completely! Therefore I have not used this feature in this example (no panic….in case of errors we have the reset button..).

The edit row template has not changed from our previous tutorial, but there are some changes in the display row template:

            <td class="ToDo">
                <%: Html.ValidationMessageFor(m => m.Name, "*")%><%: Html.DisplayField(m => m.Name) %>
            </td>
            <td class="editor-field">
                <%: Html.ValidationMessageFor(m => m.DueDate, "*")%><%: Html.DisplayField(m => m.DueDate)%>
            </td>
            <td class="ToDo">
                <%: Html.ValidationMessageFor(m => m.Description, "*")%><%:  Html.DisplayField(m => m.Description)%>
            </td>
            <td class="ToDoTool">
                <%:Html.DetailLink(Ajax, "Edit Details", DetailType.Edit, "ToDoSubTasks", "Home",
                    new { 
                        id = Model.id},
                                        null)%>
                <%: Html.ImgDataButton(DataButtonType.Edit, "../../Content/edit.jpg", null)%>
                
            </td>

The use of the DisplayField helper to display not editable fields not only ensures right formatting as the use of the DisplayFor standard Mvc helper, but also it enables fields representing the same data be synchronized automatically in the view! In particular it enables detail data retrieved via Ajax not only to display in a separate form but also to update the corresponding data in the master grid.

We have just introduced our next feature: the Master-Detail Ajax helper. Next to the Edit button that performs on line editing, there is also a Detail Link helper. In order to do its job this helper needs the Ajax object of the View, a name to put on the link, the name of an Action method returning a Partial View, the name of the associated Controller, and the route parameters. Moreover we need to specify also if the detail form we are going to render is a display or an edit form.

The Partial View returned by the Action method must contain neither an Ajax  nor a standard form: an Ajax form is already supplied by the Master Detail helper(this way we enhance re-usability because the same Partial View can be used with both Ajax and normal child calls):

<% Html.DetailFormFor(Ajax, m => m.ToDoList, ExternalContainerType.div,
           "ToDoSubTasks", "Home", null, "isChangedToDo", "isDeletedToDo");%>

The arguments passed to the helper are: the View Ajax object, the Master collection, the type of container where we woluld like to receive the Ajax content, an Action method and  a Controller to receive the post, some Html attributes and two CSS classes. The first class (in our case “isChangedToDo") is applied to the display data-field(the ones defined with the DisplayField helper) of the Master grid when they are changed because of the synchronization between detail form and Master data. The edit field of the grid are not updated because they contains new data provided by the user that this way can decide if continuing with  the change in the Master grid or if accepting the changes provided by the Ajax call.

Please note that the DetailFormHelper needs to be put out of the main form, don’t forget it!!!

The grid accepst also another optional row template to be used with rows that are updated because of the synchronization between detail and master View.

The second CSS class (in our case “isDeletedToDo")  is applied to a whole row when a data item retrieved by the Ajax call is discovered to be already deleted. If the user updates a deleted row a new row is created. Obviously the final choice is left to the Controller that may decide to abort the changes in a similar case.

In order to enhance re-usability of controllers (in particular of child controllers that returns Partial Views) we defined the [AcceptViewHint] Action Filters that enables a father view to require an action method of a controller uses a particular Partial View, in a way that is analogous to a client requiring that a Web Service uses a specific protocol or a client selecting a specific endpoint. The Partial View Hint is transmitted in either a Route parameter or a post parameter whose name is ViewHint.The name passed to the filter is scanned for acceptable length and may contain only alphanumeric characters in order to prevent attacks from malicious users.

I will not go into the code of the Detail View of the example that is completely analogous to the one of the Main View that we already discussed in detail in my previous post. The only things worth to note is the use of the SortableListFor helper to allows editing of the list of subtasks associated with a ToDo item. The SortableListFor helper allows delettion/in place editing/insertion of subtasks and allows also sorting of the subtasks by simply dragging them with the mouse .  The SortableListFor uses JQuery Sortable UI element, but transferring the order from the DOM into a collection of complex type on the server side….is not an easy task to accomplish.

Below the use of the helper:

<%: Html.SortableListFor(m => m.SubTasks, "SubTasksToSort", "SubTasksToSortAddItem", 0.7f,
                                        htmlAttributesContainer: new Dictionary<string, object> { { "class", "SortableList" } }
                    )%>

0.7f is the opacity used by the JQuery Sortable, the first argument is the collection to operate on, while “SubTasksToSort” is the Item template and “SubTasksToSortAddItem” is the template for inserting a new element.

Below the Item template:

<div id='<%: Html.PrefixedId("InnerContainer") %>'>
<%: Html.TextBoxFor(m => m.Name, new Dictionary<string, object> { { "class", "ToDoDetailName" } })%><%: Html.ValidationMessageFor(m => m.Name, "*") %>
<%: Html.TextBoxFor(m => m.WorkingDays, new Dictionary<string, object> {{"class", "ToDoDetailDuration"}})%><%: Html.ValidationMessageFor(m => m.WorkingDays, "*") %>
<%: Html.SortableListDeleteButton("Delete",  ManipulationButtonStyle.Link) %>
</div>

As one can see the SortableListFor helper has it own delete buttons.

Finally the Add Item template:

<div >
<%: Html.SortableListAddButton("Insert New", ManipulationButtonStyle.Link)%>
<span id='<%: Html.SortableListNewName() %>' style="visibility:hidden">
<%: Html.TextBoxFor(m => m.Name, new Dictionary<string, object> { { "class", "ToDoDetailName" } })%><%: Html.ValidationMessageFor(m => m.Name, "*") %>
<%: Html.TextBoxFor(m => m.WorkingDays, new Dictionary<string, object> {{"class", "ToDoDetailDuration"}})%><%: Html.ValidationMessageFor(m => m.WorkingDays, "*") %>
<%: Html.SortableListDeleteButton("Delete",  ManipulationButtonStyle.Link) %>
</span>
</div>

The Add Button of the SortableListFor needs a target container that it will make visible when it is pressed. We can specify it by giving the id returned by the helper

 Html.SortableListNewName() to a span, div or other container.

At moment we have finished……Next time we will speak about typed input fields and about the advanced filtering capabilities of the 0.8 release of the MVC Controls Toolkit.

                                          Stay Tuned……

                                           Francesco

For more information or consulences feel free to contact me,

Tags: , , , , ,

Oct 30 2010

Defining MVC Controls 2: Using the DataGrid

Category: MVC | Entity Framework | Asp.netFrancesco @ 00:46

See a more recent version of this tutorial: Mvc Controls Toolkit Datagrid Updated Tutorial

Defining MVC Controls 1

Defining MVC Controls 3: Datagrid, Sorting, and Master-Detail Views

Data Filtering, in the New Mvc 3 Version of the Mvc Controls Toolkit

This post is a tutorial un how to use the Update/Delete/Insert Templated datagrid of the MVC Controls Toolkit. You can download the full code used in this tutorial here (the package is named BasicTutorialsCode and it contains also the database)..

Our Data model is simply a Todo List with three fields: Name(varchar), Description(varchar) and DueDate(date), plus the principal key id of type identity. As first step we define the Database, and a table named ToDo  with the previous four fields.

Then, we create an entity framework model based on the database just created: I called it MeetingsModel because in a future post I will extend it with the management of a meeting room.

Now it is time to create a MetaClass to handle the constraints on our ToDo items. Let say that the Name and Description fields are required (i.e they can’t be null), while for the date we have a strange constraints: the DueDate can range from 1 month before today to 6 months after today. Since today is not a definite value such constraints can not be interpreted as a Data constraints, thus its natural place is not together with the other data constraints of the data layer: we will take care of it in a few minutes! First let take care of the the other simpler constraints, and add to our project a new file named Todo.cs just under the Models folder. What we put in this file is quite standard:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using MVCControlsToolkit.DataAnnotations;

namespace Mvc_Examples.Models
{
    [MetadataType(typeof(MetaToDo))]
    public partial class ToDo
    {

    }
    public class MetaToDo
    {
        [Required]
        public object Name { get; set; }
        [Required]
        public object Description { get; set; }
        
    }
}

Now let go to the View Model! What we need to put into the View Model? For sure the ToDo items extracted from the database. However, we would like the items be paged, so we have to insert at least a Page property containing the current page. A single Page int is enough for the MVC Controls Toolkit pager to work, but for a better paging experience it is better to supply also the total number of pages. It is ok, our pager control handle also this information, if available.

Now…the user might edit some fields and then he might change page. What does we do if there are validation errors in its editing? The better thing to do is to go back to the previous page and force him to correct the errors. Therefore, we have to remember what page we come from. Thus we need also a previous page property in our View Model. Luckily we don’t need to put it in a hidden field because our pager already offers  this type of service when a “previous page” property is available.

Summing Up, our View Model needs the following properties: CurrPage, PrevPage, and TotalPages, plus a ToDoList property containing all ToDo items.

Now we have to decide how to handle the strange constraint on the DueDate. One way to do it is to define a View Model version of the ToDo class where we can apply it. In this case we might solve in a simpler way but I prefer to be more general:

using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using MVCControlsToolkit.DataAnnotations;
 
namespace Mvc_Examples.Models
{
    [MetadataType(typeof(MetaToDo))]
    public partial class ToDoView
    {
        public int? id { get; set; }
 
        public string Name { get; set; }
 
        public string Description { set; get; }
 
        [DateRange(DynamicMinimum="MinDate", DynamicMaximum="MaxDate")]
        public DateTime DueDate {set;get;}
 
        [MileStone]
        public DateTime MaxDate
        {
            get 
            {
                return DateTime.Today.AddMonths(6);
            }
            set 
            {
            }
        }
 
        [MileStone]
        public DateTime MinDate
        {
            get 
            {
                return DateTime.Today.AddMonths(-1);
            }
            set 
            {
            }
        
        
        }
 
    }
}
 

Please notice that we have used the same MetaData class of the original ToDo class. This is important in order to avoid code duplications! As a general rule when building a View Model one has to use either the original data classes, or for each data class we define a modified version and use it as a son of the View Model: this way, we can use the same metaclasses. Please avoid putting fields from more than one data class into a single View Model Class. This can be done only if data are someway aggregated and transformed into other properties, otherwise it is better to keep a one to one correspondence between data classes and classes used in the View Model: this way we increase modularity and we can reuse the metadata classes of the original data classes(duplicating code always causes a lot of problems…).

We have defined two new properties MinData and MaxData that are always equal to Today minus one month and Today plus 6 months: they define the range of possible values for the DueDate property.

The Milestone attribute that decorates these two properties is defined in the MVCControlsToolkit.DataAnnotations namespace and it do exactly nothing…

It is just an hint for the View not to put these properties  into input fields  because their only purpose in the View is giving help to the user: the view designer can use them to display a message for the user, for example.

Our constraint on the DueDate is enforced by using the DateRange attribute defined in the MVCControlsToolkit.DataAnnotations namespace. This attribute act very similarly to the normal Range validation attribute, but accepts also dynamic minimum and maximum, that are essentially the names of the properties containing the actual minimum and maximum.

Next step is the design of the data access procedures. It is not a good idea to insert them into the controller methods, because they might be useful to more than one controller, and because it is always better to keep the data layer separate from the controllers layer. We will put them as static methods of our View Model that this way becomes a kind of  a standard for accessing ToDo items(we will use it always when paged ToDo items are needed). This choice is acceptable for this application, but in different situations we might have introduced a new class.

Therefore our View Model now contains the properties we discussed before:

        public int TotalPages { get; set; }
        public int CurrPage { get; set; }
        public int PrevPage { get; set; }
        public List<Tracker<ToDoView>> ToDoList {get; set;}

Plus two static methods, say GetToDoPage and UpdatePage, respectively for retrieving one page of data, and for passing to the database the updates made by the user to one page of data.

In order to help passing changes to the database the MVC Controls Toolkit furnishes the class Tacker<T> that is a wrapper to put around a data item. It maintains two versions of the data item, the original version and the one with the changes applied by the user. Moreover, it has also a Boolean value Changed to signal that the two versions are different.

Comparison between the two versions yields easily the operation to be done on the database:

  • Old version null and new version different from null: we have an insertion
  • Old version different from null and new version null: we have a deletion
  • Both versions different from null: we have an update

After this short premise let go analyze the GetToDoPage method:

        public static List<Tracker<ToDoView>> GetToDoPage(int pageDim, out int totalPages, int page = 1) 
        {
            List<Tracker<ToDoView>> result;
            using (SiteDbEntities context = new SiteDbEntities())
            {
                int rowCount = context.ToDo.Count();
                if (rowCount == 0)
                {
                    totalPages=0;
                    return new List<Tracker<ToDoView>>();
                }
                totalPages = rowCount / pageDim;
                if (rowCount % pageDim > 0) totalPages++;
                int toSkip = (page-1) * pageDim;
                result = (from item in context.ToDo
                          orderby item.DueDate ascending
                          select new Tracker<ToDoView>()
                          {
                           Value = new ToDoView() { Name = item.Name, Description = item.Description, DueDate = item.DueDate, id = item.id },
                           OldValue = new ToDoView() { Name = item.Name, Description = item.Description, DueDate = item.DueDate, id=item.id }, 
                          Changed=false }).Skip(toSkip).Take(pageDim).ToList();
            }
            return result;
        }

The result is of type  List<Tracker<ToDoView>> since we have used our wrapper around each data item. We create a context, with the using keyword to be sure it will be disposed at the end of the operation since it contains a connection to the database that is not a managed object.

As first operation we counts the total rows, then we do some mathematics to compute the pages: the remainder operation % is needed to take into account partially filled pages.In order to reach the request page we use the Skip and Take methods. In the select construct we create also our wrapper and fill it with two copies of the same data item, that will become the previous and actual version of the data item. Please notice also that we transfer data from the ToDo object into a fresh ToDoView object.

The UpdatePage method is a little bit more complex, but quite easy, too:

        public static void UpdatePage(List<Tracker<ToDoView>> items)
        {
            using (SiteDbEntities context = new SiteDbEntities())
            {
                bool aChange = false;
                foreach (Tracker<ToDoView> item in items)
                {
                    if (item.Changed)
                    {
                        if (item.OldValue == null) //insertion
                        {
                            if (item.Value != null)
                            {
                                ToDo curr=new ToDo() 
                                         { Name = item.Value.Name, Description = item.Value.Description, DueDate = item.Value.DueDate };
                                aChange = true;
                                context.ToDo.AddObject(curr);
                            }
                        }
                        else if (item.Value == null) //deletion
                        {
                            ToDo curr=new ToDo() { Name = item.OldValue.Name, Description = item.OldValue.Description, DueDate = item.OldValue.DueDate, id=item.OldValue.id.Value };
                            context.ToDo.Attach(curr);
                            context.ObjectStateManager.ChangeObjectState(curr, System.Data.EntityState.Deleted);
                            aChange = true;
                        }
                        else//update
                        {
                            ToDo curr = new ToDo() { Name = item.Value.Name, Description = item.Value.Description, DueDate = item.Value.DueDate, id=item.Value.id.Value };
                            context.ToDo.Attach(curr);
                            context.ObjectStateManager.ChangeObjectState(curr, System.Data.EntityState.Modified);
                            aChange = true;
                        }
                    }
                }
                if (aChange)
                {
                    try
                    {
                        context.SaveChanges();
                        items.ForEach((item) => { item.Confirm(); });//confirm changes have been passed
                    }
                    catch
                    {
                    }
                }
            }
        }
                    

We have a loop an all modified items where we verify if the item has changed, and if changed we analyze what operation needs to be done on the database, as previously.explained. Please notice the use of the ObjectStateManger to set the correct state of the various objects in the different cases.  The case of the insertion is the only one that doesn’t require to set manually the state of the object.

If at least a data item has changed we do a SubmitChanges() that passes all changes in a single transaction to the database. In  case no exception is fired we call the Confirm() method of the Tracker<T> wrapper  that sets the old version equal to the new version of the data item since all changes has been passed to the database.In case of exceptions an error message should be returned to the controller that need to say to the user to retry the post in a few minutes….however we have not handled this for sake of simplicity(a too complex example doesn’t help the understanding).

Now we can go now to the controller that has just two Action methods, one for handling the initial get and the second for handling the subsequent posts:       

         public const int PageDim=5;//into an actual application this should be put in a config file
        public ActionResult Index()
        {
            int totalPages;
            ToDoViewModel result = new ToDoViewModel()
                {
                    ToDoList = ToDoViewModel.GetToDoPage(PageDim, out totalPages),
                    TotalPages = totalPages,
                    CurrPage=1,
                    PrevPage=1
                };
            return View(result);
        }

        [HttpPost]     
        public ActionResult Index(ToDoViewModel model)
        {
            if (!ModelState.IsValid)
            {
                
                model.CurrPage=model.PrevPage; //cancel possible page change and force correcting errors
                return View(model);
            }
            else
            {
                ToDoViewModel.UpdatePage(model.ToDoList);
                int totalPages;
                ToDoViewModel result = new ToDoViewModel()
                {
                    ToDoList = ToDoViewModel.GetToDoPage(PageDim, out totalPages, model.CurrPage),
                    TotalPages = totalPages,
                    CurrPage = model.CurrPage,
                    PrevPage = model.CurrPage
                };
                return View(result);
            }
        }

 

The first method just displays the first page and it is quite trivial. The second method just controls if there are validation errors. In case there are it cancels a possible page change by resetting the current page to the previous page, and then returns the same View Model it received in order to let the user correct the errors. In case everything is ok it passes the changes to the database, and retrieve the new page requested by the user. That’s all!

We finally arrived to the datagrid. In order to have the datagrid working we need to prepare 4 templates:

  1. ToDoGrid: it displays the general container where all data items will be inserted. In our case it is just a table.
  2. ToDoDisplayItem: it displays a row of data when the grid is in display mode
  3. ToDoEditItem: it displays a row of data when the grid is in edit mode
  4. ToDoInsertItem: it defines the look of the insert new row component, normally it just displays an insert button.

Well, Let go see in detail each single template.

ToDoGrid
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Mvc_Examples.Models.ToDoView>" %>

<table class="ToDo" >

<tr>
<td class="ToDoHeader"><strong>Name</strong></td>
<td class="ToDoHeader"><strong>Due Date</strong></td>
<td class="ToDoHeader"><strong>Description</strong></td>
<td class="ToDoHeader"><strong></strong></td>
<td class="ToDoHeader"><strong></strong></td>
</tr>
<%:ViewData["Content"] as MvcHtmlString %>
</table>

It just displays the <table> tags and the header of the table. The <%:ViewData["Content"] as MvcHtmlString %> construct defines where all data items have to be inserted. It is a kind of placeholder and it needs to be inserted “as it is” in any template that describes a datagrid container.

It is worth to point out that this template is passed an empty data item object to help the automatic construction of the container. Specifically, we can use reflection to extract all columns and we can also automatically decide some facts about the look of the container by extracting the attributes of each data item property (as for instance the display attribute).

ToDoDisplayItem
          <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Mvc_Examples.Models.ToDoView>" %>
          <%@ Import Namespace=" MVCControlsToolkit.Core" %>
          <%@ Import Namespace=" MVCControlsToolkit.Controls" %>
          
                      <td class="ToDo">
                          <%: Html.ValidationMessageFor(m => m.Name, "*")%><%: Model.Name %>
                      </td>
                      <td class="editor-field">
                          <%: Html.ValidationMessageFor(m => m.DueDate, "*")%><%: Model.DueDate.ToString("D")%>
                      </td>
                      <td class="ToDo">
                          <%: Html.ValidationMessageFor(m => m.Description, "*")%><%:  Model.Description %>
                      </td>
                      <td class="ToDoTool">
                          <%: Html.ImgDataButton(DataButtonType.Edit, "../../Content/edit.jpg", null)%>
                      </td>
                      <td class="ToDoTool">
                          <%: Html.ImgDataButton(DataButtonType.Delete, "../../Content/delete.jpg", null)%>

          The display item View Model is just a data item, it is not a Tracker<T> wrapper. The wrapper is handled automatically by the DataGrid.

          We don’t need to put the <tr> tag in each data item: the container tag for each item is defined in the DataGrid helper and it is automatically inserted by the DataGrid. We have also the option to supply a delegate that returns a different item container as a function of the data item and of its position in the DataGrid. 

          The last thing worth to discuss are the two data buttons: the first one switches the row in edit mode, while the second one just delete the row. I used here an image button but there are also link and normal buttons.

          ToDoEditItem
          <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Mvc_Examples.Models.ToDoView>" %>
          <%@ Import Namespace=" MVCControlsToolkit.Core" %>
          <%@ Import Namespace=" MVCControlsToolkit.Controls" %>
                      
                      <td class="ToDo">
                          <%: Html.ValidationMessageFor(m => m.Name, "*")%><%: Html.TextBoxFor(m => m.Name) %>
                      </td>
                      <td class="ToDo">
                          <%: Html.ValidationMessageFor(m => m.DueDate, "*")%><%: Html.DateTimeFor(m => m.DueDate, DateTime.Today).Date()%>
                      </td>
                      <td class="ToDo">
                          <%: Html.ValidationMessageFor(m => m.Description, "*")%><%: Html.TextBoxFor(m => m.Description) %>
                      </td>
                      <td class="ToDoTool" colspan="2">
                          <%: Html.HiddenFor(m => m.id) %>
                          <%: Html.ImgDataButton(DataButtonType.Cancel, "../../Content/undo.jpg", null)%>
                      </td>

          The edit template is completely analogous to the display template. The only difference being that it contains input field to allow user to edit fields.

          Please, notice the Hidden filed containing the key! It is necessary! Here we have a cancel button that undo all changes done to the data item and put the row in display mode.

          Last thing worth to point out is the DateTimeFor helper that is able to take as input, date, time or date and time. It is able to read the DateRange attribute and to enforce its constraints. This means the user is allowed to insert only dates that conforms with the constraints. More information about the DateTimeFor Helper can be found here.

          ToDoInsertItem
          %@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Mvc_Examples.Models.ToDoView>" %>
          <%@ Import Namespace=" MVCControlsToolkit.Core" %>
          <%@ Import Namespace=" MVCControlsToolkit.Controls" %>
          
          <td colspan="5" class="ToDo"><%: Html.ImgDataButton(DataButtonType.Insert, "../../Content/add.jpg", null)%></td>
          The insert template just contains an insert button that when clicked causes a new row to appear in edit mode.
          The Datagrid

          Once we have all templates defined we just need to use the datagrid and pager helpers:

          <div>
              <%:Html.DataGridFor(m => m.ToDoList, ItemContainerType.tr,  "ToDoEditItem",  "ToDoDisplayItem", "ToDoGrid", "ToDoInsertItem")%>
              </div>
              <div class="ToDoPager">
                              <% var pager = Html.PagerFor(m => m.CurrPage, m => m.PrevPage, m => m.TotalPages); %>
                              <%:pager.PageButton("<<", PageButtonType.First, PageButtonStyle.Link) %>
                              <%:pager.PageButton("<", PageButtonType.Previous, PageButtonStyle.Link) %>
                              <%:pager.PageChoice(5) %>
                              <%:pager.PageButton(">", PageButtonType.Next, PageButtonStyle.Link) %>
                              <%:pager.PageButton(">>", PageButtonType.Last, PageButtonStyle.Link) %>
                          </div>
              <div>
              <input type="submit" value="Save" />
              <%:Html.HiddenFor(m => m.TotalPages) %>

          The first argument of the helper is the definition of the property to display in the DataGrid, as usual, the second defines the item container, and the last ones are the names of the templates defined before.

          There are also optional arguments to define html attributes(also as a function of the data item) and to pass a function to change dynamically the item container.

          The pager is composed of various parts that can be also used separately. Here I used link buttons but one can use also image or normal buttons.

          Well we have finished this short adventure,,,,

          Next post will be about the new features of the new release of the datagrid:, sorting, master-detail helpers…….

                                                                  Stay Tuned !

                                                                  Francesco

          For more information or consulences feel free to contact me

          Tags: , , , , , ,