DarioSantarelli.Blog(this);

Archive for the ‘ASP.NET MVC’ Category

ASP.NET SignalR and Ext.NET MessageBus

Posted by dariosantarelli on October 20, 2013


Most of the modern web applications provides real-time functionalities (“real-time web“) through a set of technologies and practices that enable users to receive information as soon as it is published by its authors, rather than requiring that they or their software check a source periodically for updates.  Moreover, in very scalable and complex architectures, server-side code execution is often asynchronous. Just for example, let’s think to a task-based UI which submits a command like “book a plane ticket” to a web service. The server-side command processing could be performed after some hours: for example, the command could be just enqueued to a command-bus to be processed later. In scenarios like that, the client can’t count on an updated read model just after sending the command. As a consequence, in order to receive a feedback as soon as possible, all the involved clients should poll the server until the command execution reaches a significant state (e.g. in progress, completed, canceled etc.) and the read model is updated, ready for queries.

Before WebSockets, the classic implementations of this kind of real-time features were not so easy and they used to adopt strategies like forever frame (see “Comet“) or periodic/long polling. Today, all the modern browsers and web servers fully support WebSockets and they can extabilish bi-directional and persistent communications, so that a client can receive content through a “push” action performed by the server. In the ASP.NET world, SignalR is a growing new library that uses WebSockets under the covers when it’s available, and gracefully fallbacks to other techniques and technologies when it isn’t, while the application code stays the same. SignalR also provides a very simple, high-level API for doing server-to-client RPC (call JavaScript functions in clients’ browsers from server-side code) in an ASP.NET application, as well as adding useful hooks for connection management, e.g. connect/disconnect events, grouping connections, authorization.

Developers that are currently using the Ext.NET component framework can take advantage on SignalR by combining it with the Ext.NET MessageBus component. The MessageBus provides a simple and robust client-side infrastructure to propagate notifications to listening UI components. The reference scenario I’m talking about in this post is represented in the figure below:


1. The client browser extabilishes a persistent connection to a server-side SignalR application hub. After that the client mantains a reference to an auto-generated hub proxy.
2. The Ext.NET UI components submits commands to the server.
3. At any time of the server-side command execution, the server can use the SignalR hub to push notification messages back to all the involved clients via RPC.
4. Any client receiving a SignalR message through the hub proxy redirects the message to the Ext.NET Message Bus
5. On the basis of the specific type of message, the Ext.NET UI components are updated through a message handler function. In fact, each Ext.NET component has a MessageBusListeners property (client side handlers of MessageBus client side events) and a MessageBusDirectEvents property (server side handlers of MessageBus client side events).

Let’s have a look to a minimalistic example implemented in an ASP.NET MVC web application. Here’s the view :

@using Ext.Net;
@using Ext.Net.MVC;
@{
    Layout = null;
    var X = Html.X();
}
<!DOCTYPE html>
<html>
    <head>        
        <title>SignalR and Ext.NET MessageBus example</title>
        @Scripts.Render("~/bundles/modernizr")
        @Scripts.Render("~/bundles/jquery")
        @Scripts.Render("~/bundles/jquery.signalR")
        <script src="@Url.Content("~/signalr/hubs")" type="text/javascript"></script>
    </head>
<body>
@(X.ResourceManager())
@(X.Viewport().Padding(20).Items()

.Add(X.Button().Icon(Icon.Add).Text("New customer")
      .DirectClickAction("AddCustomer", "Customers")) 

.Add(X.Button().Icon(Icon.Delete).Text("Delete customer")
      .DirectClickAction("DeleteCustomer", "Customers"))

.Add(X.GridPanel().Title("Customers").MarginSpec("10 0 0 0").Icon(Icon.User)
      .Store(X.Store()
              .AutoLoad(true)
              .Proxy(X.AjaxProxy().Url("/customers/all").Reader(X.JsonReader()))
              .Model(X.Model()
                      .Fields(fields =>
                      {
                        fields.Add(X.ModelField().Name("Id"));
                        fields.Add(X.ModelField().Name("Name"));
                        fields.Add(X.ModelField().Name("Surname"));
                        fields.Add(X.ModelField().Name("Email"));
                      })))
              .ColumnModel(columnModel =>
              {
                columnModel.Columns.Add(X.Column().Text("Name").DataIndex("Name").Flex(1));
                columnModel.Columns.Add(X.Column().Text("Surname").DataIndex("Surname").Flex(1));
                columnModel.Columns.Add(X.Column().Text("Email").DataIndex("Email").Flex(1));
              })
              .MessageBusListeners(new MessageBusListener 
              {   
                 Name = "Customers.*",
                 Handler = "this.getStore().reload();",
                 Buffer = 500
              }))

.Add(X.GridPanel().Title("Events log").MarginSpec("10 0 0 0").Icon(Icon.Report)
      .Store(X.Store()
      .Model(X.Model()
              .Fields(fields =>
                          {
                              fields.Add(X.ModelField().Name("EventId"));
                              fields.Add(X.ModelField().Name("DateTime"));
                              fields.Add(X.ModelField().Name("Name"));
                              fields.Add(X.ModelField().Name("Data"));
                          })))
                    .ColumnModel(columnModel =>
                    {
                        columnModel.Columns.Add(X.Column().Text("EventId").DataIndex("EventId").Flex(1));
                        columnModel.Columns.Add(X.Column().Text("DateTime").DataIndex("DateTime").Flex(1));
                        columnModel.Columns.Add(X.Column().Text("Event name").DataIndex("Name").Flex(1));
                        columnModel.Columns.Add(X.Column().Text("Event data").DataIndex("Data").Flex(1));
                    })
                    .MessageBusListeners(new MessageBusListener
                    {
                        Name = "Customers.*",
                        Handler = "this.getStore().add({ EventId: data.Id, DateTime: getFormattedDateTime(), 
                                   Name: name, Data: \"CustomerId: \" + data.CustomerId });"
})))
<script type="text/javascript">      
var loadHub = function () {
    var hubProxy = $.connection.applicationHub;
    hubProxy.client.publish = function (name, message) {
        Ext.net.Bus.publish(name, message);
    };
    $.connection.hub.start().done(function () {
        Ext.net.Notification.show({
            title: "Info",
            iconCls: "#Accept",
            html: "SignalR connection is active!"
        });
    });
};

$(document).ready(function () {
    loadHub();
});

</script>    
</body>
</html>

As you can see, the view is composed by the following Ext.NET components:

– A couple of buttons which send commands to the server (e.g. Add/Remove a customer)
– A grid panel which holds the current customer data
– A grid panel which holds trace data about messages received through the SignalR connection.

The integration between the client-side SignalR hub proxy and the Ext.NET components MessageBus is done through the loadHub javascript function: it wraps the SignalR hub named “applicationHub” so that all the received messages are redirected to the Ext.NET MessageBus and again to the listening UI components. Please note that the SignalR “publish” function and the Ext.NET MessageBus “publish” function accept the same parameters: the message name and the message data. For this reason, the integration between the two worlds is practically natural.

In the example above, the Store of the former GridPanel is reloaded each time its MessageBusListener intercepts any message whose name starts with the prefix “Customers.” . Please pay attention to the Buffer property: it’s very useful when the component is under message storming and we want the UI to be refreshed just after a specified delay during which no messages have been received.

What about server-side code? Well, the server-side code is not so relevant in this post. The most important thing to be considered here is that at some point of the server-side command execution, the code retrieves the SignalR hub, selects which clients will receive the RPC (for simplicity, in this example a message is sent to all connected clients) and finally pushes a message containing the data the client needs for updating UI. Here an example:

GlobalHost.ConnectionManager.GetHubContext<ApplicationHub>()
          .Clients.All.publish("Customers.Added", new CustomerAdded { ...<data for clients>... });

Useful links

Advertisements

Posted in .NET Framework, AJAX, ASP.NET, ASP.NET MVC, C#, Microsoft Technology, Programming, Web Development | Tagged: , , , , , , , | 1 Comment »

Experimenting with Ext.NET support for Razor: a GridPanel with AJAX proxy

Posted by dariosantarelli on June 2, 2012


In these days I’m having fun experimenting the Razor View Engine support introduced in Ext.NET v.2.0 (Beta3 at the time of writing) and I’m appreciating how fluently you can configure any Ext.NET component. Nowadays there isn’t a good documentation about these new features (on Ext.NET official site you can read “Server-side Ext.NET API documentation is being worked on”) so if you want more info about Razor support please have a look at the Ext.NET official forum (I’ve found this thread particularly helpful!). Moreover, in the Ext.NET Examples Explorer you can try to get the necessary knowledge for translating WebForms code examples into Razor views.
In this post I’d like to show one of my first tests: a simple GridPanel supporting server-side data paging via AJAX proxy.
Here the interesting Razor syntax:

@Html.X().ResourceManager()
@(Html.X().GridPanel()
        .Title("Customers")
        .Store(store => store.Add(Html.X().Store()
            .AutoLoad(true)
            .RemoteSort(false)
            .IsPagingStore(true)
            .RemotePaging(true)
            .PageSize(20)
            .Proxy(proxy => proxy.Add(Html.X().AjaxProxy()
                            .Url("/Customers/GetCustomers")
                            .Reader(reader => reader.Add(Html.X().JsonReader()
                                                                   .Root("data")
                                                                   .TotalProperty("total")
                                                                   .IDProperty("CustomerID")))))
            .Model(model => model.Add(Html.X().Model()
                        .Fields(fields =>
                        {
                            fields.Add(Html.X().ModelField().Name("CustomerID"));
                            fields.Add(Html.X().ModelField().Name("ContactName"));
                            fields.Add(Html.X().ModelField().Name("CompanyName"));
                            fields.Add(Html.X().ModelField().Name("Phone"));
                            fields.Add(Html.X().ModelField().Name("Fax"));
                            fields.Add(Html.X().ModelField().Name("Region"));
                        })))))
        .ColumnModel(columnModel =>
        {
            columnModel.Columns.Add(Html.X().Column().Text("ContactName").DataIndex("ContactName").Flex(1));
            columnModel.Columns.Add(Html.X().Column().Text("CompanyName").DataIndex("CompanyName").Flex(1));
            columnModel.Columns.Add(Html.X().Column().Text("Phone").DataIndex("Phone"));
            columnModel.Columns.Add(Html.X().Column().Text("Fax").DataIndex("Fax"));
            columnModel.Columns.Add(Html.X().Column().Text("Region").DataIndex("Region"));
        })
        .BottomBar(bar => bar.Add(Html.X().PagingToolbar()
                                          .DisplayInfo(true)
                                          .DisplayMsg("Displaying customers {0} - {1} of {2}")))
        .View(view => view.Add(Html.X().GridView())))

 

First of all, please note the Html.X() helper method. This is the entry point for configuring any Ext.NET component. As requisite for getting started, you can call the Html.X().ResourceManager() helper method. Like the counterpart of Ext.NET for WebForms, it automatically injects every script and stylesheet you need into your page. The output should be something like this:

<link type="text/css" rel="stylesheet" href="/extjs/resources/css/ext-all-gray-embedded-css/ext.axd?v=25767" />   
<link type="text/css" rel="stylesheet" href="/extnet/resources/extnet-all-embedded-css/ext.axd?v=25767" />
<script type="text/javascript" src="/extjs/ext-all-js/ext.axd?v=25767"></script>
<script type="text/javascript" src="/extnet/extnet-all-js/ext.axd?v=25767"></script> 

OK, now through the Html.X() helper you can start configuring the GridPanel. In my example I have an AjaxProxy which calls a controller action in order to get back some JSON data to bind to the GridPanel. Some point of interest:

  • start and limit are the two standard ExtJs querystring parameters sent by the proxy to the remote data source in order to tell it how to page data.
  • AjaxProxy can process the resulting JSON via a JsonReader. In particular, pay attention to the Root() and TotalProperty() methods. They tell the reader respectively which root property in the JSON response contains the data rows and which property contains the total results count. They are two fundamental info for the right grid rendering.

Finally, the controller action code:

public ActionResult GetCustomers(int start, int limit)
{
    IEnumerable dtoArray;
    int total;

    using (var unitOfWork = _unitOfWorkFactory.Create())
    {
        var customerRepository = _repositoryFactory.CreateCustomerRepository(unitOfWork);
        var customers = customerRepository.Query()
                                          .Skip(start)
                                          .Take(limit);

         dtoArray = (from customer in customers
                        select new
                                {
                                    customer.CustomerID,
                                    customer.CompanyName,
                                    customer.ContactName,
                                    customer.Phone,
                                    customer.Fax,
                                    customer.Region
                                }).ToArray();

         total = customerRepository.Query().Count();
    }

    return Json(new { data = dtoArray, total = total }, JsonRequestBehavior.AllowGet);
}

 

Posted in AJAX, ASP.NET, ASP.NET MVC, C#, Programming, Web Development | 8 Comments »

[ASP.NET MVC 2] Splitting DateTime in drop-down lists and model binding

Posted by dariosantarelli on December 26, 2010


OK this is not the classic DateTime picker bound to a textbox… with a jQuery calendar ;).
If you need a custom datetime editor template that splits the datetime parts in drop-down lists like this…

<%= Html.EditorFor(model => model.BirthDate, “Date”) %>

…or like this…

 

<%= Html.EditorFor(model => model.EventDateTime, “DateTime”) %>

…then this post may help you. As you should know, in ASP.NET MVC 2 the default model binder has some difficulties to combine splitted datetime parts in the View. So, if you need to define a DateTime property in your model and make a custom editor template that splits the DateTime parts in different controls (e.g. TextBox and/or DropDownList), first you should read this smart solution by Scott Hanselman. The idea is to separate the way we render the month field, the day field, the year field etc. from the mechanism that will assemble them back in a DateTime structure for model binding.
Starting from the Global.asax, the first thing to do is to register the Scott’s Custom Model Binder and then specify all the available options (the strings there are the suffixes of the fields in your View that will be holding the Date, the Time, the Day etc.)

ModelBinders.Binders[typeof(DateTime)]  = new DateTimeModelBinder()
{
  Date = "Date", // Date parts are not splitted in the View
                 // (e.g. the whole date is held by a TextBox  with id “xxx_Date”)
  Time = "Time", // Time parts are not  splitted in the View
                 // (e.g. the whole time  is held by a TextBox with id “xxx_Time”)
  Day = "Day",  
  Month = "Month",
  Year = "Year",
  Hour = "Hour",
  Minute = "Minute",
  Second = "Second"
};


Now, let’s have a look to template editors. In Views\Shared\EditorTemplates directory we can put two simple templates: Date.ascx and DateTime.ascx. The former renders only the drop-down lists for the date part of the DateTime structure (Month, Day, Year), while the latter renders the time part too. Here the code for Date.ascx:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.DateTime>" %>
<%@ Import Namespace="System.Threading" %>

<%= Html.DropDownListFor(dateTime => dateTime.Month, Enumerable.Range(1, 12).Select(i => new SelectListItem                          
{                              
  Value = i.ToString(),                              
  Text = Thread.CurrentThread.CurrentUICulture.DateTimeFormat.GetMonthName(i),                             
  Selected = (i == Model.Month && Model != DateTime.MinValue && Model != DateTime.MaxValue)                         
}),"-- Month --")%>  /

<%= Html.DropDownListFor(dateTime => dateTime.Day, Enumerable.Range(1, 31).Select(i => new SelectListItem                          
{                              
  Value = i.ToString(),                              
  Text = i.ToString(),                             
  Selected = (i == Model.Day && Model != DateTime.MinValue && Model != DateTime.MaxValue)                         
}), "-- Day --")%> /

<%= Html.DropDownListFor(dateTime => dateTime.Year, Enumerable.Range(DateTime.Now.Year-110, 110).Select(i => new SelectListItem                           
{                                                             
  Value = i.ToString(),                               
  Text = i.ToString(),                              
  Selected = (i == Model.Year && Model != DateTime.MinValue && Model != DateTime.MaxValue)                          
}), "-- Year --")%>

<%= Html.HiddenFor(dateTime => dateTime.Hour)%>
<%= Html.HiddenFor(dateTime => dateTime.Minute)%>
<%= Html.HiddenFor(dateTime => dateTime.Second)%>

That’s all!
Note that in the template editor above, the Hour, Minute and Second parts are rendered as HTML hidden fileds, because the Scott’s DateTimeModelBinder configured in the Global.asax expects a value for all the six parts of the splitted DateTime structure. It’s just a clean workaround to make the Scott’s model binder work without any change to the original code. In a real implementation hidden fields should be not required ;).

Now, what about validation? Well, both client-side and server-side validations are quite trivial: the server-side validation can be obtained through a custom ValidationAttribute that checks if the DateTime value is correct (e.g. the value should be not equal to DateTime.MinValue or DateTime.MaxValue).

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class DateRequiredAttribute : ValidationAttribute
{       
   public DateRequiredAttribute() : base() { }

   public override string FormatErrorMessage(string name)
   {
     return string.Format(CultureInfo.CurrentUICulture, ErrorMessageString, name);
   }
  public override bool IsValid(object value)
   {
     DateTime dateTime = (DateTime)value;
     return (dateTime != DateTime.MinValue && dateTime != DateTime.MaxValue);           
   }
}

The corresponding client-side validation adapter can be implemented by deriving the DataAnnotationsModelValidator class. It allows us to specify a remote validation rule from the client. In this scenario, the part of the DateTime structure that could be validated is the Date part.
So, we can create a SplittedDateRequiredValidator in order to check if each drop-down is holding a valid value. To accomplish this requirement, a simple solution is to make the client-side validator aware of the IDs of the <select> elements holding the DateTime’s Month, Day and Year values.

public sealed class SplittedDateRequiredValidator : DataAnnotationsModelValidator<DateRequiredAttribute>
{
   private string _message;
   private string _dayField;
   private string _monthField;
   private string _yearField;

   public SplittedDateRequiredValidator(ModelMetadata metadata, ControllerContext context, DateRequiredAttribute attribute)
                                        : base(metadata, context, attribute)
   {
      _message = attribute.ErrorMessage;
       _dayField = metadata.PropertyName + "_Day";
       _monthField = metadata.PropertyName + "_Month";
       _yearField = metadata.PropertyName + "_Year";           
   }

   public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
   {
      ModelClientValidationRule rule = new ModelClientValidationRule
      {
         ErrorMessage = _message,
         ValidationType = "splittedDateRequiredValidator"               
      };

      rule.ValidationParameters.Add("dayFieldId", _dayField);
      rule.ValidationParameters.Add("monthFieldId", _monthField);
      rule.ValidationParameters.Add("yearFieldId", _yearField);

      return new[] { rule };
   }
}

Before looking at javascript validator code, let’s register the SplittedDateRequiredValidator as the client-side validation adapter for all model properties decorated with the DateRequiredAttribute. To accomplish that, we have to put the following line of code in the Global.asax…

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(DateRequiredAttribute), typeof(SplittedDateRequiredValidator));

 

Finally, the client-side validator will evaluate the selected index of the drop-down lists in order to ensure that the user has selected a valid date (note that the isValidDate function simply checks if the users has specified an existing date).

Sys.Mvc.ValidatorRegistry.validators.splittedDateRequiredValidator = function (rule) {        
  var dayFieldId = rule.ValidationParameters.dayFieldId;    
  var monthFieldId = rule.ValidationParameters.monthFieldId;    
  var yearFieldId = rule.ValidationParameters.yearFieldId;    
  return function (value, context) {                
    var dayIdx = $get(dayFieldId).selectedIndex;        
    var monthIdx = $get(monthFieldId).selectedIndex;        
    var yearIdx = $get(yearFieldId).selectedIndex;        
    if (dayIdx === 0 || monthIdx === 0 || yearIdx === 0) return false;        
    else return isValidDate(parseInt($get(yearFieldId).value), monthIdx, dayIdx);     
  };
};


function
isValidDate(y, m, d) {
var date = new
Date(y, m – 1, d);
var convertedDate = “”
+ date.getFullYear() + (date.getMonth() + 1) + date.getDate();
var givenDate = “”
+ y + m + d;
return (givenDate == convertedDate);
}

Ok let’s put everything together!
Assuming that our model defines a property ”BirthDate” like this…

[DateRequired(ErrorMessage = “Invalid date. Please specify valid values!”)]
[DataType(DataType.Date)]
[
DisplayName(“Birthdate”
)]
public DateTime BirthDate { get; set; }

… if we put the following code in our View…

<% Html.EnableClientValidation(); %>


<%= Html.EditorFor(m => m.BirthDate, “Date”) %><br />
<%= Html.ValidationMessageFor(m => m.BirthDate)
%>

…the output would be, for example, the following…

HTH

Posted in ASP.NET MVC | Tagged: , , | 11 Comments »