Wednesday, December 17, 2014

Progress bar for long running server calls in ASP.Net MVC

Progress bar for long running server calls in ASP.Net MVC


Last I faced to fix an issue to create client side progress bar for long running server side call. so the way I thought to fix this issue as Producer Consumer Approach. Normally Producer updates the results and Consumer will poll the results and display in a J query progress bar. Then I found the Microsoft SignalR library is the most easiest way to fix this issue. Normally we can use this library to chat applications also.

First need to download SignalR using nuget,
https://www.nuget.org/packages/Microsoft.AspNet.SignalR/2.1.2


Second Step, 

Create folder named as hubs in your project directory. 

Create two classes inside hubs folder, 

Startup.cs
using Owin;
using Microsoft.Owin;
[assembly: OwinStartup(typeof(SignalRChat.Startup))]
namespace SignalRChat
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // Any connection or hub wire up and configuration should go here
            app.MapSignalR();
        }
    }
}
ProgressHub.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using Microsoft.AspNet.SignalR;

namespace RealTimeProgressBar
{
    public class ProgressHub : Hub
    {

        public static void SendMessage(string msg , int count)
        {
            var message = "Process completed for " + msg;
            var hubContext = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
            hubContext.Clients.All.sendMessage(string.Format(message), count);
        }
    }
}

Now in long running controller action, we need to update / send the progress to client action.

In controller,
// assembly
using Microsoft.AspNet.SignalR;
using RealTimeProgressBar;   
//call this method inside your working action
ProgressHub.SendMessage("initializing and preparing",  2);

// View, establishing the connection to server and updating the view
<!--The jQuery library is required and is referenced by default in _Layout.cshtml. -->
<!--Reference the SignalR library. -->
<script src="~/Scripts/jquery.signalR-2.1.2.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="~/signalr/hubs"></script>
<!--SignalR script to update the chat page and send messages.--> 

function StartInvoicing()
{
    var progressNotifier = $.connection.progressHub;

    // client-side sendMessage function that will be called from the server-side
    progressNotifier.client.sendMessage = function (message, count) {
        // update progress
        UpdateProgress(message, count);
        //alert(message);
    };

    // establish the connection to the server and start server-side operation
    $.connection.hub.start().done(function () {
        // call the method CallLongOperation defined in the Hub
        progressNotifier.server.getCountAndMessage();
    });
}

// Using kendo progress bar to update view

 function UpdateProgress(message, count) {
        var result = $("#result");
        result.html(message);
        $("#progressBar").data("kendoProgressBar").value(count);
    }

Instead of kendo progress bar you can use some jquery progress bars to update this percentage.








Saturday, May 24, 2014

Handling complex view models in Razor in Asp.Net MVC

Sometimes we all face some difficulties while handling some complex view models in mvc. Most of them are happens while passing view model back to controller form razor view. Following example describes some clear information regarding complex model binding

Here is my complex model,

namespace FEWO.Web.Models
{
    public class PriceModel
    {
        public Guid Id { get; set; }
        public Guid SeasonTimesId { get; set; }
        public Guid CategoryId { get; set; }
        public Guid PercentageId { get; set; }
        public Guid PriceTypeId { get; set; }
        public decimal Value { get; set; }

        public virtual Category Category { get; set; }
        public virtual Percentage Percentage { get; set; }
        public virtual Price_Type Price_Type { get; set; }
        public virtual SeasonTime SeasonTime { get; set; }
    }

    public class GroupedPriceModelBySeasonTime
    {
        public string GroupName { get; set; }
        public List<PriceModel> PriceModelList { get; set; }
    }

      public class HolidayComplexPriceModel
    {
        public Guid HolidayComplexId { get; set; }
        public string Year { get; set; }
        public List<GroupedModelBySeasonTime> SeasonTimeGroupedModel { get; set; }
    }
}


Here I want to pass the HolidayComplexPriceModel to the view from action method. In action method i will bind this view model a follows

List<PriceModel> priceViewModel  = _repo.GetPriceModelList();
List<GroupedModelBySeasonTime> gmodel = new List<GroupedModelBySeasonTime>();
foreach (var item in priceViewModel.GroupBy(con => con.SeasonTimesId).ToList())
   {
        GroupedModelBySeasonTime  m = new GroupedModelBySeasonTime();
         m.GroupName = item.ElementAt(0).SeasonTime.Season_Shortcut
         List<PriceModel> pmodel = new List<PriceModel>();
                foreach (var items in item.OrderBy(con => con.Category.Category_Name))
                 {
                    pmodel.Add(items);
                  }
          m.PriceModelList = pmodel;
          gmodel.Add(m);
    }

return new HolidayComplexPriceModel(){ SeasonTimeGroupedModel  = gmodel }

Following are the important tips, what are the usual mistakes we might do in razor,

1. Dont use any foreach loop to bind the view model, use for loop

    Ex -
 @using (Html.BeginForm("Index", "Price", FormMethod.Post, new { enctype = "multipart/form-data",
  id = "SavePrice" }))
   {
    foreach(var item in Model.SeasonTimeGroupedModel) {}
   // if you use foreach then you might bind element like  item.Id, then razor viewmodel didnot realize the  model    property. So need to bind like this, Model.SeasonTimeGroupedModel[0].Id

    Use simple for loop like below,
  for (int i = 0; i < Model.SeasonTimeGroupedModel.Count; i++) {}
  }

2. Dont do any grouping in view, do the gruoping in controller action. If grouping happens in view,
    ex- var groupList = Model.ExampleList.Group(x => x.Id);  then you need to bind elements like below
    @Html.TextBoxFor(model => groupList.Name) then Razor didnot realize the model property.

3. Using nested for loops,
     @for (int i = 0; i < Model.SeasonTimeGroupedModel.Count; i++)
        {
                  Model.SeasonTimeGroupedModel[i].GroupName
                  for (int e = 0; e < Model.SeasonTimeGroupedModel[i].PriceModelList.Count; e++)
                   {
                    Html.TextBoxFor(model => Model.SeasonTimeGroupedModel[i].PriceModelList[e].Value)
                   }
        }

4. Dont use any action link to post complex view model. It is clear that action link wont post complex      model. if you want to use multiple post in single view then use multiple submits
 Example
  <input type="submit"  name="submitbutton1" value="submit1" />
  <input type="submit"  name="submitbutton2" value="submit2" />

   In controller action differentiate both by
   if (Request.Form["submitbutton1"] != null)
    {
     // Submit button 1 function goes in here
    }
    if (Request.Form["submitbutton2"] != null)
    {
     // Submit button 2 function goes in here
    }

Cool. Those are some few tips from me to keep coding happily, Enjoy :)