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

          Oct 21 2010

          …Something More about Ria Services

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

          Silverlight…without Ria Services

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

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

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

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

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

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

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

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

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

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

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

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

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

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

           

                                                                                            Stay Tuned !

                                                                                            Francesco

          For more information or consulences feel free to contact me

          Tags: , ,

          Oct 6 2010

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

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

          Data Validation 1

          Data Validation 2: Enriching WPF with Data Annotations Support

          Something More about Ria Services

          Silverlight…without Ria Services

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

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

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

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

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

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

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

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

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

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

           

          The INotifyDataErrors interface and its advantages on the IDataErrorInfo interface

          The INotifyDataError interface is defined as:

          public interface INotifyDataErrorInfo

          {

                bool HasErrors { get; }

               event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

               IEnumerable GetErrors(string propertyName);

          }

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

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

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

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

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

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

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

                                               Stay Tuned!

                                                Francesco

          For more information or consulences feel free to contact me

          Tags: , , , , , , ,

          Sep 3 2010

          Data Validation 1

          Category: WPF | Silverlight | MVC | Entity Frameworkfrancesco @ 22:48

          Data Validation 2: Enriching WPF with Data Annotations Support

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

          Both Dot Net and Silverlight offer support for data validation through  various “ad hoc” tools. However, the supported tools and the way validation errors are propagated to and handled by the user interface depend on the kind of projects involved in the solution. For instance Silverlight supports automatically the INotifyDataErrors interface that is not available in WPF. It also supports data annotations on the business class but only if RIA services or DataForm control are used. (Don’t worry, I am going to explain all these tools!). Moreover, it is not obvious how to select each time the right tool.

          This is the first of a series of posts where I shall explain:

          1. How these tools work, 
          2. How to use them and the pre-built support offered for them in WPF, Silverlight and MVC, and, finally,
          3. How to use and combine them in complex scenarios.

          The problem solved by the Data Validation tools is the separation between the normal flow of control and the flow of validation errors. This is a labour-saving strategy whose aim is to allow the programmer to concentrate mainly in implementing his business logic. The programmer only needs to define data validation errors and the associated message errors. Then some standard mechanism  automatically propagate them to the UI interface. In this way the plumbing logic to propagate data errors to the UI is implemented once and for all in a standard way and it is not mixed with the code implementing the business logic.  In this series of  posts we shall see how most of this plumbing logic is already taken care of by the pre-built engines used in various type of applications (MVC, WPF, Silverlight, and dynamic data). RIA services also allow this pre-built plumbing logic propagate through the boundaries of web Services. This way data validation constraints that are defined directly on the business classes on the server  can reach the UI of the Silverlight client.

          I summarize what I said in a couple of statements:

          1. Data constraints can be defined in their natural place i.e in the business classes;.
          2. Pre-built plumbing logic allows such a definitions to propagate till the UI with a minimum programming effort.

          Let now move describing the Data Validation tools. They are:

          1. Data Annotations. Attributes that decorate either the members or the classes themselves and that state in a declarative way the constraints imposed either on each single member or on the whole  instantiation of the class. There are standard constraints such as [Range(a, b)] that constraint the member value to be within a and b, but also custom constraints that specify user defined code to be executed to verify the constraints.This is the simplest and more effective way to specify constraints, because the code to handle them is completely separated by the code of the class also in the case of custom constraints and can be reused all over the application. Since constraints are stated in a declarative way they are self-documented and they can be easily changed to fit new specifications.
          2. Interfaces IDataErrorInfo and INotifyDataErrors.  The business class may expose validation errors in a standard way by implementing the members of one of these interfaces. Other modules such as the pre-built error handling UI modules of Silverlight, WPF and MVC can query these interfaces to handle the errors. The disadvantage of this approach is that the implementation of the interfaces and hence the error validation code is mixed with the code handling normal operations of the class. However  it takes advantage of standard plumbing logics to propagate errors in the same way as Data Annotations. The better use of these interfaces is letting error defined with Data Annotations propagate to other modules in case the other modules are not able to handle directly Data Annotations: this way it is possible to build easily the plumbing logics to propagate errors through several layers in complex scenarios. Ria services do this! Errors defined on the server-side business classes are propagated to the UI of the client through a INotifyDataErrors interface implementation on the client-side business class that is generated automatically by Visual Studio. INotifyDataErrors has the advantage of exposing errors through events, specifically,event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged ,  while IDataErrorInfo do the same job with the only help of the indexer: string this[ string memberName] { get; }.The event solution is more attractive because it allows asynchronous data validation, for instance with the help of a web service that performs server-side verifications. When the asynchronous call to the web service returns, the UI is updated by raising the event. Obviously also INotifyDataErrors expose directly the errors through the GetErrors methods, and it has also a Boolean member HasErrors that can be used to enable/disable some UI logics. Unluckily, INotifyDataErrors is only available in Silverlight!
          3. Validation Rules. Validation rules are only available in WPF and are defined on the UI, not on the business classes! Therefore a discourage their use and i will not discuss them further.
          4. Exceptions. Exceptions are not a specific tool to handle data validation but sometimes they are useful to help other tools, such as Data Annotations, to propagate validation errors easily through several layers.

          The table below summarizes the main properties of the above tools, and it is an useful start point in the design of data validation logic:

            WPF
          built-in UI  support
          Silverlight
          built-in UI  support
          MVC
          built-in UI  support
          Namespaces
          IDataErrorInfo yes yes yes System.ComponentModel
          INotifyDataErrors absent in .net yes absent in .net System.ComponentModel
          Data Annotations no only with RIA or data controls yes System.ComponentModel.DataAnnotations
          (need to add reference to assembly with the same name)

          As already hinted INotifyDataErrorInfo is available only in Silverlight, while Data Annotations have only a limited built-in support.Silverlight native binding class offers no support for data annotations but data controls such as the DataForm control know how to use the data annotations of the object that is bound to them. Moreover, Ria services offer support for data annotations because Visual Studio wraps the automatically generated client-side business class within a  INotifyDataErrorInfo interface implementation that extracts validation errors from the data annotations. Ria services are able to perform data validation directly on the client-side if all custom data validation attributes are made available on the client side, otherwise they do the validation with the help of the server-side copy of the business object.

          For what concerns Asp.net applications while the dynamic data engine relies on data annotations for data validations ad for defining the way data are presented to the user, standard Asp.net applications have no built-in mechanism to take advantage of the above described validation techniques. However, It is worth to point out that also in case of lack of built-in support for data annotations it is easy to validate either single members or whole objects by using the static validation methods of the Validator class in the System.ComponentModel.DataAnnotations namespace. Validations errors extracted this way can be made available to the UI by implementing either INotifyDataErrorInfo or IDataErrorInfo, or, in case of Asp.net applications, by a custom presentation logic.

          In the next posts we will see how to use in practice INotifyDataErrorInfo, IDataErrorInfo, and data annotations.

          Stay Tuned!

          Francesco

          Tags: , , , ,