April 2010 - Posts - Raj Kaimal

April 2010 - Posts

Editing Project files, Resource Editors in VS 2010

Editing Project Files

Visual Studio gives you the ability to easily edit the project file associated with your project (.csproj or .vbproj). You might do this to change settings related to how the project is compiled since proj files are MSBuild files. The ability to edit project files has been available in previous versions of Visual studio and is not a new feature.

One would normally close Visual Studio and edit the proj file using a text editor. 
The better way is to first unload the project in Visual Studio by right clicking on the project in the solution explorer and selecting “Unload Project”

image 
The project gets unloaded and is marked “unavailable”
The project file can now be edited by right clicking on the unloaded project.

image
image  
After editing the file, the project can be reloaded.

image

Resource editors in VS 2010

Visual studio also comes with a number of resource editors (see list here).
For example, you could open a file using the Binary editor like so.

Go to File > Open > File..

Select a File and choose the “Open With..” option in the bottom right.

image 
We are given the option to choose an editor.

image 
Note that clicking on the “Add..” in the dialog above allows you to include your favorite editor.
image 
Choosing the “Binary editor” above allows us to edit the file in hex format. In addition, we can also search for hex bytes or ASCII strings using the Find command.
image 

The “Open With..” option is also available from within the solution explorer as shown below:

image

Enjoy!

 

Mr. Incredible: No matter how many times you save the world, it always manages to get back in jeopardy again. Sometimes I just want it to stay saved! You know, for a little bit? I feel like the maid; I just cleaned up this mess! Can we keep it clean for... for ten minutes!

Posted by rajbk | 2 comment(s)
Filed under: , ,

Localization in ASP.NET MVC 2 using ModelMetadata

This post uses an MVC 2 RTM application inside VS 2010 that is targeting the .NET Framework 4.

.NET 4 DataAnnotations comes with a new Display attribute that has several properties including specifying the value that is used for display in the UI and a ResourceType. Unfortunately, this attribute is new and is not supported in MVC 2 RTM.

The good news is it will be supported and is currently available in the MVC Futures release.

The steps to get this working are shown below:

  1. Download the MVC futures library
     
  2. Add a reference to the Microsoft.Web.MVC.AspNet4 dll.
     
  3. Add a folder in your MVC project where you will store the resx files
    image
     
  4. Open the resx file and change “Access Modifier” to “Public”. This allows the resources to accessible from other assemblies. Internaly, it changes the “Custom Tool” used to generate the code behind from  ResXFileCodeGenerator to “PublicResXFileCodeGenerator”

     image 
  5. Confirm that the “Build Action” of the resx is set to “Embedded Resource”
     
  6. Add your localized strings in the resx.
     
  7. Register the new ModelMetadataProvider
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
     
        RegisterRoutes(RouteTable.Routes);
     
        //Add this
        ModelMetadataProviders.Current = new DataAnnotations4ModelMetadataProvider();
        DataAnnotations4ModelValidatorProvider.RegisterProvider();
    }

     
  8. Use the Display attribute in your Model
    public class Employee
    {
        [Display(Name="ID")]
        public int ID { get; set; }
     
        [Display(ResourceType = typeof(Common), Name="Name")]
        public string Name { get; set; }
    }

  9. Use the new HTML UI Helpers in your strongly typed view:
      <%: Html.EditorForModel() %>
      <%: Html.EditorFor(m => m) %>
      <%: Html.LabelFor(m => m.Name) %>

      ..and you are good to go.

    Adventure is out there!

    Posted by rajbk | 9 comment(s)
    Filed under: , , ,

    Setting up Visual Studio 2010 to step into Microsoft .NET Source Code

    Using the Microsoft Symbol Server to obtain symbol debugging information is now much easier in VS 2010. Microsoft gives you access to their internet symbol server that contains symbol files for most of the .NET framework including the recently announced availability of MVC 2 Symbols. 

    SETUP

    In VS 2010 RTM, go to Tools –> Options –> Debugging –> General. Check “Enable .NET Framework source stepping”
    image
    We get the following dialog box

     image
    This automatically disables “Enable My Code”
     image
    Go to Debugging –> Symbols and Check “Microsoft Symbol Servers”. You can selectively exclude modules if you want to.
      image
    You will get a warning dialog like so:
    image
    Hitting OK will start the download process
      image
    The setup is complete. You are now ready to start debugging!


    DEBUGGING

    Add a break point to your application and run the application in debug mode (F5 shortcut for me). Go to your call stack when you hit the break point. Right click on a frame that is grayed out.
    image
    Select “Load Symbols from” “Microsoft Symbol Servers”. VS will begin a one time download of that assembly. This assembly will be cached locally so you don’t have to wait for the download the next time you debug the app.
    image 
    We get a one time license agreement dialog box
    image

    You might see an error like the one below regarding different encoding (hopefully will be fixed).
    image  
    Assemblies for which the symbols have been loaded are no longer grayed out. Double clicking on any entry in the call stack should now directly take you to the source code for that assembly.

    image

    image

    AFAIK, not all symbols are available on the MS symbol server. In cases like that you will see a tab like the one below and be given the option to “Show Disassembly”.

    image

    Enjoy!

     

    Check out this post also: VS 2010 Debugger Improvements (BreakPoints, DataTips, Import/Export)

    Newsreel Announcer: Humiliated, Muntz vows a return to Paradise Falls and promises to capture the beast alive!
    Charles Muntz: [speaking to a large audience outside in the newsreel] I promise to capture the beast alive, and I will not come back until I do!

    Posted by rajbk | 10 comment(s)
    Filed under: , , , , ,

    ASP.NET Dynamic Data Deployment Error

    You have an ASP.NET 3.5 dynamic data website that works great on your local box. When you deploy it to your production machine and turn on debug, you get the YSD

    Server Error in '/MyPath/MyApp' Application.


    Parser Error

    Description: An error occurred during the parsing of a resource required to service this request. Please review the following specific parse error details and modify your source file appropriately.

    Parser Error Message: Unknown server tag 'asp:DynamicDataManager'.

    Source Error:

    Line 5: 
    Line 6:  <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
    Line 7:      <asp:DynamicDataManager ID="DynamicDataManager1" runat="server" AutoLoadForeignKeys="true" />
    Line 8: 
    Line 9:      <h2><%= table.DisplayName%></h2>

    Probable Causes

    • The server does not have .NET 3.5 SP1, which includes ASP.NET Dynamic Data, installed. Download it here.
    • The third tagPrefix shown below is missing from web.config
    • <pages>
      <controls>
      <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add tagPrefix="asp" namespace="System.Web.DynamicData" assembly="System.Web.DynamicData, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      </controls>
      </pages>
       
       
      Hope that helps!

      TeamHanselmanLargeBanner2
    Posted by rajbk | with no comments
    Filed under: , , ,

    ASP.NET Asynchronous Pages and when to use them

    There have been several articles posted about using  asynchronous pages in ASP.NET but none of them go into detail as to when you should use them. I finally saw a great post by Thomas Marquardt that explains the process in depth. He addresses a key misconception too:

    So, in your ASP.NET application, when should you perform work asynchronously instead of synchronously? Well, only 1 thread per CPU can execute at a time.  Did you catch that?  A lot of people seem to miss this point...only one thread executes at a time on a CPU. When you have more than this, you pay an expensive penalty--a context switch. However, if a thread is blocked waiting on work...then it makes sense to switch to another thread, one that can execute now.  It also makes sense to switch threads if you want work to be done in parallel as opposed to in series, but up until a certain point it actually makes much more sense to execute work in series, again, because of the expensive context switch.

    Pop quiz: If you have a thread that is doing a lot of computational work and using the CPU heavily, and this takes a while, should you switch to another thread? No! The current thread is efficiently using the CPU, so switching will only incur the cost of a context switch.

    Ok, well, what if you have a thread that makes an HTTP or SOAP request to another server and takes a long time, should you switch threads? Yes! You can perform the HTTP or SOAP request asynchronously, so that once the "send" has occurred, you can unwind the current thread and not use any threads until there is an I/O completion for the "receive". Between the "send" and the "receive", the remote server is busy, so locally you don't need to be blocking on a thread, but instead make use of the asynchronous APIs provided in .NET Framework so that you can unwind and be notified upon completion. Again, it only makes sense to switch threads if the benefit from doing so out weights the cost of the switch.

    Read more about it in these posts:

    Performing Asynchronous Work, or Tasks, in ASP.NET Applications
    http://blogs.msdn.com/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx

    ASP.NET Thread Usage on IIS 7.0 and 6.0
    http://blogs.msdn.com/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx

    PS: I generally do not write posts that simply link to other posts but think it is warranted in this case.

    Posted by rajbk | 1 comment(s)
    Filed under: , , , ,

    Code snippets for ASP.NET MVC2 in VS 2010

    VS 2010 comes with ready made snippets which helps you save time while coding.

    You insert a snippet by typing the name of the code snippet and hitting the Tab key twice. You can also use the following method if you wish to see a listing of snippets available.

    • Press Ctrl + K, Ctrl + X
      image
      Select ASP.NET MVC2 with the arrow keys and hit enter to see a list of snippets available.
      image 


    The MVC related snippets you get out of the box (for C#) are listed below:

    HTML

    • actionlink
      Markup snippet for an ASP.NET MVC action link helper
      <%= Html.ActionLink("linktext", "actionname") %>
       
    • beginformajaxcs
      Markup snippet for an ASP.NET MVC AJAX-enabled form helper in C#
      <% using (Ajax.BeginForm("actionname", new AjaxOptions {UpdateTargetId= "elementid" })) { %>
      <% } %>
       
    • beginformcs
      Markup snippet for an ASP.NET MVC form helper in C#
      <% using (Html.BeginForm()) { %>
      <% } %>
       
    • displayforcs
      Markup snippet for an ASP.NET MVC templated helper.
      <%= Html.DisplayFor(x => x.Property) %>
       
    • editorforcs
      Markup snippet for an ASP.NET MVC templated helper.
      <%= Html.EditorFor(x => x.Property) %>
       
    • foreachcs
      Markup snippet for an ASP.NET MVC foreach statement in C#
      <% foreach (var item in collection) { %>
      <% } %>
       
    • ifcs
      Markup snippet for a code-nugget if else statement in C#
      <% if (true) { %>
      <% } %>
       
    • ifelsecs
      Markup snippet for a code-nugget if else statement in C#
      <% if (true) { %>
      <% } else { %>
      <% } %>
       
    • renderpartialcs
      Markup snippet for an ASP.NET MVC partial view rendering in C#
      <% Html.RenderPartial("viewname"); %>
       
    • textboxmvc
      Markup snippet for an ASP.NET MVC textbox helper
      <%= Html.TextBox("name") %>
       
    • validationsummarymvc
      Markup snippet for an ASP.NET MVC validation summary helper
      <%
      = Html.ValidationSummary() %>

    CS

    • mvcaction
      Code snippet for an action.
      public ActionResult Action()
      {
          return View();
      }
       
    • mvcpostaction
      Code snippet for an action via http post.
      [HttpPost]

      public ActionResult Action()
      {
          return View();
      }
       

    Enjoy!

    Posted by rajbk | 3 comment(s)
    Filed under: , , ,

    ASP.NET MVC Paging/Sorting/Filtering a list using ModelMetadata

    This post looks at how to control paging, sorting and filtering when displaying a list of data by specifying attributes in your Model using the ASP.NET MVC framework and the excellent MVCContrib library.

    Please see this post for a way of rendering the UI without using custom Sorting and Filtering attributes.

    It also shows how to hide/show columns and control the formatting of data using attributes. 

    This uses the Northwind database. A sample project is attached at the end of this post.

    Let’s start by looking at a class called ProductViewModel. The properties in the class are decorated with attributes.

    • The OrderBy attribute tells the system that the Model can be sorted using that property.
    • The SearchFilter attribute tells the system that filtering is allowed on that property. Filtering type is set by the  FilterType enum which currently supports Equals and Contains.
    • The ScaffoldColumn property specifies if a column is hidden or not
    • The DisplayFormat specifies how the data is formatted.
    public class ProductViewModel
    {
        [OrderBy(IsDefault = true)]
        [ScaffoldColumn(false)]
        public int? ProductID { get; set; }
     
        [SearchFilter(FilterType.Contains)]
        [OrderBy]
        [DisplayName("Product Name")]
        public string ProductName { get; set; }
     
        [OrderBy]
        [DisplayName("Unit Price")]
        [DisplayFormat(DataFormatString = "{0:c}")] 
        public System.Nullable<decimal> UnitPrice { get; set; }
     
        [DisplayName("Category Name")]
        public string CategoryName { get; set; }
     
        [SearchFilter]
        [ScaffoldColumn(false)]
        public int? CategoryID { get; set; }
     
        [SearchFilter]
        [ScaffoldColumn(false)]
        public int? SupplierID { get; set; }
     
        [OrderBy]
        public bool Discontinued { get; set; }
    }

    Before we explore the code further, lets look at the UI. 

    The UI has a section for filtering the data. The column headers with links are sortable. Paging is also supported with the help of a pager row. The pager is rendered using the MVCContrib Pager component. The data is displayed using a customized version of the MVCContrib Grid component. The customization was done in order for the Grid to be aware of the attributes mentioned above.

    NwSalesUI

    Now, let’s look at what happens when we perform actions on this page. The diagram below shows the process:

    northwindsales_modelbinding

    The form on the page has its method set to “GET” therefore we see all the parameters in the query string. The query string is shown in blue above. This query gets routed to an action called Index with parameters of type ProductViewModel and PageSortOptions.

    The parameters in the query string get mapped to the input parameters using model binding. The ProductView object created has the information needed to filter data while the PageAndSorting object is used for paging and sorting the data.

    The last block in the figure above shows how the filtered and paged list is created.

    We receive a product list from our product repository (which is of type IQueryable) and first filter it by calliing the AsFiltered extension method passing in the productFilters object and then call the AsPagination extension method passing in the pageSort object.

    The AsFiltered extension method looks at the type of the filter instance passed in. It skips properties in the instance that do not have the SearchFilter attribute. For properties that have the SearchFilter attribute, it adds filter expression trees to filter against the IQueryable data.

    The AsPagination extension method looks at the type of the IQueryable and ensures that the column being sorted on has the OrderBy attribute. If it does not find one, it looks for the default sort field [OrderBy(IsDefault = true)]. It is required that at least one attribute in your model has the [OrderBy(IsDefault = true)]. This because a person could be performing paging without specifying an order by column. As you may recall the LINQ Skip method now requires that you call an OrderBy method before it. Therefore we need a default order by column to perform paging. The extension method adds a order expressoin tree to the IQueryable and calls the MVCContrib AsPagination extension method to page the data.

    Implementation Notes

    Auto Postback
    The search filter region auto performs a get request anytime the dropdown selection is changed. This is implemented using the following jQuery snippet

    $(document).ready(function () {
        $("#productSearch").change(function () {
            this.submit();
        });
    });

    Strongly Typed View
    The code used in the Action method is shown below:

    public ActionResult Index(ProductViewModel productFilters, PageSortOptions pageSortOptions)
    {
        var productPagedList = productRepository.GetProductsProjected().AsFiltered(productFilters).AsPagination(pageSortOptions);
     
        var productViewFilterContainer = new ProductViewFilterContainer();
        productViewFilterContainer.Fill(productFilters.CategoryID, productFilters.SupplierID, productFilters.ProductName);
     
        var gridSortOptions = new GridSortOptions { Column = pageSortOptions.Column, Direction = pageSortOptions.Direction };
     
        var productListContainer = new ProductListContainerModel
        {
            ProductPagedList = productPagedList,
            ProductViewFilterContainer = productViewFilterContainer,
            GridSortOptions = gridSortOptions
        };
     
        
        return View(productListContainer);
    }


    As you see above, the object that is returned to the view is of type ProductListContainerModel. This contains all the information need for the view to render the Search filter section (including dropdowns),  the Html.Pager (MVCContrib) and the Html.Grid (from MVCContrib). It also stores the state of the search filters so that they can recreate themselves when the page reloads (Viewstate, I miss you! :0) 

    The class diagram for the container class is shown below.

     

    image

    Custom MVCContrib Grid

    The MVCContrib grid default behavior was overridden so that it would auto generate the columns and format the columns based on the metadata and also make it aware of our custom attributes (see MetaDataGridModel in the sample code).


    Generic Extension methods for Sorting and Filtering


    The extension method AsFiltered takes in an IQueryable<T> and uses expression trees to query against the IQueryable data. The query is constructed using the Model metadata and the properties of the T filter (productFilters in our case). Properties in the Model that do not have the SearchFilter attribute are skipped when creating the filter expression tree.  It returns an IQueryable<T>.

    The extension method AsPagination takes in an IQuerable<T> and first ensures that the column being sorted on has the OrderBy attribute. If not, we look for the default OrderBy column ([OrderBy(IsDefault = true)]). We then build an expression tree to sort on this column. We finally hand off the call to the MVCContrib AsPagination which returns an IPagination<T>.

    This type as you can see in the class diagram above is passed to the view and used by the MVCContrib Grid and Pager components.


    Custom Provider
    To get the system to recognize our custom attributes, we create our MetadataProvider as mentioned in this article (http://bradwilson.typepad.com/blog/2010/01/why-you-dont-need-modelmetadataattributes.html)

    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        ModelMetadata metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
     
        SearchFilterAttribute searchFilterAttribute = attributes.OfType<SearchFilterAttribute>().FirstOrDefault();
        if (searchFilterAttribute != null)
        {
            metadata.AdditionalValues.Add(Globals.SearchFilterAttributeKey, searchFilterAttribute);
        }
     
        OrderByAttribute orderByAttribute = attributes.OfType<OrderByAttribute>().FirstOrDefault();
        if (orderByAttribute != null)
        {
            metadata.AdditionalValues.Add(Globals.OrderByAttributeKey, orderByAttribute);
        }
     
        return metadata;
    }

    We register our MetadataProvider in Global.asax.cs.

    protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
     
                RegisterRoutes(RouteTable.Routes);
     
                ModelMetadataProviders.Current = new MvcFlan.QueryModelMetaDataProvider();
            }


    Bugs, Comments and Suggestions are welcome!

    You can download the sample code below.


    This code is purely experimental. Use at your own risk. Please see this post for a way of rendering the UI without using custom Sorting and Filtering attributes.
    Download Sample Code (VS 2010 RTM)

    Posted by rajbk | 3 comment(s)
    Filed under: , , , ,

    Securing an ASP.NET MVC 2 Application

    This post attempts to look at some of the methods that can be used to secure an ASP.NET MVC 2 Application called Northwind Traders Human Resources

    The sample code for the project is attached at the bottom of this post.

    We are going to use a slightly modified Northwind database. The screen capture from SQL server management studio shows the change. I added a new column called Salary, inserted some random salaries for the employees and then turned off AllowNulls.

    image  

    The reporting relationships for Northwind Employees is shown below.

     image

    The requirements for our application are as follows:

    • Employees can see their LastName, FirstName, Title, Address and Salary
    • Employees are allowed to edit only their Address information
    • Employees can see the LastName, FirstName, Title, Address and Salary of their immediate reports
    • Employees cannot see records of non immediate reports. 
    • Employees are allowed to edit only the Salary and Title information of their immediate reports.
    • Employees are not allowed to edit the Address of an immediate report
    • Employees should be authenticated into the system. Employees by default get the “Employee” role. If a user has direct reports, they will also get assigned a “Manager” role. We use a very basic empId/pwd scheme of EmployeeID (1-9) and password test$1. You should never do this in an actual application.
    • The application should protect from Cross Site Request Forgery (CSRF). For example, Michael could trick Steven, who is already logged on to the HR website, to load a page which contains a malicious request. where without Steven’s knowledge, a form on the site posts information back to the Northwind HR website using Steven’s credentials. Michael could use this technique to give himself a raise :-)

    UI Notes
    The layout of our app looks like so:

    image

    When Nancy (EmpID 1) signs on, she sees the default page with her details and is allowed to edit her address.

    image

    If Nancy attempts to view the record of employee Andrew who has an employeeID of 2 (Employees/Edit/2), she will get a “Not Authorized” error page.
    image 
    When Andrew (EmpID 2) signs on, he can edit the address field of his record and change the title and salary of employees that directly report to him.

    image

     

    Implementation Notes

    All controllers inherit from a BaseController. The BaseController currently only has error handling code.

    When a user signs on, we check to see if they are in a Manager role. We then create a FormsAuthenticationTicket, encrypt it (including the roles that the employee belongs to) and add it to a cookie.

    private void SetAuthenticationCookie(int employeeID, List<string> roles)
          {
              HttpCookiesSection cookieSection = (HttpCookiesSection) ConfigurationManager.GetSection("system.web/httpCookies");
              AuthenticationSection authenticationSection = (AuthenticationSection) ConfigurationManager.GetSection("system.web/authentication");
     
              FormsAuthenticationTicket authTicket =
                  new FormsAuthenticationTicket(
                  1, employeeID.ToString(), DateTime.Now, DateTime.Now.AddMinutes(authenticationSection.Forms.Timeout.TotalMinutes), 
                  false, string.Join("|", roles.ToArray()));
     
              String encryptedTicket = FormsAuthentication.Encrypt(authTicket);
              HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
     
              if (cookieSection.RequireSSL || authenticationSection.Forms.RequireSSL)
              {
                  authCookie.Secure = true;
              }
     
              HttpContext.Current.Response.Cookies.Add(authCookie);
          }

    We read this cookie back in Global.asax and set the Context.User to be a new GenericPrincipal with the roles we assigned earlier.

    protected void Application_AuthenticateRequest(Object sender, EventArgs e)
    {
        if (Context.User != null)
        {
            string cookieName = FormsAuthentication.FormsCookieName;
            HttpCookie authCookie = Context.Request.Cookies[cookieName];
            if (authCookie == null)
                return;
     
            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
            string[] roles = authTicket.UserData.Split(new char[] { '|' });
     
            FormsIdentity fi = (FormsIdentity)(Context.User.Identity);
            Context.User = new System.Security.Principal.GenericPrincipal(fi, roles);
        }
    }


    MVC provides the Authorize attribute which is a type of Action Filter that runs around the execution of the method. The Authorize attribute is added to ensure that only authorized users can execute that Action. We add the AuthorizeToView attribute to any Action method that requires authorization.

    [HttpPost]
    [Authorize(Order = 1)]
    //To prevent CSRF
    [ValidateAntiForgeryToken(Salt = Globals.EditSalt, Order = 2)]
    //See AuthorizeToViewIDAttribute class
    [AuthorizeToViewID(Order = 3)] 
    [ActionName("Edit")]
    public ActionResult Update(int id)
    {
        var employeeToEdit = employeeRepository.GetEmployee(id);
        if (employeeToEdit != null)
        {
            //Employees can edit only their address
            //A manager can edit the title and salary of their subordinate
            string[] whiteList = (employeeToEdit.IsSubordinate) ? new string[] { "Title", "Salary" } : new string[] { "Address" };
     
            if (TryUpdateModel(employeeToEdit, whiteList))
            {
                employeeRepository.Save(employeeToEdit);
                return RedirectToAction("Details", new { id = id });
            }
            else
            {
                ModelState.AddModelError("", "Please correct the following errors.");
            }
        }
        return View(employeeToEdit);
    }
     


    We use the TryUpdateModel with a white list to ensure that (a) an employee is able to edit only their Address and (b) that a manager is able to edit only the Title and Salary of a subordinate. This works in conjunction with the AuthorizeToViewIDAttribute.

    The ValidateAntiForgeryToken attribute is added (with a salt) to avoid CSRF. The Order on the attributes specify the order in which the attributes are executed.

    The Edit View uses the AntiForgeryToken helper to render the hidden token:

    ...
    ...
    <% using (Html.BeginForm())
       {%>
    <%=Html.AntiForgeryToken(NorthwindHR.Models.Globals.EditSalt)%>
    <%= Html.ValidationSummary(true, "Please correct the errors and try again.") %>
    <div class="editor-label">
        <%= Html.LabelFor(model => model.LastName) %>
    </div>
    <div class="editor-field">
    ...
    ...

    Using the Authorize attribute alone is not sufficient. It only checks to see if the user is authorized or is in a certain role. It does not check to see if the user has permission to access a certain record.
    Hence, we ensure that a user has permissions to view a record by creating a custom attribute AuthorizeToViewIDAttribute that inherits from ActionFilterAttribute. This action filter makes a call to ensure that the current user is authorized to view a certain record.

    public class AuthorizeToViewIDAttribute : ActionFilterAttribute
    {
        IEmployeeRepository employeeRepository = new EmployeeRepository();
     
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext.ActionParameters.ContainsKey("id") && filterContext.ActionParameters["id"] != null)
            {
                if (employeeRepository.IsAuthorizedToView((int)filterContext.ActionParameters["id"]))
                {
                    return;
                }
            }
            throw new UnauthorizedAccessException("The record does not exist or you do not have permission to access it");
        }
    }
     


    Instead of extending the ActionFilterAttribute, you could extend the AuthorizeAttribute as described here.

    In addition, Kazi Manzur Rashid suggests the following method:

    public class CustomAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
    {
       private readonly IEmployeeRepository employeeRepository = new EmployeeRepository();
       public void OnAuthorization(AuthorizationContext filterContext)
       {
           ValueProviderResult result = filterContext.Controller.ValueProvider.GetValue("id");
           if (result != null)
           {
               int id;
               if (int.TryParse(result.AttemptedValue, out id) && IsAuthorized(id))
               {
                   HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
                   cachePolicy.SetProxyMaxAge(new TimeSpan(0));
                   cachePolicy.AddValidationCallback(CacheValidateHandler, id);
                   return;
               }
           }
           filterContext.Result = new RedirectResult("/Error/NotAuthorized");
       }
     
       private bool IsAuthorized(int id)
       {
           return employeeRepository.IsAuthorizedToView(id);
       }
     
       private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
       {
           validationStatus = OnCacheAuthorization((int) data);
       }
     
       private HttpValidationStatus OnCacheAuthorization(int id)
       {
           bool isAuthorized = IsAuthorized(id);
           return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
       }
    }
     
     
     
     


    The application uses View specific models for ease of model binding. The View specific model contains just the properties required to render the view.

    public class EmployeeViewModel
    {
        public int EmployeeID;
        
        [Required]
        [DisplayName("Last Name")]
        public string LastName { get; set; }
     
        [Required]
        [DisplayName("First Name")]
        public string FirstName { get; set; }
     
        [Required]
        [DisplayName("Title")]
        public string Title { get; set; }
     
        [Required]
        [DisplayName("Address")]
        public string Address { get; set; }
     
        [Required]
        [DisplayName("Salary")]
        [Range(500, double.MaxValue)]
        public decimal Salary { get; set; }
     
        public bool IsSubordinate { get; set; }
    }
     

    To help with displaying readonly/editable fields, we use a helper method as shown below:

     
     
    //Simple extension method to display a TextboxFor or DisplayFor based on the isEditable variable
    public static MvcHtmlString TextBoxOrLabelFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, 
                                                                     Expression<Func<TModel, TProperty>> expression, 
                                                                     bool isEditable)
    {
        if (isEditable)
        {
            return htmlHelper.TextBoxFor(expression);
        }
        else
        {
            return htmlHelper.DisplayFor(expression);
        }
    }
     
     

    The helper method is used in the view like so:

    <%=Html.TextBoxOrLabelFor(model => model.Title, Model.IsSubordinate)%>

    You can download the demo project below. 
    VS 2008, ASP.NET MVC 2 RTM
    Remember to change the connectionString to point to your Northwind DB





    Feedback and bugs are always welcome :-)

    Posted by rajbk | 4 comment(s)
    Filed under: , , , ,
    More Posts