Showing posts with label ASP.NET MVC. Show all posts
Showing posts with label ASP.NET MVC. Show all posts

Nov 10, 2016

Quick look at .NET Core 5


ASP.NET Core is a cross-platform open source framework for building web application

program.cs
This has public Main() function, just like you have in console application.

startup.cs
This has configure method which can be used to configure HTTP request pipeline. You write your application middleware (term commonly used in nodeJS) here in this method, for example you can use "UseMvc" extension method to set MVF as the default handler. The startup class  also has ConfigureServices which is used for dependency injection.

wwwroot
wwwroot is the directory in your project for static resources like css,js and image files. The static file middleware (UseStaticFiles) will only server files from wwwroot directory.

Exception Handline
For exception handling you can use UseDeveloperExceptionPage just for the developer environment, which will give detail call stack for troubleshooting.

Configuration
ASP.NET Core supports configuring application using JSON,XML, INI or even environment variable. You can create configuration builder with multiple sources. This way you can have configuration coming from different sources as well as configuration can be overwritten for different source..

var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();

project.json
The project.json file is used on .NET Core projects to define project metadata, compilation information, and dependencies.

New Features

Tag Helper Tag Helpers are a new feature in MVC that you can use for generating HTML. The syntax looks like HTML (elements and attributes) but is processed by Razor on the server. 

Controller With MVC6, both mvf and web api controller use the same base class. 

Gulp,Grunt,Bower and npm Support for VS





Aug 11, 2014

Asynchronous vs Synchronous Method

ASP.Net processes a request (http) by using a thread from thread pool. The processing thread cannot service any other request if its being processed synchronously. This can eventually result in condition known as thread starvation. After this condition server starts queuing up the request and if the queue becomes full server starts rejecting request with 503 status code - Server Too Busy.

In IIS you can set Threads Per Processor Limit and the Queue Length (IIS->Web Server(Feature View)->ASP)

Example of how you can write async action in MVC/Web api

 public async Task Create(Product product)
 {
                .   
                .
                .
var response = await client.PostAsJsonAsync(uri, product);
                .
                .  
}
In the above example, code will be executed synchronously until it hits await key word. The await keyword registers rest of the method as a callback on the task and then it immediately return. This will free up the processing thread. When the awaited task completes, it will invoke that callback to resume the execution of the method.

This is mostly helpful for I/O bound operations. For I/O intensive activity thread tells kernel what I/O it wants done, after which it continues on, rather than sitting and waiting for response from kernel. When kernel is done with I/O it callback so that rest of the processing can be done.

On the hand if the operation is primarily CPU intensive using asynchronous action will not provide any benefits rather it will make things worse because of its overhead.



http://msdn.microsoft.com/en-us/library/hh873177(v=vs.110).aspx

Aug 9, 2014

Protecting your Mvc page from CSRF

Adding @Html.AntiForgeryToken() to a view does following
 
 sets __RequestVerificationToken Cookie
 add hidden input __RequestVerificationToken to the page

And then you can add attribute ValidateAntiForgeryToken to the HttpPost action which will validate __RequestVerificationToken between cookie and posted hidden input value. This helps defend against cross-site request forgery. If any of the values are missing or the values does not match (it has some encryption logic so if you try to compare, the value it will not looksame), it will throw HttpAntiForgeryException.


With the absence of attribute ValidateAntiForgeryToken, your site can be easily prone to csrf. Refer to following for a quick way to create this condition


<body>
    <form name="badform" method="post" action="http://localhost/product-ui/Product/Create">
        <input type="hidden" name="sku" value="1234" />
        <input type="hidden" name="overview" value="something...." />
    </form>
    <script type="text/javascript">
        document.badform.submit();
    </script>
</body>

Jun 10, 2014

Cookie

Cookie is a small piece of data sent from a server and stored in the browser. Browser sends cookie back with each subsequent request based on set of rules. Few of the common examples

ASP.NET_SessionId is a cookie that ASP.NET uses to store a unique identifier for your session.

   Set-Cookie: ASP.NET_SessionId=huilln20biy333vr3smug2sb; path=/; HttpOnly


This is set only when user tries to store something to the session, for example in asp.net mvc if we add this code, then we should see this cookie.
ControllerContext.HttpContext.Session.Add("a",1);

.ASPXAUTH is a cookie that asp.net uses for form authentication.

     Set-Cookie: .ASPXAUTH=095F6C2AF0126AF84BD5A30AD2866328E06F61755EA6FCDEDAA5A79F9039FB38AC4812628A42C700B7E927B58CA6B50F831DA2143A06385AA422ED313CB39303C3C0DA75DCFE9BCF363B7969FCFC6B0114D362CE6C1A04C424C7B1D46A440170B1DABD47E6DD8C91D6EE64B74F5224B6; path=/; HttpOnly

A web server specifies a cookie to be stored by sending an HTTP header called Set-Cookie. This is how response header looks.

     Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]
     Set-Cookie: MyCompany=SessionKey={some unique id}&UserName=MyName&UserId=MyId; domain=.somewhere.com; path=/

In asp.net mvc you can use following code in order to perform set this header

        var httpCookie = new HttpCookie("MyCompany", "SessionKey=something&UserName=MyName");
        httpCookie.Values["UserId"] = "MyId";
    
 httpCookie.Expires = DateTime.Today.AddDays(1);
 httpCookie.Domain = "bogus.com";
 httpCookie.Path = "/product";
 httpCookie.Secure = true;
 ControllerContext.HttpContext.Response.Cookies.Add(
  httpCookie
  );


Value is string in the format name=value. In the above example we have used subcookies in order to increase the number as there is limitation on number of cookie.

Value is sent to the server with each subsequent request if option allows

     Cookie: MyCompany=SessionKey={some unique id}&UserName=MyName&UserId=MyId;

Each of the options after cookie value are separated by semicolon and space.

Expires option indicates when the cookie expires and should not be sent back to the server. Without the expires option, a cookie has a lifespan of a single session as we saw in case of ASPXAUTH,ASP.NET_SessionId

Domain options indicates domains for which cookie should be sent. By default domain is set to the host name of the page setting the cookie. This is useful for case like mail.somewhere.com and finance.somewhere.com. By setting .somewhere.com cookie can be shared these sets of sites. Browser performs a trail comparison of this value and the host name to which a request is sent.

Path options is another way to control cookie. This comparison is done by comparing the option value character-by-character against the start of the request URL. If the characters match, then the Cookie header is sent.

If secure option is specified then cookie will only be sent to the server when a request is made using SSL and the HTTPS protocol.

May 21, 2014

Browser Cach

Cache-Control
public-Means cached version can be saved by proxy and intermediate servers where evryone can see
private-Only user's private browser can cache it
no-cache-this is useful for cases where url is same but content may change

Max-Age
In specifies the max age (in seconds), until then resource is not considered stale. Until then no need to send request to server. Fiddler should not show any traffic and browser load page from the cache.

Expires
Absolute time until resource is not considered stale. The inclusion of just an Expires header with no Cache-Control header indicates that the content can be cached by both browsers and public/shared caches

Last-Modified
If response contains "Last-Modified" then browser sends following cache header in subsequent request to the same resource. Server can implement logic to verify if the resource has been modified since last modified sent by the browser and if it is not then it can send "304 Not Modified" status with no content.
If-Modified-Since: Mon, 19 May 2014 15:40:42 GMT

ETag
Its like a hash or fingerprint to a resource which server can send and in any subsequent request to the same resource browser can send this value as "If-None-Match". Based on this value server can decide if resource has been modified or not. Typically server will have this ETag and it will verify it with the If-None-Match value and if it is same then "304 Not Modified" can be responded.
If-None-Match: -1265215684

Cache With Refresh
Hitting refresh results in an “If-None-Match” header being sent to the origin web server for all content that is currently on the disk cache, independent of the expiration date of the cached content.

CTRL + Refresh or CTRL +F5 
Hitting CTRL and refresh (in Internet Explorer only) or CTRL and F5 (Internet Explorer and Firefox) will insert a “Cache-Control=no-cache” header in the request, resulting in all of the content being served directly from the origin servers with no content being delivered from the local browser cache. All objects will contain a response code of 200, indicating that all were served directly from the servers.

Browser Setting
Review browser setting to verify when the page is out of date (Every Visit/Never/Once per Session/When the page is out of date). Along with this check settings to enable/disable caching of SSL content.

Sample Cache related Http  Response Headers
Cache-Control: private, max-age=1000 (,s-maxage=0)
Expires: Mon, 19 May 2014 14:42:14 GMT
Last-Modified: Mon, 19 May 2014 15:40:42 GMT
ETag: -1265215684

HTML5 Application Cache
HTML5 provides an application caching mechanism that lets web-based applications run offline. Applications that are cached load and work correctly even if users click the refresh button when they are offline.

Nov 16, 2013

Update Mvc application from 4 to 5

Make sure you have back up of your project before you start updating your project.

Open Manage Nuget Package for the solution
Update Entity Framework
Update Asp.Net Mvc
Update Asp.Net Mvc web api2
Installed Microsoft.AspNet.WebHelpers

The idea is to update only top level package and it automatically updated other packages on which these packages were dependent on.

Update web.config
<add key="webpages:Version" value="3.0.0.0" />

And Update web.config in views folder for mvc version to 5

Compile your project and you should be ready to go at least I was able to update my project following these steps.

Oct 15, 2012

Form Authentication(FormsAuthenticationModule)


Forms authentication cookie contains forms authentication ticket which is passed with each request. In case of cookieless forms authentication, the ticket will be passed in the URL in an encrypted format.

The ticket is encrypted and signed using the <machineKey> configuration element of the server's Machine.config file. If you deploy your application in a Web farm, you must ensure that the configuration files on each server share the same value for validationKey and decryptionKey, which are used for hashing and decryption respectively.

The authentication ticket contains various bits of information, such as the user name, the issue and expiry dates and user data.

Forms authentication tickets can be generated manually by using the FormsAuthenticationTicket class.

FormsAuthentication cookie name is .ASPXAUTH which is by default set and send to the browser once user is authenticated.

.ASPXAUTH=095F6C2AF0126AF84BD5A30AD2866328E06F61755EA6FCDEDAA5A79F9039FB38AC4812628A42C700B7E927B58CA6B50F831DA2143A06385AA422ED313CB39303C3C0DA75DCFE9BCF363B7969FCFC6B0114D362CE6C1A04C424C7B1D46A440170B1DABD47E6DD8C91D6EE64B74F5224B6


In case of non-persistent cookie, if the ticket is expired, cookie will also expire, and the user will be redirected to the logon page. If the ticket is marked as persistent, where the cookie is stored on the client box, browsers can use the same authentication cookie to log on to the Web site any time. FormsAuthentication.SignOut removes the forms-authentication ticket information from the cookie or the URL if CookiesSupported is false.


Jul 9, 2012

Xsl Transformation Custome ActionResult

In this post I am creating a custome action result which takes xml data and uses xsl to transform xml data to output page.

Our custome action result will be derived from abstract class ActionResult and need to implement abstract method ExecuteResult. ControllerActionInvoker calls ExecuteResult on the action result returned by the controller action.

In this method we first find the Xsl based on the ViewName and then transform xml using this xsl to generate response output

 public override void ExecuteResult(ControllerContext context)
 {
  if (context == null)
  {
   throw new ArgumentNullException("context");
  }

  if (string.IsNullOrEmpty(ViewName))
  {
   ViewName = context.RouteData.GetRequiredString("action");
  }

  XslCompiledTransform view = FindView(context, ViewName,true);
  
  var response = context.HttpContext.Response.Output;

  RenderView(response, view);
  
 }

Here are the helper method used for used for this.

Based on the viewName, I find view. I am using "~/Views/{1}/{0}.xsl" as ViewLocationFormat. If required more format can be used similar to View engine.

XslCompiledTransform is being cached for performance.


 private static XslCompiledTransform FindView(ControllerContext context, string viewName, bool useCache)
 {
  string controller = context.RouteData.Values["controller"].ToString();

  string keyPath = Path.Combine(controller, viewName);

  string virtualPath = string.Format("~/Views/{1}/{0}.xsl", viewName, controller);
  string viewPath = HttpContext.Current.Server.MapPath(virtualPath);

  // Try the cache           
  if (useCache)
  {
   ObjectCache cache = GetCache();

   if (cache[keyPath] == null)
   {
    XslCompiledTransform xslt = new XslCompiledTransform();
    xslt.Load(viewPath);
    cache[keyPath] = xslt;    
   }

   return cache[keyPath] as XslCompiledTransform;
  }
  else
  {
   XslCompiledTransform xslt = new XslCompiledTransform();
   xslt.Load(viewPath);
   return xslt;
  }
 }
 
 private void RenderView(TextWriter response, XslCompiledTransform view)
 {
  response.Write(TransformXmltoHtml(PageData, view));
    }

May 24, 2012

Custom Controller Factory

Constructor Injection is the DI technique of passing an object's dependencies to its constructor. But this by default will not work with Mvc unless you have a parameterless constructor also defined. If you try to run, it will throw error stating that “No parameterless constructor is defined for this object”. A way to get around this and have DI is by defining both parameterless constructor as well as one with which you want to have dependency.

public class ProductController : Controller
    {
        #region Constructor
        
        public ProductController()
            : this(new ProductModel())
        {
        }

        public ProductController(IProductModel productModel)
        {
            if (productModel == null)
                throw new ArgumentNullException("productModel");
            ProductModel = productModel;
        }

        private IProductModel ProductModel { get; set; } 

        #endregion

The other way to achieve this is by defining CustomeControllerFactoryWithoutDefaultConstructor something like this

public class CustomeControllerFactoryWithoutDefaultConstructor : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
        {
            return Activator.CreateInstance(controllerType, new ProductModel()) as IController;
        }
    }

And then register this in Global.asax:

ControllerBuilder.Current.SetControllerFactory(typeof(CustomeControllerFactoryWithoutDefaultConstructor));

Feb 13, 2012

From Browser (request) to View (response) - Mvc Under the Hood

In Global.asax.cs we register routes something like this

protected void Application_Start()
 {
  AreaRegistration.RegisterAllAreas();

  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);
 }
 
 public static void RegisterRoutes(RouteCollection routes)
 {
  routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

  routes.MapRoute(
   "Default", // Route name
   "{controller}/{action}/{id}", // URL with parameters
   new { controller = "Employee", action = "CreateEmployee", id = UrlParameter.Optional } // Parameter defaults
  );

 }


routes.MapRoute is extension method defined in System.Web.Mvc.RouteCollectionExtensions.cs. When you decompile this you will notice that how a route with particular URL is being register and associated with MvcRouteHandler.

Route route = new Route(url, new MvcRouteHandler()) { 
  Defaults = new RouteValueDictionary(defaults), 
  Constraints = new RouteValueDictionary(constraints),
  DataTokens = new RouteValueDictionary() 
 };

There are bunch of default HttpModules and HttpHandlers in your IIS configuration. Just take a look here - %WINDOWS%\Microsoft.NET\Framework\v4.0.30319\Config\web.config.

and in the list of modules () notice UrlRoutingModule
Now decompile UrlRoutingModule and look at PostResolveRequestCache where it Matches the HTTP request to a route, retrieves the handler for that route, and sets the handler as the HTTP handler for the current request.

public virtual void PostResolveRequestCache(HttpContextBase context)
 {
  RouteData routeData = this.RouteCollection.GetRouteData(context);
  if (routeData == null)
  {
   return;
  }
  IRouteHandler routeHandler = routeData.RouteHandler;
  if (routeHandler == null)
  {
   throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
  }
  if (routeHandler is StopRoutingHandler)
  {
   return;
  }
  RequestContext requestContext = new RequestContext(context, routeData);
  context.Request.RequestContext = requestContext;
  IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
  if (httpHandler == null)
  {
   throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[]
   {
    routeHandler.GetType()
   }));
  }
  if (!(httpHandler is UrlAuthFailureHandler))
  {
   context.RemapHandler(httpHandler);
   return;
  }
  if (FormsAuthenticationModule.FormsAuthRequired)
  {
   UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
   return;
  }
  throw new HttpException(401, SR.GetString("Assess_Denied_Description3"));
 }

This is how MvcRouteHandler(routeHandler.GetHttpHandler(requestContext)) is used for the Mvc application.  In case of aspx.net, all the handlers are listed in the web.config file, which are based on the file extension. You will noticed that there is non for mvc.

As requests move through the pipeline(modules) it find UrlRouting module (refer to web config). UrlRouting module matches incoming request URL with registered collections of Routes. For found matched Route it gets corresponding route handler (MvcRouteHandler). From routehandler (MvcRouteHandler) it gets the GetHttpHandler which in this case if MvcHandler.

Once the request comes to the MvcHandler.ProcessRequest, it creates controller from the controller factory (DefaultControllerFactory) and then call execute method on the controller which calls controller action.

Execute (member of IController which is implemented in ControllerBase)
   -> ExecuteCore (which is implemented in Controller)
         ->ControllerActionInvoker.InvokeAction
             ->GetParameterValues- This uses model binder to get the parameter value.
             -> Based on filter it calls filter.OnActionExecuting and finally call controller action.

At this point in the lifecycle, we’ve entered our application code. Your controller action return ActionResult.

             ->After view is executed it calls filter.OnActionExecuted
             ->ControllerActionInvoker.InvokeActionResult
                 ->ViewResultBase->ExecuteResult
                 ->FindView (which is implementation of IViewEngine). This returns ViewEngineResult                    which is an implementation of IView
                 ->This then call Render(member of IView) on the View and pass ViewData,tempdata and context.HttpContext.Response.Output writer so that View can process the data and write it to the output.



1. RouteConstraint
The URL Routing framework recognizes two different types of constraints. If you supply string for a constraint then the string is interpreted as a regular expression. The other option is to create a custom constraint by creating an instance of a class that implements the IRouteConstraint interface. Routing framework provides HttpMethodConstraint.

2. RouteHandler

3. ControllerFactory
Mvc handler gets the name of controller (Based on the route) and then uses controller factory (DefaultControllerFactory) to create an instance of controller. The default factory uses reflection to create an instance that implements IController and whose name ends with Controller.

DefaultControllerFactory uses the parameter-less constructor so if you want to use DI it will throw error stating that “No parameterless constructor is defined for this object”. This is one of the case where you can implement a custome controller factory by implementing DefaultControllerFactory and override GetControllerInstance.

4. ActionInvoker- This is the sequence of operation which happens here.

ControllerActionInvoker.InvokeAction
      InvokeAuthorizationFilters
      GetParameterValues
      InvokeActionMethodWithFilters
            OnActionExecuting
            InvokeActionMethod Returns ActionResult
            OnActionExecuted
      InvokeActionResultWithFilters 
            OnResultExecuting
            InvokeActionResult Calls ExecuteResult on ActionResult returned by InvokeActionMethod     
            OnResultExecuted 

5. ActionMethodSelectorAttribute
Actions, with the default action invoker, are selected based on their name.

One of the scenario for a custom selector attribute is if you want to choose action based on the browser like mobile browser or desktop browser.

6. AuthorizationFilter


7. ActionFilter
Action Filters are executed before and after an action is executed.

Filters can be used for many purpose like to extract data from session\httpcontext. This makes controller lighter and any component called from controller free from objects like session data\httpcontext. Having this encapsulated in filter makes it easily reusable across different controller action. The other places where I have used is to manage (set/get) http headers ( like If-Modified-Since and If-None-Match) specifically for Etag to support conditional caching. In short I consider this to be a place for anything which doesnot belongs to model.

8. ModelBinder
The default model binder maps HTTP parameters (by using value providers) to action method parameters using their names. 

For DefaultModelBinder you need to have parameter object with parameterless constructor. Also you cannot use interface type as a parameter. To extend the support for these types of parameter you can extend DefaultModelBinder. Also the other condition you might use it to convert the id of the person directly to the Person object looking up on the database, although I consider this kind of thing to belong to model.

9. ValueProvider
Value providers are used by the model binding system in MVC to populate the values of model objects. MVC 3.0 includes value providers for several common value sources
System.Web.Mvc.FormValueProviderFactory
System.Web.Mvc.HttpFileCollectionValueProviderFactory
System.Web.Mvc.QueryStringValueProviderFactory
System.Web.Mvc.RouteDataValueProviderFactory

For any other value provider like CookieValueProviderFactory we can implement ValueProviderFactory.

10. ControllerBase
All controllers inherit from the base class Controller. To encapsulate common logic or conventions you can have your own base calss which inherits from Controllers. You can also apply filter to this base.

11. ResultFilter
Like action filter result filters are executed before and after action result is executed. ActionFilterAttribute implements both IActionFilter, IResultFilter.

Logging/Output caching from action result can be done at this level.

12. ActionResult
ASP.NET MVC comes with many different kind of results to render views, to render JSON, plain text, files and to redirect to other actions.

You can implement this in case of any specific type of requirement. One of the example where I have tried this is to write Xsl Transformation ActionResult where I pass xml object (same as view model) and based on the action it uses xsl file to transform xml object to output page.

13. ViewEngine
Example Razor(cshtml) and ASPX/WebForm view

One of the example where I create a MobileCapableRazorViewEngine by deriving it from RazorViewEngine and overriding FindView to specify view name based on the browser from where the request is coming.

14. HtmlHelper
HtmlHelper is just an extension method of the HtmlHelper class.Views must be very dumb and thin, and they should only have html markup and calls to HtmlHelpers. Writting helpers come very handy for extracting the code from the view and putting it into something that is testable\reusable.

15. TempDataProvider
Life span of TempData object is only till the subsequent request, or until the item is removed explicitly. This is useful when you want to pass data to another view that you will be redirecting to, rather than rendering to. By default, TempData is stored in the session using SessionStateTempDataProvider.

CustomeTempDataProvider just need to implement ITempDataProvider which has method SaveTempData where you can put your logic for storing this wherever you want.

Jan 18, 2012

Mobile Capable View Engine- Custome View Engine

One of the nice feature of ASP .NET MVC framework is its Extensibility. I created a custom view engine which changes view location based on the device from where its being accessed.

In the following code I update view name with mobile specif view, if the request is being originating from mobile devise

 public class MobileCapableRazorViewEngine : RazorViewEngine
    {
        public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            if(IsMobileDevice(controllerContext))
   {
    viewName = viewName + ".mobile";
    masterName = masterName + ".mobile"
   }
        }
 }
 
 private static bool IsMobileDevice(ControllerContext controllerContext)
 {
  //ASP.NET’s built-in browser detection support may not be sufficient for all applications specifically in case of latest devices
  //add the open source 51Degrees.mobi Foundation library to your project
  //you can also use UserAgent controllerContext.HttpContext.Request.UserAgent
  return controllerContext.HttpContext.Request.Browser.IsMobileDevice;
 }


And in the global.asax remove the default view engine and add our custome view engine.

 //Remove default RazorViewEngine and add MobileCapableRazorViewEngine
 ViewEngines.Engines.Remove(ViewEngines.Engines.OfType().First());
 ViewEngines.Engines.Add(new MobileCapableRazorViewEngine());

 

Similar approach can be taken for FindPartialView.

Dec 1, 2011

Create a Custome valueprovider

Value providers are used by the model binding system in MVC to populate the values of model objects. MVC 3.0 includes value providers for several common value sources
System.Web.Mvc.FormValueProviderFactory
System.Web.Mvc.HttpFileCollectionValueProviderFactory
System.Web.Mvc.QueryStringValueProviderFactory
System.Web.Mvc.RouteDataValueProviderFactory

Decompile System.Web.Mvc.DefaultModelBinder, to review how BindProperty/BindModel (calls ContainsPrefix(text) on the value provider) and BindModel (GetValue) method uses Value Provider for populating model objects.

Below I have created a CookieValueProviderFactory derived from ValueProviderFactory which has a abstract method to GetValueProvider. In this case this gets my custome CookieValueProvider which implements IValueProvider (bool ContainsPrefix(string prefix) and System.Web.Mvc.ValueProviderResult GetValue(string key))

 public class CookieValueProviderFactory : ValueProviderFactory
    {
        public override IValueProvider GetValueProvider(ControllerContext controllerContext)
        {
            return new CookieValueProvider(controllerContext.HttpContext.Request.Cookies);
        }

        private class CookieValueProvider : IValueProvider
        {

            private readonly HttpCookieCollection _cookieCollection;

            public CookieValueProvider(HttpCookieCollection cookieCollection)
            {
                _cookieCollection = cookieCollection;
            }

            public bool ContainsPrefix(string prefix)
            {
                return _cookieCollection[prefix] != null;
            }

            public ValueProviderResult GetValue(string key)
            {
                HttpCookie cookie = _cookieCollection[key];
                return cookie != null ?
                           new ValueProviderResult(cookie.Value,
                                       cookie.Value,
                                       CultureInfo.CurrentUICulture)
                           : null;
            }
        }
    }

Then I add this to the ValueProviderFactories like this

 protected void Application_Start()
 {
  ValueProviderFactories.Factories.Add(new CookieValueProviderFactory());
 }

To test this I added cookies and in the Action this was automatically binded.

 private void AddCookie(IUserData userData)
 {
  HttpContext.Response.Cookies.Add(
   new HttpCookie("somethingToCookies", "Anything")
   );
 }
 
 public ActionResult CreateEmployee(string somethingToCookies)
 {
        }
 

Nov 30, 2011

Custome TempData Provider

Life span of TempData object is only till the subsequent request, or until the item is removed explicitly. This is useful when you want to pass data to another view that you will be redirecting to, rather than rendering to. By default, TempData is stored in the session using SessionStateTempDataProvider. So if you want to switch away from the default Session-State Mode, and use State Server Mode or SQL Server Mode, you need to store TempData objects which can be serializable.

You can also specify a custome temp data provider by setting TempDataProvider property of the controller.
public EmployeeController()
        {
            TempDataProvider = new CustomeTempDataProvider();
        }
 

CustomeTempDataProvider just need to implement ITempDataProvider. Refer to the sample code. In this example I am saving/loadion temp data to/from HttpContext.Current.Cache.

public class CustomeTempDataProvider : ITempDataProvider

    {
        internal string TempDataSessionStateKey = "__ControllerTempData";

        public IDictionary LoadTempData(ControllerContext controllerContext)
        {
            string userIdentifier = controllerContext.HttpContext.Request.UserHostAddress;

            HttpContext current = HttpContext.Current;
            if (current != null)
            {
                Dictionary dictionary = HttpContext.Current.Cache[TempDataSessionStateKey + userIdentifier] as Dictionary;
                if (dictionary != null)
                {
                    HttpContext.Current.Cache.Remove(TempDataSessionStateKey + userIdentifier);
                    return dictionary;
                }
            }
            return new Dictionary(StringComparer.OrdinalIgnoreCase);
        }

        public void SaveTempData(ControllerContext controllerContext, IDictionary values)
        {
            string userIdentifier = controllerContext.HttpContext.Request.UserHostAddress;

            if (controllerContext == null)
            {
                throw new ArgumentNullException("controllerContext");
            }
            HttpContext current = HttpContext.Current;
            bool flag = values != null && values.Count > 0;
            if (current == null)
            {
                if (flag)
                {
                    throw new InvalidOperationException("SessionStateTempDataProvider_SessionStateDisabled");
                }
            }
            else
            {
                if (flag)
                {
                    HttpContext.Current.Cache[TempDataSessionStateKey + userIdentifier] = values;
                    return;
                }
                if (HttpContext.Current.Cache[TempDataSessionStateKey + userIdentifier] != null)
                {
                    HttpContext.Current.Cache.Remove(TempDataSessionStateKey + userIdentifier);
                }
            }
        }
    }
 

How Default Model Binder works under the hood

If you have a requirement to write a custom model binding by implementing interface IModelBinder, then it's better to have a peek on DefaultModelBinder which is implemented in System.Web.Mvc. There is a lot which happens in this class and for most of the cases I prefer to create my custom model binder by deriving from DefaultModelBinder. In this way I can override whatever I need to and rest I can get for free.

There is a very less documentation around DefaultModelBinder so to find out what happens under the hood I decompiled System.Web.Mvc.DefaultModelBinder. Here are some of the important methods

  • BindModel - Only Member of IModelBinder interface. This binds simple as well as complex model.
  • BindComplexModel - This Creates Model by calling CreateModel and then call BindComplexElementalModel which calls BindProperties.
  • CreateModel - This create an instance of Model Type which you specify as a parameter type in the controller. So in the particular case CreateModel will return an object of type Employee.
    [HttpPost]
        public ActionResult EmployeeList(Employee employee)
       
    one case in which I had to override this was when I had a requirement to support for Interface type parameter. Refer to my posting on custom model binding
  • BindProperties - Binds all the properties of the model class by using controller context and binding context. This is the place where lot of magic happens. In the above case this will loop through all the properties of Employee and then bind from the controller context. This is one of the best part of default model binder which saves lot of development time. So for example it will bind the value of the property from HttpContext.Request.Form, which is collection of form variables. By default there are five ValueProviders:ChildActionValueProvider, FormValueProvider, RouteDataValueProvider, QueryStringValueProvider, HttpFileCollectionValueProvider.
  • BindProperty Calls GetPropertyValue which call BindModel which calls BindSimpleModel which gets the value.

In case of filters, DefaultModelBinder creates the Model object and in the filter you can access this model object using filterContext.ActionParameters[key] and populates the model object. In this case no binding happens in the DefaultModelBinder. Put a break point at the following location CreateModel, BindModel in the DefaultModelBinder and in your filter actionexecuting. You will notice that BindModel is called first and then CreateModel which creates the model object and then the filter action will be called. In filter action you can access the model object which CreateModel created using filterContext.ActionParameters[key]


The ModelBindingContext object is created and populated by whoever calls into the BindModel() method. If the model is coming in as an argument to your action method, this is done by ControllerActionInvoker.GetParameterValue(), and the ModelName property will be set to the name of the parameter (unless overridden by [Bind(Prefix = ... )]).

ModelBindingContext bindingContext = new ModelBindingContext() {
  FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null), // only fall back if prefix not specified 
  ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),
  ModelName = parameterName, 
  ModelState = controllerContext.Controller.ViewData.ModelState, 
  PropertyFilter = propertyFilter,
  ValueProvider = controllerContext.Controller.ValueProvider 
 };
 

If the model is being updated via UpdateModel(), the ModelBindingContext object is created by the UpdateModel() method itself. The ModelName parameter will be the prefix argument passed to UpdateModel().

Nov 16, 2011

How to persist Model State from a controller action using Action Filter

To persist ModelState from a controller action I created a filter called StateManagementFilter

    public class StateManagementFilter : ActionFilterAttribute
    {
        #region Constructor

        /// 
        /// 
        /// 
        /// This will reset state before action is executed.         /// This will save action state after it's executed        /// User can access/store state by the type        public StateManagementFilter(bool resetState = false, bool saveState = true, string stateType= "")
        {
            StateType = stateType;
            ShouldResetState = resetState;
            ShouldSaveState = saveState;

            //To avoid sending a user someone else’s state, we’ll need a way of uniquely identifying users.
            //Here I am using SessionID which may not be perfect always so we can use any shot of identification (like UserID) which uniquely identifies an User.
            HttpContext current = HttpContext.Current;
            UserIdentifier = "UserIdentifier" + current.Session.SessionID;
            
            if (resetState)
                ResetUserState();
        }

        #endregion

        #region ActionFilterAttribute Override

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (filterContext.Controller.ViewData.Model != null && ShouldSaveState)
                SetUserState(filterContext.Controller);
            base.OnActionExecuted(filterContext);
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (!ShouldResetState)
                filterContext.Controller.ViewData.Model = GetUserState(filterContext.Controller);
            base.OnActionExecuting(filterContext);
        }

        #endregion

        #region Private Property

        private string StateType { get; set; }
        private string UserIdentifier { get; set; }
        private bool ShouldResetState { get; set; }
        private bool ShouldSaveState { get; set; }

        #endregion

        #region Private Method

        private void ResetUserState()
        {
            HttpContext.Current.Cache.Remove(StateType + UserIdentifier);
        }


        /// 
        /// This stores the model object in the cache. For simplicity I am using web caching mechanism but we can use any caching framework 
        /// 
        ///         private void SetUserState(ControllerBase controller)
        {
            //If model object need to persisted only till the subsequent requests then TempData can be used
            //controller.TempData["__UserStateData"] = controller.ViewData.Model;
            System.Web.HttpContext.Current.Cache[StateType + UserIdentifier] = controller.ViewData.Model;
        }

        /// 
        /// This retrieves state from the cache
        /// 
        ///         /// 
        private object GetUserState(ControllerBase controller)
        {
            //return controller.TempData["__UserStateData"];
            return HttpContext.Current.Cache[StateType + UserIdentifier];
        }

        #endregion

    }


In the controller I can use this filter like like this

[StateManagementFilter(true, true, "EmployeeList")]
        public ActionResult EmployeeList()
        {
            return View(new Employee());
        }

        [HttpPost, StateManagementFilter(false, true, "EmployeeList")]
        public ActionResult EmployeeList(Employee employee)
        {
            Employee employee1 = (Employee) ViewData.Model;
            return View(employee);
        }

In the first controller action I am saving the state which will mean that the next controller action will get the View Data Model from the first controller action. So in the above example the new Employee object which is getting created in the first controller action can be available in the second controller action.

Also by specifying the type of state, controller can specify which state it will be interest in.

This will be mostly useful when you perform some costly business logic to create a model object in one controller action and would like to persist. So by using this filter you can save this in cache.

This can also be used for redirect call. For example you perform some business logic and then you decide based on some condition that this need to be redirected to different action. Although for this particular case it will be better to use TempData as the life span of TempData object is onli till the subsiquest request.Refer to the commented code in SetState/GetState. For using temp data in web farm you may need to implement custome temp data provider.

Nov 14, 2011

Custom Model Binder on Interface Type

Its very common to use Default Model Binder which maps a browser request to a data object, something like this.

public ActionResult EmployeeList(Employee employee)
{
    return View();
}


One down side of this is that during unit test you have to construct an object from employee class. It will be nice if we can have interface parameter type something like this. In this case, unit test doesn't have to bother on the concrete implementation of Employee class.

public ActionResult EmployeeList(IEmployee employee)
{
    return View();
}

But unfortunately this will throw exception "Cannot create an instance of an interface". The reason for this is that during binding it try to create an instance of model type which in this case is an interface and hence it throws exception. You can easily decompile System.Web.Mvc.DefaultModelBinder and look at method CreateModel where this is failing.

To get away with the issue I created an InterfaceTypeModelBinder. The constructor of the InterfaceTypeModelBinder takes ModelType as aparameter.

public class InterfaceTypeModelBinder : DefaultModelBinder
{
        private Type ModelType { get; set; }

        public InterfaceTypeModelBinder(Type modelType)
        {
            ModelType = modelType;
        }

        protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
        {
            Type type = GetModelType(bindingContext.ModelType);
            Debug.WriteLine(Environment.StackTrace);
            return Activator.CreateInstance(type);
        }

        private  Type GetModelType( Type modelType)
        {
            Type type = modelType;

            if (ModelType != null)
                type = ModelType;

            if (modelType.IsGenericType)
            {
                Type genericTypeDefinition = type.GetGenericTypeDefinition();
                if (genericTypeDefinition == typeof(IDictionary<,>))
                {
                    type = typeof(Dictionary<,>).MakeGenericType(type.GetGenericArguments());
                }
                else
                {
                    if (genericTypeDefinition == typeof(IEnumerable<>) || genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IList<>))
                    {
                        type = typeof(List<>).MakeGenericType(type.GetGenericArguments());
                    }
                }
            }
            return type;
        }       
}
        
Now in my Global.asax.cs file I can register my binder like this
protected void Application_Start()
{
     AreaRegistration.RegisterAllAreas();

     RegisterGlobalFilters(GlobalFilters.Filters);
     RegisterRoutes(RouteTable.Routes);
     ModelBinders.Binders[typeof(IEmployee)] = new InterfaceTypeModelBinder(typeof(Employee));
     ModelBinders.Binders[typeof(ICustomer)] = new InterfaceTypeModelBinder(typeof(Customer));
}


By doing this I can use IEmployee or ICustomer or any type as a parameter as long as it is registered.