Branislav Abadjimarinov's technical blog

Branislav Abadjimarinov's technical blog - Browsing ASP.NET

  1. AJAX the easy way in ASP.NET Web Forms using genric handlers

    In this article we'll see a quick and elegant ajax call solution based on generic handlers in asp.net and jQuery. The goal is to populate a street list in a dropdown with ajax call based on a selected postal code in a dropdown. The usual way to do this in ASP.NET Web Forms will be to use the AjaxToolkit, MS Ajax library and web service. The problem with this approach is that it is coplex and prone to errors.
    To accomplish the task we will create a generic handler(.ashx file) in our asp.net project. The generic handler is a lightweight approach to deliver content to the user. It doesn't have the heavy page lifecycle. By default the generic handler has only one method - ProcessRequest and one readonly property - IsReusable. The handler inherits directly from IHttpHandler. The IsReusable property defines if one handler object can be pooled and shared on different requests. If you set it to true be sure that the ProcessRequest method is thread safe. Than we will implement the ProcessRequest methods as follows:

    using System;
    using System.Web;
    using System.Collections.Generic;
    using System.Runtime.Serialization.Json;

    public class GetStreetsByPostCode : IHttpHandler
    {
      public void ProcessRequest(HttpContext context)
      {
        List<string> streets = new List<string>();
        int postCode;
        string postCodeParameter = context.Request.QueryString["code"];
       
        if (!String.IsNullOrEmpty(postCodeParameter) &&
            int.TryParse(postCodeParameter, out postCode))
        {
          AddressService service = new AddressService();
          streets = service.GetAvailableStreetsByPostCode(postCode);
        }
       
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<string>));
        serializer.WriteObject(context.Response.OutputStream, streets);

        context.Response.ContentType = "application/json";
      }

      public bool IsReusable
      {
        get
        {
          return false;
        }
      }
    }

    As you see we are using DataContractJsonSerializer object to serialize data in JSON format. The JSON format is native data representation format for the JavaScript language. Also we set the content type http header to "application/json" which is the JSON document mime type. The AddressService is just a class used to get data. This is all we need from server side. For client side we will use jQuery library. Based on a selected post code we will populate a dropdown with available streets for this post code. The script bellow will make an ajax request to our generic handler, parse the JSON data and fill in the dropdown values.

    $(function() {
      $("#DropDownPost").change(function() {
        LoadAddress();
      });
    });
    function LoadAddress() {
      var selectedPostCode = $("#DropDownPostCode option:selected").text();
      if (selectedPostCode && selectedPostCode != "0") {
        $.post("/GetStreetsByPostCode.ashx?code=" + selectedPostCode,
             function(result) {
                 var streets = eval("(" + result + ")");
                 ClearStreetsDropDown();
                 PopulateStreetsDropDown(streets);});
      } else {
        ClearStreetsDropDown();
      }
    }
    function ClearStreetsDropDown() {
      $('#DropDownStreet option').each(function(i, option) { $(option).remove(); });
    }
    function PopulateStreetsDropDown(streets) {
        $('#DropDownStreet').append(
            $('<option selected="selected"></option>').val("").html("")
          );
        $.each(streets, function(val, text) {
          $('#DropDownStreet').append(
            $('<option></option>').val(text).html(text)
          );
        });
    }

    Posted by Branislav Abadjimarinov on March 26 at 12:15 AM

  2. How to speed up compilations in ASP.NET Web Site

    When working with ASP.NET most developers, including me, choose to use the Web Site model. One of the reasons for that is the smooth working process. If you make a change in your website you do not need to manually recompile - asp.net does that for you. But because of this change there is a slight delay before the site starts again. There are several tricks to decrease this time to minimum.

    1) Use the optimize compilation option. It was introduced with a hotfix for .net 3.5 sp1. You can find it here - http://support.microsoft.com/kb/961884.
     In general on change in App_Code, web.config or Global.asax file ASP.NET wipes the temporary files and generates new. This option allows ASP.NET to generate only the changed part of the files and leave the others. The only thing you have to do to enable this behavior is to put in your web.config :
    <compilation optimizeCompilations="true">
    The fix is for Windows XP,Windows Server 2003, Windows Vista and Windows Server 2008.
    If you're running Windows 7 or Windows Server 2008 R2 you already have this feature installed but you still need to enable it.

    The first trick will take care of the redundant file generation but files still will be copied. The Second one will speed the copy and access process.

    2) Keep your ASP.NET temporary files in the RAM. There are some programs that create a RAM disk - a hard disk partition that is persisted in the RAM. I personally use RAMDisk - it is free for hard disks bellow 4GB and you can download it from this address - http://memory.dataram.com/products-and-services/software/ramdisk/download-ramdisk. The UI of the software is pretty simple - you have to choose size of disk in MB and Format.

    RAMDisk

    The options for format are FAT16, FAT32 & Unformatted. You can use Also NTFS which I will recommend. You need to choose the Unformatted option. Then you Start the RAMDisk, go to Control Panel->Administrative Tools->Computer Management and in the left pane choose Storage->Disk Management. Right-click on the unallocated space and choose New Simple Volume. You'll be prompted to choose Letter for the volume - I usually choose R (for RAMDisk).

    ComputerManagement

    Now you should be able to see the new drive in Windows Explorer.

    WindowsExplorer

    The only thing you have to do next is to change the path for your temporary ASP.NET files in web config's compiler section.
    <compilation defaultLanguage="c#" debug="true" optimizeCompilations="true" tempDirectory="R:\ASP_NET_TempFiles\" >
    On the next website request you'll see that the files will be populated on your newly created RAM disk.

    The size of the disk depends on how big your project is. I usually use no more than 512MB - a good round number :-) .

    If you want to keep the data in the virtual disk even when you shut down your PC you can persist the drive to image file (.img) from RAMDisk.

    RAMDisk second tab

     

    P.S. Have in mind that there are technologies that does put your most used files in the RAM automatically (for example Microsoft Super Fetch) but you do not have controll what does and what doesn't get cached. So just try this method and see if there are any improvements in web application start time.

    Posted by Branislav Abadjimarinov on February 02 at 11:18 AM

  3. Renew User in the same Request in asp.net while using forms authentication with cookies

    In ASP.NET forms authentication the usual authentication flow goes like this:

    • a user submits his credentials;
    • they are  validated against database, web.config or other data source;
    • an authentication cookie is issued and added to the current response (which is not sent yet);
    • the cookie is sent along with the response;
    • on the next request from that user the cookie is decrypted and the current Security Principal is initialised with the data;

    While this is good for most cases it has one flow - immediately after the authentication cookie is issued the current Security Principal for the request is outdated. You can check this with the following code:

    if(Membership.ValidateUser(userName, password))
    {
        FormsAuthentication.SetAuthCookie(userName, false);
        bool isAuthenticated = Request.IsAuthenticated; // isAuthenticated is false
    }

    This outcome is logical because of the authentication flow but some times it is confusing and can cause problems. A possible solution is to renew the information for the Current Security Principal for the request. This can be done with the following code:
    public void RenewCurrentUser()
    {
      System.Web.HttpCookie authCookie =
        System.Web.HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
      if (authCookie != null)
      {
        FormsAuthenticationTicket authTicket = null;
        authTicket = FormsAuthentication.Decrypt(authCookie.Value);

        if (authTicket != null && !authTicket.Expired)
        {
          FormsAuthenticationTicket newAuthTicket = authTicket;

          if (FormsAuthentication.SlidingExpiration)
          {
            newAuthTicket = FormsAuthentication.RenewTicketIfOld(authTicket);
          }
          string userData = newAuthTicket.UserData;
          string[] roles = userData.Split(',');

          System.Web.HttpContext.Current.User =
            new System.Security.Principal.GenericPrincipal(new FormsIdentity(newAuthTicket), roles);
        }
      }
    }

    You can check if the solution works like this:

    if(Membership.ValidateUser(userName, password))
    {
        FormsAuthentication.SetAuthCookie(userName, false);
        bool isAuthenticated = Request.IsAuthenticated; // isAuthenticated is false
        RenewCurrentUser();
        isAuthenticated = Request.IsAuthenticated; // isAuthenticated is true
    }
     

    Posted by Branislav Abadjimarinov on January 24 at 3:52 AM

  4. How to render asp.net control to string

    Every control in ASP.NET inherits from System.Web.UI.Control and have the RenderControl method. When you want to get the html output of the control at any point of the page execution lifecycle you can use the following method.

    public string RenderControl(Control ctrl)
    {
        StringBuilder sb = new StringBuilder();
        StringWriter tw = new StringWriter(sb);
        HtmlTextWriter hw = new HtmlTextWriter(tw);

        ctrl.RenderControl(hw);
        return sb.ToString();
    }

    At this point ASP.NET MVC does not have full support for asp.net controls. Using this trick you can render control's html directly on the view using a html helper. While this is good for read-only controls it will not work with controls that require event handling because in ASP.NET MVC by default you are missing  the page lifecycle.

    Posted by Branislav Abadjimarinov on October 28 at 9:10 AM

  5. ASP.NET MVC default route problem

    If you are working with ASP.NET MVC you definitely have seen that in the startup project in Visual Studio in Global.asax file there is a default route added like this:

    routes.MapRoute(
    "Content",     // Route name
    "{controller}/{action}/{id}",    // URL with parameters
    new { controller = "Home", action = "Index", id = "" } // Parameter defaults
    );

    It is very useful to quickly show how the MVC works but in commercial projects I'll suggest  not to use a default route for handling /{Controller}/{Action} requests in ASP.NET MVC. By doing that you can get a ton of these exceptions:

    System.Web.HttpException: The controller for path '/MyCustomPath/' could not be found or it does not implement IController.

    The reason for this is that when you make a request for non-existing path like /MyCustomPath/ the MVC Controller factory will try to instantiate a class inheriting IController and named "MyCustomPath" and when It doesn't find it an exception will be thrown.
    If the non-existing url is in some static file like .css or .js or the html of the page you can get a lot of these exceptions in just a few seconds if the web server is under a heavy load.

    The solution is simple - just remove the default route and add explicitly routes with the controllers you actually use. By doing that you'll get a 404 without the exception.

    Posted by Branislav Abadjimarinov on October 26 at 1:09 PM

© Copyright 2017 Powered by AtomSite 1.3.0.0