Monday, 26 October 2015

MVC5 pass DroDownList Selected Item to controller using JavaScript Async



There are many methods where you can pass the selected Item in a Dropdownlist to a controller. Most common methods are Post requests to a controller where you pass the whole forms collection.

In this module , I need to view the Account report before I fill in the form data. So I cant use a HTTP Post here because the form data is not yet ready.

Following is a very simple way of accomplish this using Ajax and JS.


 
 function NavigateToReport() {
            var List = document.getElementById("AgencyId");
            var id = List.options[List.selectedIndex].value;

            var url = '@Url.Action("AgentAccount", "LocalAgencyAdditions", new { id = "__id__"})';
            window.location.href = url.replace('__id__', id);
        }

Just define a new Action Method in the controller.
 
 public async Task AgentAccount(int? id)
        {

            if (id != null)
                return RedirectToAction("AgentAccount", "Report", new { agentId = id, type = 2 });

            return RedirectToAction("Index");

        }

Wednesday, 21 October 2015

CacheCow: Invalidate cache for different resource routing

CacheCow is a great tool we can use In ASP.Net WebApi projects to maintain cache with SQL Server persistence.  You can view how to implement CacheCow with SQL Server in this post.

However, there is a problem in the default implementation of invalidating cache if we have different kind of GET requests.

eg : Assume our GET request is http://myhost/api/customers. So a POST, PUT,PATCH or DELETE requests for same request http://myhost/api/customers would invalidate the cache.

But what if we have different formations of GET requests.

Eg :

GET : http://myhost/api/customers_country/SriLanka

GET : http://myhost/api/customers_city/Colombo

So any update request to  http://myhost/api/customers would not invalidate cache of  aforementioned GET requests.

Following is a very simple way of invalidate cache based on the root routing value.

I'm using hard coded sql here because this is a separate entity to my other business entities.


 
 public class CacheCowHackService : ICacheCowHackService
    {
        public int InvalidateEntityCache(string parentPattern)
        {
            int noOfRowDeleted;
            using (var ctx = new MyContext())
            {
                //Delete command
                noOfRowDeleted = ctx.Database.ExecuteSqlCommand(
                    $"delete from CacheState where RoutePattern LIKE '%{parentPattern}%'");

            }
            return noOfRowDeleted;
        }
    }

What all we need to do is call this method when ever there is an update to a resource.
 
public VehicleViewModel Create(VehicleViewModel model)
        {
            var id = Guid.NewGuid();
            model.Id = id;

            using (var transactionScope = new TransactionScope())
            {
                ......
                var entity = VehicleMap.ModelToEntity(model);
               ....
                _vehicleService.Add(entity);
                entity = _vehicleService.GetSingle(x => x.Id == id);
                model = VehicleMap.EntityToModel(entity);
                CacheCowHack.InvalidateEntityCache(Const.VehicleRoute);
                transactionScope.Complete();
                return model;
            }
        }


Tuesday, 20 October 2015

WebApi Cache using CacheCow and SQL Server persistancy

I wrote how to implement in-memory cache in this post. But this solution is not good if we want to host our Api in a web farm or if we are going to use load balancing. The best approach is using DB persistence to hold the cache.

Fortunately, CacheCow gives us the SQL Server implementation. Following are the configurations we need to made in order to activate this.
Refer following NuGet into your project.

Install-Package CacheCow.Server.EntityTagStore.SqlServer

In the WebApiConfig.cs file add the following code.
 
 var connString = System.Configuration.ConfigurationManager.ConnectionStrings["Your_Conn_Str"].ConnectionString;
 var eTagStore = new SqlServerEntityTagStore(connString);  
 var cacheCowCacheHandler = new CachingHandler(config,eTagStore); 
 cacheCowCacheHandler.AddLastModifiedHeader = false;
 config.MessageHandlers.Add(cacheCowCacheHandler);

Now this code would not run as it is. Because we need to do required changes to the DB as well. The required sql scripts are getting download along with the NuGet package. Execute that script against your DB. It would create a table CacheState and other relevant procedures.

Now the CacheCow DB persistence is done.Lets shoot some Web API and requests and test results.

When we are doing our initial Get request to a WebApi resource, the fiddler looks like as follows. Check the  request and response headers .


you can clearly see that its responded with status 200 - Ok. Following is the status of the CacheState now.

You can clearly see that the ETag is created and saved in the DB.
If we are going to do the same GET request again, we would get the 304 from server.

You can clearly see that the request been made to the server with the ETag If-None-Match. Server get the ETag and responded with 304 Not Modified. So no need to get the resource back again. ie: Client has the latest version.

Monday, 19 October 2015

Caching the WEBApi REST calls using CacheCow - In memory

Caching is a very important aspect when we are designing  a RESTfull web APIs. The main advantage is we do not need to send the same resource over and over again if the content is not updated.

Traditional way of doing the caching is using ETags and maintain these ETags manually in both server and client end. ETag(Entity Tag) is the property which client validates against each request to a resource. Client sends the  request with the If-None-Match: "ETag#". If the ETag not updated , the server returns the 304 - Not Modified . ie : Client has the latest version of the resource requested.

Installing and configuring the Cachecow is simple. All we need to do it install the nuget package to the WebApi project.

Install-Package CacheCow.Server -Version 0.4.12

Add the following code to the WebApiConfig.cs file.
 
var cacheCowCacheHandler = new CacheCow.Server.CachingHandler();
config.MessageHandlers.Add(cacheCowCacheHandler);

This would create an in-memory cache for requested resources (Weak resources which consist the 'W/' prefix in the ETag ). This is Ok for a single server application where there are no any load balances or web farms. I will tell you how to configure persistence caching using CacheCow in my next post.

Thursday, 15 October 2015

WebAPI Data call with Pagination

Most of the time when we are calling for a data API call, we fetch all records and do the pagination in the client side. But this hinders the network performance and lead to a heavy unnecessary data fetch. Hence , it is better to do the pagination in the server side and return only required data to the client.

Following code demonstrates an easy way to achieve this.

1. The WebAPI controller code
 
[HttpGet, Route("ByClient/{clientID:int}")]
public IHttpActionResult GetByClient(int clientID, int pageSize=5, int page=1 ,string sort= "id")
{
      try
      {
          object header;
          var receivedModel = VehicleProvider.GetByClient(clientID, pageSize , page,  sort, out header);

          HttpContext.Current.Response.Headers.Add("Pagination",
              Newtonsoft.Json.JsonConvert.SerializeObject(header));

          IHttpActionResult response = Ok(receivedModel);
          return response;
     }
     catch (Exception)
     {
          return InternalServerError();
     }
           
}

The actual pagination is going inside the BL's GetByClient method. In this code we are adding a response header attribute as "Pagination". This information is useful to client in order to determine the current data location and the total size.
2. The BL Code
 
public IQueryable GetByClient(int clientID, int pageSize, int page, string sort, out object header)
        {
            var totalCount = _vehicleService.Count();
            var totalPages = (int)Math.Ceiling((double)totalCount / pageSize);


            var paginationHeader = new
            {
                currentPage = page,
                pageSize = pageSize,
                totalCount = totalCount,
                totalPages = totalPages
            };

            header = paginationHeader;

            var results = VehicleMap.EntityToModelQueryble( _vehicleService.GetAll(r => r.ClientId == clientID));
            return results.ApplySort(sort).Skip(pageSize*(page - 1))
                .Take(pageSize);
        }

In this code , we are creating a new object to pass as the header.

Wednesday, 14 October 2015

Dynamic Sorting for WebAPI Get method

We can sort results which returns from a WebAPI Get method using Linq Dynamics as follows.

First of all we need to create an extension method for IQueryable<T>. This method would perform required sorting. Following is the code.


 
public static class IQueryableExtensions
    {
        public static IQueryable<T> ApplySort<T>(this IQueryable<T> source, string sort)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            if (sort == null)
            {
                return source;
            }

           var lstSort = sort.Split(',');

      
            string completeSortExpression = "";
            foreach (var sortOption in lstSort)
            {
           
                if (sortOption.StartsWith("-"))
                {
                    completeSortExpression = completeSortExpression + sortOption.Remove(0, 1) + " descending,";
                }
                else
                {
                    completeSortExpression = completeSortExpression + sortOption + ",";
                }

            }

            if (!string.IsNullOrWhiteSpace(completeSortExpression))
            {
                source = source.OrderBy(completeSortExpression.Remove(completeSortExpression.Count() - 1));
            }

            return source;
        }
    }


We have hard coded the separator and treat the - sign as the descending sorter. source.OrderBy is using the linq dynamics OrderBy method.
Application of the sorting can be done as follows.

 
[HttpGet, Route("ByClient/{clientID:int}")]
public IHttpActionResult GetByClient(int clientID,string sort="id")
{
     try
      {
          var receivedModel = VehicleProvider.GetByClient(clientID);
          IHttpActionResult response = Ok(receivedModel.ApplySort(sort));
          return response;
      }
      catch (Exception)
      {
          return InternalServerError();
      }
     
}


The VehicleProvider.GetByClient method should return an IQueryable<T>. All we need to do is plug the ApplySort method with sort parameters to the result.

[Solved] : HTTP 405 - Method Not Allowed from WebAPI DELETE request

The IIS would return the 405 Error sometimes when we send a DELETE request. This happens mainly because of following two reasons.


  1. The Routing attributes are not matched 
  2. Incorrect web.config where we do not allow DELETE request. 
The first cause is easy to solve. Make sure your routing name s correct and your attributes are also correct. Eg : If the passing id attribute is a guid and if we pass an int , it throws 405.

 
If your Delete method looks like this , make sure you are passing a correct Guid value.

If you having a config issue, make sure that you are having following settings in the web.config correctly.


 
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
       <remove name="FormsAuthentication" />
      <remove name="WebDAVModule"/>
    </modules>
     <httpProtocol>
    <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
    </customHeaders>
    </httpProtocol>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
      
  </handlers>
  </system.webServer>