Friday, July 19, 2019

ASP.NET Web API Caching

In this blog, we will discuss about the memory caching and how it improve the overall performance of Web API. Cached data save database call or external call to process the future request.



Web API doesn’t support the output caching and we have to store the data in local memory or in database.

Here is code sample to implement memory caching in web api.

Microsoft provides the System.Runtime.Caching library for memory caching.

Add a reference above lib and here is a Helper Class to store data or get data from cached memory

using System;
using System.Runtime.Caching;

public static class MemoryCacher
{
    public static object GetValue(string key)
    {
        MemoryCache memoryCache = MemoryCache.Default;
        return memoryCache.Get(key);
    }

    public static bool Add(string key, object value, DateTimeOffset absExpiration)
    {
        MemoryCache memoryCache = MemoryCache.Default;
        return memoryCache.Add(key, value, absExpiration);
    }

    public static void Delete(string key)
    {
        MemoryCache memoryCache = MemoryCache.Default;
        if (memoryCache.Contains(key))
        {


            memoryCache.Remove(key);
        }
    }
}

Add data in cache memory:
MemoryCacher.Add(“Key”,Object, DateTimeOffset.UtcNow.AddYears(1))

Get data from cache memory:
MemoryCacher.Get(“Key”)

ASP.NET Web API provides the filters, that you can use to add extra logic before or after action executes, so above caching data logic you can use inside filter to cache web api response.

here is example, how to create action filter that cache web api response.

  public class WebAPICacheAttribute : ActionFilterAttribute
    {
        public int Duration { getset; }
        private bool CacheEnabled = false;

      public WebAPICacheAttribute(int _duration, bool _cacheEnabled)
        {
            Duration = _duration;
            CacheEnabled = _cacheEnabled;
        }

        public override void OnActionExecuting(HttpActionContext context)
        {
            if (CacheEnabled)
            {
                if (context != null)
                {                   
                        //generate cache key from HTTP request URI and Header
                        string _cachekey = string.Join(":"new string[]
                        {
                            context.Request.RequestUri.OriginalString,
                            context.Request.Headers.Accept.FirstOrDefault().ToString(),
                        });                      

                        // Check Key exists
                        if (MemoryCacher.Contains(_cachekey))
                        {    

                            var val = (string)MemoryCacher.GetValue(_cachekey);
                            if (val != null)
                            {
                                context.Response = context.Request.CreateResponse();
                                context.Response.Content = new StringContent(val);
                                var contenttype = (MediaTypeHeaderValue)MemoryCacher.GetValue(_cachekey +
                            ":response-ct");
                                if (contenttype == null)
                                    contenttype = new MediaTypeHeaderValue(_cachekey.Split(':')[1]);
                                context.Response.Content.Headers.ContentType = contenttype;
                                return;
                            }
                        }                    
                }             
            }
        }


        public override void OnActionExecuted(HttpActionExecutedContext context)
        {
          
                if (CacheEnabled)
                {
                    if (WebApiCache != null)
                    {
                        string _cachekey = string.Join(":"new string[]
                        {
                            context.Request.RequestUri.OriginalString,
                            context.Request.Headers.Accept.FirstOrDefault().ToString(),
                        });

                    if (context.Response != null && context.Response.Content != null)
                        {
                           string body = context.Response.Content.ReadAsStringAsync().Result;

                         if (MemoryCacher.Contains(_cachekey))
                            {
                        MemoryCacher.Add(_cachekey, body, DateTime.Now.AddSeconds(Duration));
                                MemoryCacher.Add(_cachekey + ":response-ct",
                                context.Response.Content.Headers.ContentType,
                                DateTime.Now.AddSeconds(_timespan));
                            }
                            else
                            {
                        MemoryCacher.Add(_cachekey, body, DateTime.Now.AddSeconds(Duration));
                                MemoryCacher.Add(_cachekey + ":response-ct",
                                context.Response.Content.Headers.ContentType,
                                DateTime.Now.AddSeconds(Duration));
                            }
                        }
                    }
                }                    
        }

    }




Now You can use WebAPICache action filter on GetProject Action to cache Project data and if the client will send the same request, the Web  API will not call to data repository layer to get project records , it will get from cache
Web API Controller:

public class ProjectApiController : ApiController
{
 [HttpGet]
        [Route("api/Project/{projectId:int}")]
        [WebAPICache(_duration:3600,_cacheEnabled: true)]
        public Project GetProject(int projectId)
        {
            return Repository.GetProperty(projectId);
 }
  }


Other related topics 
  1. ASP.NET Web API Caching
  2. Steps To Improve ASP.NET Web API Performance
  3. 4 Simple Steps To Create ASP.NET Web API
  4. Parameter Binding or Model Binding in ASP.NET Web API
  5. The Life-cycle of an ASP.NET Web API
Thanks for visiting !!

Parameter Binding or Model Binding

The process of setting values to the parameter of  WEB API method is called parameter binding and In parameter binding, it takes an HTTP request and converts it into .Net types so that you can able to set action parameters.

The generic rules or default behavior of WEB API parameter binding: 
·         If parameter is simple .Net primitive types (integer, bool, decimal etc.) it try to get from HTTP Request URI.
·         If parameter is Complex Type, It read the value from http request http request body. 



Here is HTTP Request URI, Which is being used to call to Web API to get project information.


public class ProjectApiController : ApiController
    {

        public ProjectApiController()

        {
        }

       [HttpGet]

        public Project GetProject(int projectId)

        {

            return Repository.GetProperty(projectId);
        }
   }


GetProject is a web api method that has one simple type parameter – (int projectId) and by default, simple type parameter value will be set from URI [?projectId=2] , so its value should be 2 .

By using [FromUri] attribute, the parameter value should be read from Http Request Uri.


       [HttpGet]

        public Project GetProject([FromUri] int projectId)

        {

            return Repository.GetProperty(projectId);

        }


By using [FromBody] attribute, the parameter value should read from Http Request Body.

       [HttpGet]

        public Project GetProject([FromBody] int projectId)

        {
            return Repository.GetProperty(projectId);             
         }


By default, the value of complex type of parameter should read from Body but you can set value from Uri by using [FromUri] attribute 



SearchParameter as complex type: 
  
public class SearchParameter
    {

        public int PageNumber { getset; }

        public int PageSize { getset; }

        public string Name { getset; }

        public string Location { getset; }

    }

               [HttpGet]

    public HttpResponseMessage Get([FromUri]SearchParameter filter)

        {

        }




Other related topics:
  1. ASP.NET Web API Caching
  2. Steps To Improve ASP.NET Web API Performance
  3. 4 Simple Steps To Create ASP.NET Web API
  4. Parameter Binding or Model Binding in ASP.NET Web API
  5. The Life-cycle of an ASP.NET Web API
Thanks for visiting!! 

Steps To Improve ASP.NET Web API Performance

ASP.NET Web API is a .net framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices. ASP.NET Web API is an ideal platform for building RESTful applications on the .NET Framework.

In this blog, we will discuss about 4 techniques to quickly improve the ASP.NET Web API performance.

1.     JSON serializer over XML:

JSON serializer use JSON Formatter and by default Web API provides media-type formatters for both JSON and XML.A media type formatter is used to read CLR objects from an HTTP message and write the CLR object in HTTP message body.
JSON serializer is highly recommend and it very faster compare to other Serlializer like XML.

2.     Asynchronous Task Based WebAPI :

     Async Web API action help to increase the number of concurrent HTTP requests Web API can handle. We can implement asynchronous programming for web api controller by using keywords async, Task, await

public class UserController : ApiController
{        public async Task<IHttpActionResult> Read()
{
var data = await _db.UserMaster.ToListAsync();
return Ok(data);
}
}

3.     Cache Data:
Web API  does not provide the output Caching attribute to cache web api response but you can Cache data in memory to process same future request without hitting database and it improve the performance of ASP.NET Web API and Microsoft provides the System.Runtime.Caching library for memory caching.  More information about web api caching

4.     Multi ResultSets:
Web API should return as many objects as you can in one Web API request and combining objects into one aggregate object and It reduce the number of HTTP request to web API

Example : Aggregate Object ‘UserScreenData’

class
 UserScreenData
{
public List<State> States { getset; }
public List<Country> Countries { getset; }
}

           Web API Method to return Aggregate object

public async Task<IHttpActionResult> GetScreenData()
{
UserScreenData screen = new Controllers.UserScreenData();
screen.State = await _db.State.ToListAsync();
screen.Countries = await _db.Country.ToListAsync();
return Ok(data);
}

Other ASP.NET WEB API related topics:
Thanks for visiting!!

Setup Swagger for ASP.Net Web API

this blog demonstrates step by step to add swagger in web api project and will submit  http request GET/POST/PUT via swagger UI. Swagger ...