Kurser och utbildning av IT-proffs och systemutvecklare

i Sök

Cornerstones utvecklarblogg

Alla Taggar

All Tags » ASP.Net   (RSS)

  • Fluent-API to add ActionFilters to Controllers – ASP.NET MVC Part 2

    I’m working with my Fluent-API for adding Action Filters to Controllers and Action Methods. In my previous post, I created a new instance of each Action Filter and add it to an Action Method or Controller. Based on how the Action Filters are often implemented they don’t or shouldn’t keep any state, so in that case I don’t need to create a new instance of the same Action Filter with the same configuration for each Action Method I want to add it to. I also want to have an option to have a better overview of which Action Filter is added to which Controllers and Action Methods. I have added to methods, AddFilterToControllers and AddFilterToActions:

     

                .AddFilterToActions(new HandleErrorAttribute(),
                                    c=> c.About(),
                                    c=> c.Index());
                .AddFilterToActions(new OutOfMemoryException(),
                                    c=> c.About());
                 .AddFilterToActions(new HandleErrorAttribute() ...,
                                    c=> c.MyMethod());
    
                .AddFilterToControllers(new HandleErrorAttribute(),
                                        typeof(HomeController),
                                        typeof(Controller));
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

     

    The code is not yet available due to some more changes and testing.

    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
  • Fluent-API to add ActionFilters to Controller in ASP.NET MVC

    Note: The name of the classes and the methods are just temporary and may change, I’m so bad when it comes to naming classes and methods. The source code is simple and haven’t done so much refactoring etc. Just wanted to see if I could get it to work, so please have that in mind.

    When we create controllers for our ASP.NET MVC application we can also add Action Filters to handle cross-cutting concerns, like Authorization, Error handling and Caching etc. If we want to have Error handling on every controller we need to add the HandleErrorAttribute to all controllers, like this:

    [HandleError] 
    public MyController : Controller 
    { 
    }


    By adding Action Filters by using attributes it can be hard to get a good overview of which controllers that has the HandleErrorAttribute. The same regarding Action methods, for example if we have a lot of Controllers and want to see see all the Action methods that uses for example the OutputCacheAttribute, we need to go through all Controllers and methods, there is no easy way to get a simple overview of them.

    Adding Action Filters to Action methods and Controllers also add some sort of “dependency” to action filters (not a big deal, though). I decided to try a way to add Action Filters to Controllers and Action methods in one single file, so I could get a better overview of which Controllers and Action methods uses what ActionFilter etc. Because ActionFitlers contains cross-cutting concerns I also wanted to move it away as attributes from the Controllers and Action methods so developers don’t need to care about the cross-cutting concerns during the creation of Controllers. instead add them later.

    I sort of used a Fluent-API for the configuration of ActionFilters, and the configuration is added to the Global.asax’s Application_Start event. Here is an example where I add the ErroHandler Action Filter to all Controllers Action Methods:

    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
    ConfigActionFilter.ConfigController<Controller>() 
                      .AddFilterToController(new HandleErrorAttribute());
    
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }


    If I want to add an Action Filters to a specific Controller I just use the type of the Controller:

    ConfigActionFilter.ConfigController<HomeController>()
                                  .AddFilterToController(new HandleErrorAttribute());
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

     

    Note: The reason I don’t use XML and a XML meta data provider as a configuration is because I wanted to make the configuration type safe. If we use XML we can only get an exception if we spell something wrong at runtime.


    If I want to add Action Filters to Action Methods I can write something like this:

     
    ConfigActionFilter.ConfigController<HomeController>()
                                  .AddFilterToController(new HandleErrorAttribute())
                                  .AddFilterToAction(c => c.About(),
                                                     new HandleErrorAttribute(),
                                                     new OutputCacheAttribute() { Duration=10, VaryByParam = "none" })
                                  .AddFilterToAction(c => c.Index(),
                                                     new HandleErrorAttribute());
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

     

    I didn’t wanted to use a string for the ActionMethod (AddFilterToAction(“About”)) because it would not be type safe. I want to get a warning or error while typing the code, so instead I created an Expression. The AddFilterToAction takes a params of FilterAttributes, so I can easy add several of Action Filters to an Action Method. The AddFilterToController method will add Action Filter to the Controller, just like adding an Action Filter attribute to the class definition, so all Action Methods within the Controller will use the Action Filter.

    I created a Custom ControllerFactory so I could add my Custom ControllerActionInvoker to the Controller:

     public class ActionFilterConfigControllerFactory : DefaultControllerFactory
     {
            public override IController CreateController(RequestContext requestContext, string controllerName)
            {
                 ...
                    controllerInstance.ActionInvoker = new ActionFilerConfigControllerActionInvoker();
    
                return controller;
            }
     }
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

     

    The custom ControllerActionInvoker will make sure the Action Filter added by using my solution is added to the FilterInfo class. This is done by overriding the GetFilters method:

    public class ActionFilerConfigControllerActionInvoker : ControllerActionInvoker
    {
            protected override FilterInfo GetFilters(ControllerContext controllerContext,
                                                     ActionDescriptor actionDescriptor)
            {
                var filters = base.GetFilters(controllerContext, actionDescriptor);
    
                ...
    
                if (ConfigActionFilter.Config.ContainsKey(controllerName))
                    AddFiltersToFilerList(actionDescriptor, filters, controllerName);
    
                return filters;
            }
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    }

     

    This project only a fun thing to do and I like the idea of having different options to add Action Filters, and this solution will make the Controller clean from Attributes and also have one place to add them. When I create my Controller I don’t need to worry or think about the cross-cutting concerns, I just add them later and into the Global.asax.

    I want to thanks Michel Söderström for taking time to discuss this solution with him, and get some feedback and also “host” the source code for me.

    You can download the source code with an example here: http://vinkr.net/misc/ActionFilterConfig.zip

    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
  • Introduction to Templates in ASP.NET MVC 2 Screen cast

    I have recorded some screen casts during the last week for Microsoft, one is was published today on Channel 9 (The Screen cast is in Swedish, I will eventually try to record screen casts also in English, but when that happens, I don’t now). You can found the screen cast here:

    http://channel9.msdn.com/posts/MSDNSweden/Introduktion-till-Template-i-ASPNET-MVC-2/

    For those who can’t understand Swedish, here is a short summary of the screen cast.

    With ASP.NET MVC version 2.0 we can use templates. It will help us rendering forms etc out form the Model we passed to the View. We can easy modify templates to satisfy our needs. The simples way to use the Template feature is to use the Html’s DisplayForModel method:

    <%= Html.DisplayForModel() %>


    This method will iterate through the ViewData.ModelMetadata.Properties to render the Model. The ModelMetadata hold information about the model, such as properties etc. The result will remind us about the DetailView control shipped with ASP.NET 2.0, but will put the name of the property at top, and then a new line with the value of the property, like this:

    FirstName

    John

    LastName

    Doe


    There is also a method to render a editable from, EditorForModel. By default the methods will use a default template, we can easy override the template. We do that by adding a ASP.NET MVC User Control to the ~/Views/ControllerName/DisplayTemplates or EditTemplates folder. If you want to override how a String should be “rendered”, then you simply add a String.ascx file to the Templates folders, here is an example of a DisplayTemplate:


    <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl"%>
    <i><%= Html.Encode(Model) %></i>


    The above template will make the value of the rendered property to use italic:


    FirstName

    John

    LastName

    Doe

    We can do several more stuffs with template, for more information check out the following blog post from Brad Wilson:

  • Part 1: Introduction
  • Part 2: ModelMetadata
  • Part 3: Default Templates
  • Part 4: Custom Object Templates
  • Part 5: Master Page Templates
  •  

    You can find me on twitter: http://www.twitter.com/fredrikn

  • ASP.NET AJAX 4.0 Preview 5 – Working with Converters and the new CDN

    In this blog post I’m going to show you how you can use the new Converter feature during data binding, to make this post more special I have used the new Microsoft AJAX CDN (Content Delivery Network), so I don’t need to have the AJAX 4.0 script on my server. The example code I use in this post, will use the ASP.NET AJAX DataView and also Microsoft ADO.NET Data Services to retrieve data from the server. My fantasy isn’t so special good, but I decided to make a little “rental” application, where I will use ASP.NET AJAX to simply show a list of cars and the current rental status. The status of a rental car will be sent to the client as a string and can be of the following values, Approved, Not Approved and Pending. The little app I’m building for this post, will show the rental status as different colors for the user.

    image


    Green color will show that the rental of the car is approved, red will indicate not approved and yellow will indicate a pending status. My little database have one simple table, called Rental (lack of fantasy), with three columns, ID, Description and Status. I have used Entity Framework together with ADO.Net Data Source to get the data from the database. Here is the ADO.NET Data Service, I have added a Rental operation to only enable access to the operation to get Rental entities.

    public class RentalWebDataService : DataService<RentalModel.RentalEntities>
    {
        public static void InitializeService(IDataServiceConfiguration config)
        {
            config.SetEntitySetAccessRule("Rental", EntitySetRights.AllRead);
            config.SetServiceOperationAccessRule("Rental", ServiceOperationRights.AllRead);
        }
    
        [WebGet]
        public IQueryable<RentalModel.Rental> Rental()
        {
            return this.CurrentDataSource.Rental;
        }
    }
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    To use ASP.NET AJAX 4.0 Preview 5, I have not used my local scripts, instead use the new Microsoft AJAX CDN. The Microsoft CDN service provides caching support for AJAX libraries (including jQuery and ASP.NET AJAX) and are composed of “edge cache” servers, they are strategically placed around the world to make sure your application will use the nearest server to get the ASP.NET AJAX scripts, isn’t that cool?! The service is free and both for commercial and non-commercial purpose. To get a JavaScript from the Microsoft CDN, you can simply use the <script> tag to include the script from ”http://ajax.microsoft.com/ajax”. Microsoft CND will use sub folders to versioning the different kind of scripts. The ASP.NET AJAX 4.0 Preview 5.0 script can be found under the /0909/ folder (the name of the folder represents the year and month when the ASP.NET AJAX was released). You can find the JavaScript and URLs here: http://www.asp.net/ajax/cdn/

    Note: ScriptManger can automatically request JavaScript files form the Microsoft CDN if the EnableCdn property of the ScriptManager is set to true.


    I have used the following code to include the ASP.NET AJAX 4.0 Preview 5.0 script in my demo, to make sure I can use Templates, and ADO.NET DataServices:

    <script src="http://ajax.microsoft.com/ajax/beta/0909/MicrosoftAjax.js" type="text/javascript"></script>
    <script src="http://ajax.microsoft.com/ajax/beta/0909/MicrosoftAjaxTemplates.js" type="text/javascript"></script> <script src="http://ajax.microsoft.com/ajax/beta/0909/MicrosoftAjaxAdoNet.js" type="text/javascript"></script>
    <script src="http://ajax.microsoft.com/ajax/beta/0909/MicrosoftAjaxDataContext.js" type="text/javascript"></script>
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }


    To use the ADO.NET Data Service (RentalWebDataSerivce) on the client-side, the ASP.NET AJAX $create shortcut can be used to create a ADO.Net DataContext object, the DataContex is the proxy class to access the ADO.Net Data Service:

    var RentalService = {};
    
    RentalService.dataContext = $create(Sys.Data.AdoNetDataContext,
                                             {
                                                 serviceUri: "RentalWebDataService.svc",
                                                 mergeOption: Sys.Data.MergeOption.appendOnly
                                             });
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    The following is the template I have used to list the cars:

     <table sys:attach="dataview"
            class="sys-template"
            dataview:dataprovider="{{ RentalService.dataContext }}"
            dataview:fetchoperation="Rental"
            dataview:autofetch="true">
    
            <tr sys:if="$index==0">
                <td>Booking No</td>
                <td>Description</td>
            </tr>
            
             <tr sys:style-background-color="{binding Status, convert=convertStatusToColor, ColorType=Name}">
                 <td>{{ ID }}</td>
                 <td>{{ Description }}</td>
             </tr>
    </table>
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }


    Yes, I know, I use a table instead of a DIV, shame on me..

    To create a template the class=”sys-template” must be applied to the root element of the template, the sys:attached=”dataview” will indicate that I will use the ASP.NET AJAX 4.0 DataView features together with the template. To make sure the DataView will get its data from the RentalWebDataService, the dataview:dataprovider attribute is used and its value is set to the RentalService.dataContext object, the DataView’s autofecth property is also set to true, to automatically fetch data. The fetchoperation property will specify which operation of the RentalWebDataServices should be used when the DataView fetches the data.

    There are two ways to bind data within a template, for example the short {{ field }} expression or the {binding field} expression. As you can see I have used both expressions in the template, one for the background-color style (to bind a value to a specific style attribute, the sys:style-<style> attribute is used). The {{ ID }} and {{ Description }} shortcut will make sure to get the data from the Rental’s (The entity returned from the Rental operation of the RentalWebDataService ) ID and Description property. Take a look at the binding expression used for the <tr> background-color style:

    <tr sys:style-background-color="{binding Status, convert=convertStatusToColor, ColorType=Name}">
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }


    There is now an option to specify a Converter, a method which will take the bounded value and pass it as an argument to the specified method and do a conversion and return a new whole value. The Status property in my case returns a string with different kind of status, Approved, NotApproved and Pending. What I want to do, is to convert those values into different colors as I mention earlier in my post. To do that I have used the new Converter feature. To set up a converter is easy just use the Sys.Binding.converters property and assign a method to it with two arguments, the value to convert and a binding argument (holds information about the binding).

    Sys.Binding.converters.convertStatusToColor = function(status, binding)
    {
         //do something then return the converted value
    }
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    One great thing with the converter, is the possibility to use expandos, for example in my code I have added a ColorType expando to specify if the color that returned from the converter should be a name or not. The binding argument of the converter method can be used to get the expando, for example:

    "{binding Status, convert=convertStatusToColor, ColorType=Name}"
    
    
    Sys.Binding.converters.convertStatusToColor = function(status, binding)
    {
        if( binding.ColorName == 'Name')
              //....
         //do something then return the converted value
    }
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    I have created the ugliest converter method ever made, here it is:

    Sys.Binding.converters.convertStatusToColor = function(status, binding)
    {
           if (binding.ColorType == 'Name')
          {
                 if (status == 'Approved')
                        return 'green';
                    else if (status == 'NotApproved')
                        return 'red';
                    else if (status == 'Pending')
                        return 'yellow';
                    else
                        return 'white';
            }
            else
           {
                 if (status == "Approved")
                     return '#00ff00';
                 else if (status == "NotApproved")
                     return '#ff0000';
                 else if (status == 'Pending')
                     return '#ffff00';
                 else
                     return '#ffffff';
            }
    }
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    Well, it’s a simple method to convert the incoming string, and return a color for the <tr>’s background-color. Here is the whole client-side code:

    <%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
        <script src="http://ajax.microsoft.com/ajax/beta/0909/MicrosoftAjax.js" type="text/javascript"></script> 
        <script src="http://ajax.microsoft.com/ajax/beta/0909/MicrosoftAjaxTemplates.js" type="text/javascript"></script>
        <script src="http://ajax.microsoft.com/ajax/beta/0909/MicrosoftAjaxAdoNet.js" type="text/javascript"></script>
        <script src="http://ajax.microsoft.com/ajax/beta/0909/MicrosoftAjaxDataContext.js" type="text/javascript"></script>
    
    
        <script type="text/javascript">
    
            var RentalService = {};
    
            RentalService.dataContext = $create(Sys.Data.AdoNetDataContext,
                                             {
                                                 serviceUri: "RentalWebDataService.svc",
                                                 mergeOption: Sys.Data.MergeOption.appendOnly
                                             });
    
    
            Sys.Binding.converters.convertStatusToColor = function(status, binding) {
    
                if (binding.ColorType == 'Name') {
    
                    if (status == 'Approved')
                        return 'green';
                    else if (status == 'NotApproved')
                        return 'red';
                    else if (status == 'Pending')
                        return 'yellow';
                    else
                        return 'white';
                }
                else {
                    if (status == "Approved")
                        return '#00ff00';
                    else if (status == "NotApproved")
                        return '#ff0000';
                    else if (status == 'Pending')
                        return '#ffff00';
                    else
                        return '#ffffff';
                }
            }
    
        </script>
        
        <style type="text/css">
            .sys-template { display:none; }
        </style>
    
    </head>
    <body xmlns:sys="javascript:Sys" xmlns:dataview="javascript:Sys.UI.DataView">
        
        <table sys:attach="dataview"
            class="sys-template"
            dataview:dataprovider="{{ RentalService.dataContext }}"
            dataview:fetchoperation="Rental"
            dataview:autofetch="true">
    
            <tr sys:if="$index==0">
                <td>Booking No</td>
                <td>Description</td>
            </tr>
            
             <tr sys:style-background-color="{binding Status, convert=convertStatusToColor, ColorType=Name}">
                 <td>{{ ID }}</td>
                 <td>{{ Description }}</td>
             </tr>
        </table>
        
    </body>
    </html>
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    Summary

    In this blog post you have seen how to data bind and use the DataView to do client-side binding to a ADO.NET Data Service, you have also got introduced to the Microsoft AJAX NDC, if you want to get more information about the NDC, check out Scott Guthrie's blog: http://weblogs.asp.net/scottgu/archive/2009/09/15/announcing-the-microsoft-ajax-cdn.aspx, you have also seen the new Preview 5 converter feature.

    To find more information about ASP.NET AJAX 4.0 Preview 5.0, you can check out my two earlier posts:

    http://weblogs.asp.net/fredriknormen/archive/2009/09/11/asp-net-ajax-4-0-preview-5-available.aspx

    http://weblogs.asp.net/fredriknormen/archive/2009/09/11/keep-the-first-empty-item-in-a-listbox-when-using-asp-net-ajax-4-0-preview-5-and-observer.aspx

    Jim, Dave and Bertrand Le Roy’s posts on Preview 5:
    http://weblogs.asp.net/jimwang/archive/2009/09/11/asp-net-ajax-preview-5-and-updatepanel.aspx

    http://weblogs.asp.net/infinitiesloop/archive/2009/09/10/microsoft-ajax-4-preview-5-the-dataview-control.aspx

    http://weblogs.asp.net/bleroy/archive/2009/09/14/building-a-class-browser-with-microsoft-ajax-4-0-preview-5.aspx

     

    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
  • Keep the first “empty” Item in a listbox when using ASP.NET AJAX 4.0 Preview 5 and Observer

    I got a question as a comment on my previous post about the new features and changes to ASP.NET AJAX 4.0 Preview 5. I hope I understood the question right ;) It was about using a listbox and add an empty item at the top of the list, and keep it there when adding new items to an array that is bounded to the list, and by using the Observer feature. Maybe some more have or will have the same question, so I decided to write a blog post about it.

    In this blog post I will use a sys-template for the listbox, here is an example of a listbox template by using HTML <select> element:


    <
    select sys:attach="dataview" class="sys-template" dataview:data="{{ customers }}"> <option sys:value="{{ID}}">{{ Name }}<//option> </select>


    I will in this example use the dataview feature to simply fill the Listbox with values form an array.

    If we want to use ASP.NET AJAX 4.0 and the dataview feature, we need to add the sys and dataview namespace to the <body> tag:


    <
    body xmlns:sys="javascript:Sys" xmlns:dataview="javascript:Sys.UI.DataView">
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }


    Note: We don’t need to add the sys:activate=”*” attribute to the <body> element anymore.

    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    I will in this post use a simple hard coded javascript array for the customers bounded to the listbos, and here is my simple array together with a AddCustomer method:

    <script type="text/javascript">
    
        var customers = [
                          { ID : "JD", Name : "John Doe" },
                          { ID : "JD2", Name : "Jane Doe"}
                        ];
    
        function AddCustomer()
        {
            Sys.Observer.add(customers, { ID: "JD2", Name: "Some Doe" });
        }
        
    </script>

    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } The AddCustomer method will make sure to add a new customer to the customers array. The Sys.Observer is used to make sure the listbox which is bound to the customers array should be updated automatically when a new customer is added to the customers array.

    Here is the code for the whole solution so far:

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
        <script type="text/javascript" src="Scripts/MicrosoftAjax.debug.js"></script>
        <script type="text/javascript" src="Scripts/MicrosoftAjaxTemplates.debug.js"></script>
    
        <script type="text/javascript">
    
            var customers = [
                                { ID : "JD", Name : "John Doe" },
                                { ID : "JD2", Name : "Jane Doe"}
                            ];
    
            function AddCustomer()
            {
                Sys.Observer.add(customers, { ID: "JD2", Name: "Some Doe" });
            }
        
        </script>
        
        <style type="text/css">
        
            .sys-template { display:none; }
        
        </style>
    </head>
    
    <body
        xmlns:sys="javascript:Sys"
        xmlns:dataview="javascript:Sys.UI.DataView">
        
        <form id="form1" runat="server">
        <div>
        
        <select sys:attach="dataview"
            class="sys-template"
            dataview:data="{{ customers }}">
                <option sys:value="{{ID}}">{{ Name }}<//option>
        </select>
        
       <input type="button" onclick="AddCustomer();" value="Add item" />
       
        </div>
        </form>
    </body>
    </html>
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }


    As you may already notice there is no empty item at the top of the listbox items. So we add one to the template:


    <select sys:attach="dataview" class="sys-template" dataview:data="{{ customers }}"> <option value=”” selected="selected">Select an Item<//option> <option sys:value="{{ID}}">{{ Name }}<//option> </select>
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }


    If we run the code, we will notice that several “Select an Item” will be added to the listbox. What we want to do is to keep the first and “empty” item when a new customer is added to the customers array, we don’t want to have the “empty” list repeatable for each item in the list. To solve this we can use the sys:if attribute. We simply use it to see if the index of the currently bounded item is 0, if so, we add the “empty” item. By doing that we will make sure to only have one “empty” item added, and it will only  be added when the index is 0.

    <select sys:attach="dataview"
            class="sys-template"
            dataview:data="{{ customers }}">
                <option sys:if="$index==0" sys:value="" selected="selected">Select an Item<//option>
                <option sys:value="{{ID}}">{{ Name }}<//option>
    </select>
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }


    I hope this post may be useful for some of you, but I will be so happy to see more Silverlight developers ;)

  • Dag König intervjuar Fredrik Normén om ASP.NET och Silverlight

    Under Techdays 2009 i Västerås så gav Fredrik Nomén hos oss en presentation som bland annat handlade om vad som kommer i nästa version utav Silverlight. Efter presentationen intervjuades han av Dag Köning om eventet och tekniken han pratat om. Ni kan Läs mer...
  • Expression Studio 2.0 är här!

    Robert Folkesson på Microsoft skriver på sin blogg att Expression Studio 2.0 är här. http://blogs.msdn.com/robf/archive/2008/05/04/expression-studio-2-har-sl-ppts.aspx I dagsläget har verktygen stöd för Silverlight 1.0 Läs mer...
  • ASP.Net MVC Framework 2 - A step by step guide to create a simple Web Application

    This post is different from my others, this post is a step by step guide to create a ASP.NET MVC Web application. You will learn to use most of the new features in the Preview 2 version of the ASP.NET Framework, you will also get a basic understanding about how to use LINQ to SQL. This step by step guide shouldn't be used as a "best way to create and design" a web applications by using the ASP.NET MVC Framework, this guide should only be used to get started with the MVC Framework in a fast and easy way and learn different ways to use some of the features shipped with the ASP.Net MVC Framework Preview 2.

    In this step by step guide you are going to create a web based application where you will be able to list and modify customers. This step by step guide will use C# 3.0 and the Northwind database.

     

    1. Open Visual Studio 2008

     

    2. Create a new Project and select the ”ASP.NET MVC Web Project” project and name it to “CustomerApp”. When you press the OK button a dialogue will be displayed to create a Test project for the solution.

    Select “No. do not generate a unit test project”, and press the OK button.

    Note: In this step by step guide you will not use unit test.

     

    3. Open global.asax and walk through the code. In Global.asax you can specify your routes. In this step by step guide you will use the default settings.

    The first thing to do is to create your business objects (your Model) which your Controllers will interact with. In this step by step guide you will use LINQ to SQL, even if it’s not advisable to generate domain models out from a data source you will learn how to do it, only to get up and running fast.

    Note: The business objects belongs to the Model in the MVC pattern.

     

    4. Right click on the Models folder in the Solution Explorer and select Add > New Item.

    Select the “LINQ to SQL Classes” template and name the file to “Northwind.dbml” and press the Add button.

    To create your Model out from a data source you need to add a database connection to Visual Studio. This can be done by using the Server Explorer. In the Solution Explorer, right click on the Data Connections node and select Add Connection. Enter the following information to connect to the Northwind database:

    Server Name: (local)

    Select the Northwind database, test the connection and press OK if you were able to connect to the database.

    A connection to the Northwind database will now be added under the “Data Connections” node in the Server Explorer. Expand the database and the Tables nodes; drag out the Customer table into the design view of the Northwind.dbml file. Save the file. When you save the file, LINQ to SQL will generate a Customer object for you where the columns from the Customers table will be added as properties to the Customer object. LINQ to SQL will also generate a DataContext object (NorthwindDataContext) which you will use later to access your information from the data source. Expand the Northwind.dbml file in the Solution Explorer, double click on the Northwind.designer.cs file and walk through the code. LINQ to SQL uses attributes to map classes to a table.

    Now it’s time to create the Controller which you are going to use in this application.

     

    5. Right click on the Controllers folder in the Solution Explorer and add a new item and select the “MVC Controller Class” template, name it to “CustomerController”.

     

    6. Add a private field of the type NorhwindDataContext to the Controller and name the field to “northwindDataContext”. Set the field to an instance of the NorthwindDataContext object.

    Note: The NorthwindDataContext class will exist in the CustomerApp.Models namespace.


    using
    CustomerApp.Models; namespace CustomerApp.Controllers { public class CustomerController : Controller, IDisposable { private NorthwindDataContext northwindDataContext = new NorthwindDataContext(); //... } }


    7. Make sure the CustomerController implements the IDisposable interface, and implement the Dispose method and make sure it will make a call to the northwindDataContext’s Dispose method. This will make sure the NorthwindDataContext’s Dispose method will be called when the Controller have been used.


    public class
    CustomerController : Controller, IDisposable { private NorthwindDataContext northwindDataContext = new NorthwindDataContext(); //... public void Dispose() { this.northwindDataContext.Dispose(); } }

     

    8. Add an Action method and name it to “List”. The List action will retrieve all Customers from the Customers table of the Northwind database. LINQ to SQL will be used to get all the Customers, order the Customers by CompanyName. When all customers are retrieved make sure to render a View with the name “Customers” (The View will be added later to the project), the View should render a generic list of Customer objects.


    public void
    List() { List<Customer> customers = (from customer in northwindDataContext.Customers orderby customer.CompanyName select customer).ToList(); RenderView("Customers", customers); }


    9. Create a View with the name “Customers” which will render the generic list of Customer objects. To add a View, right click on the Views folder and add a new folder with the name “Customer”. Right click on the Customer folder and add a new item, select the “MVC View Page” template and name the View to “Customers”. Make sure the View data inherits from ViewPage<List<CustomerApp.Models.Customer>>. This will make sure the Model which is passed as an argument to the RenderView method is a known and typed for the View. Open the file Customers.aspx.cs file, replace so the page inherits from ViewPage<List<CustomerApp.Models.Customer>> instead of ViewPage.

     

    public partial class Customers : ViewPage<List<CustomerApp.Models.Customer>>
    {
    }

     

    10. Add code to render a list of Customers to the View, make sure the CustomerID and ContactName are listed. Open the Customer.aspx file, add an inline-code block and use foreach to iterate through the Customers, use the ViewData property to get access to the generic list of Customer objects.


    <%
    @ Page Language="C#" AutoEventWireup="true" CodeBehind="Customers.aspx.cs" Inherits="CustomerApp.Views.Customer.Customers" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title></title> </head> <body> <div> <table> <% foreach ( var customer in ViewData) { %> <tr> <td><%=customer.CustomerID %></td> <td><%=customer.ContactName %></td> </tr> <% } %> </table> </div> </body> </html>

     

    Make sure you have saved all files, compile and run the application. To the end of the URL add “Customer/List” and press enter. The URL will look something like this: http://localhost:<port number>/Customer/List.

    Your application will now make sure the Controller “CustomerController” will be used and also the Action method “List” will be invoked. If everything works, you will now see a list of CustomerID and ContactName.

    Note: The route that was defined in global.asax looks like this:


    {controller}/{action}/{id}


    When you enter a URL like “Customer/List”, the Customer will be the name of the Controller to be used, and the List the name of the Action method to be executed, the id in the route will be empty and will be ignored.

     

    11. Add a new Action method to the CustomerController and name it to “View”. The View method should have one argument with the name ID and the type of String. Make sure the method will use LINQ to SQL to get a Customer from the Customers table which matches the CustomerID passed as an argument. If a customer is not found, make sure an ApplicationExcpetion is thrown, for example with the following message “Can’t find a Customer with the CustomerID <customerID>”. Make sure to render a View with the name “Customer”. The View should render a detail view of a Customer.


    public void
    View(string ID) { Customer customer = (from c in northwindDataContext.Customers where c.CustomerID == ID select c).FirstOrDefault(); if (customer == null) throw new ApplicationException( String.Format("Can't find a Customer with the CustomerID {0}", ID)); RenderView("Customer", customer); }


    12. Add a new View to the Views/Customer folder and name it “Customer”. Make sure the Customer View will inherit form the ViewPage<CustomerApp.Models.Customer> class.


    public partial class
    Customer : ViewPage<CustomerApp.Models.Customer> { }


    13. Open the Customer.aspx page and add inline-code block to show detail information about a Customer, make sure input text elements is used to display the content of the Customer (you will later in the lab update the Customer). Set the name attribute of the input text element to the same name as the property of the Customer object it should display. The CustomerID should be displayed as a simple text, and also be a value of an input hidden field with the name “CustomerID”. The reason why you will use a hidden field with the CustomerID, is because you will later use the MVC Frameworks “databinding” feature.

    Note: The Customer object has several properties, and to make this lab simple you can only use some of them, CustomerID, CompanyName, ContactName and Phone.

    Leave the <Form> elements action attribute empty at the moment.


    <%
    @ Page Language="C#" AutoEventWireup="true" CodeBehind="Customer.aspx.cs" Inherits="CustomerApp.Views.Customer.Customer" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title></title> </head> <body> <form method="post" action=""> <table> <tr> <td>Customer ID:</td> <td><%= ViewData.CustomerID %></td> </tr> <tr> <td>Company name:</td> <td><input type="text" name="CompanyName" value="<%= ViewData.CompanyName %>" /></td> </tr> <tr> <td>Contact Name:</td> <td><input type="text" name="ContactName" value="<%= ViewData.ContactName %>" /></td> </tr> <tr> <td>Phone:</td> <td><input type="text" name="Phone" value="<%= ViewData.Phone %>" /></td> </tr> </table> <input type="hidden" name="CustomerID" value="<%= ViewData.CustomerID %>" /> </form> </body> </html>


    Note: Instead of writing the whole <input> element, you can also use the Html help method:


    <%
    = Html.TextBox("CompanyName", ViewData.CompanyName) %>


    This method will render the following output:


    <input type=”text” size=”20” name=”CompanyName” id=”CompanyName” value=”My Company”/>


    Writing HTML will give you more control over the HTML which will be sent to the client.

    Save your View and build your solution. Start the application and to the end of the URL enter “Customer/View/ALFKI”. The URL will look something like this: http://localhost:<port number>/Customer/View/ALFKI. The Customer controller will be used and its Action method View will be called based on the route in global.asax the value after the specified Action method will be passed as an argument to the specified Action method. The argument is mapped to the argument with the name “ID”.

    If everything works, you will now see a detail view over the Customer with the CustomerID “ALFKI”.

     

    14. Open the Customers.aspx View and make sure the listed CustomerID instead will be a HyperLink, which will have the following URL pattern: “/Customer/View/<CustomerID>”.

     

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Customers.aspx.cs" Inherits="CustomerApp.Views.Customer.Customers" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title></title>
    </head>
    <body>
        <div>
            <table>
            <% foreach ( var customer in ViewData) { %>
                <tr>
                    <td>
                    <a href="/Customer/View/<%=customer.CustomerID %>"><%=customer.CustomerID %></a>
                    </td>
                    <td><%=customer.ContactName %></td>
                </tr>
            <% } %>
            </table>
        </div>
    </body>
    </html>
    


    Note: Instead of writing the <a> element you can use the Html helper method to let it create an ActionLink, for example like this:


    <%=Html.ActionLink<CustomerApp.Controllers.CustomerController>(a => a.View(customer.CustomerID), customer.CustomerID) %>


    Make sure you have saved all files, compile and run the application. To the end of the URL add “Customer/List” and press enter. The URL will look something like this: http://localhost:<port number>/Customer/List.

    If everything works correct, you will now see a list of Customers and the CustomerID will not be a HyperLink. Press on a CustomerID and make sure it will navigate to the Customer View and display detail information about the selected Customer.

    Note: The URL of the HyperLink’s href attribute does not point to a specific file, instead if will point to the Controller and the Action method to be executed when it’s pressed.

     

    15. Add a new Action method to the CustomerController and name it “Remove”. Make sure it takes an ID of type string as an argument. Use LINQ to SQL to remove a Customer with the CustomerID matches the value of the ID argument. To Remove a Customer you first need to retrieve the Customer object, and then use the method DeleteOnSubmit method of the Customers property of the NorthwindDataContext object. The DeleteOnSubmit takes a Customer object as an argument. To execute the Delete, you need to call the SubmitChanges method of the NorthwindDataContext object.

    Note: Both the View method and the Remove method will get a specific Customer by CustomerID, so you can do some refactoring here and use Extract method to extract code which will get the Customer by CustomerID and put it into a new method, and reuse that method both in the View and Remove Action method. To use the Refactoring feature within Visual Studio, mark the code you want to extract to a method and right click on the selected code block. Select Refactoring and Extract method option. Give the new method a name and press the OK button.

    If you decide to use the Refactoring feature you can mark the following code in the View method:


    Customer
    customer = (from c in northwindDataContext.Customers where c.CustomerID == ID select c).FirstOrDefault(); if (customer == null) throw new ApplicationException( String.Format("Can't find a Customer with the CustomerID {0}", ID));


    Righ click on the selected code and select Refactoring and Extract method. Give it the name “GetCustomerByID” and press the OK button. If you get an alert box, just press “Yes” to ignore it.

    You should now have a new method with the name GetCustomerByID which the View method will make a call to.

    In the Remove method, make a call to the GetCustomerByID to get the Customer object to delete. If the application fails to remove a Customer, throw an ApplicationException, for example with the following message: “Can’t remove the Customer <customerID>”. If the remove was successfully, make a call to RedirectToAction to redirect to the Action with the name “List”. This will make sure the List method of the CustomerController will be called.


    public void
    Remove(string ID) { Customer customer = GetCustomerByID(ID); northwindDataContext.Customers.DeleteOnSubmit(customer); try { northwindDataContext.SubmitChanges(); RedirectToAction("List"); } catch (Exception e) { throw new ApplicationException(
    string.Format("Can't remove the Customer {0}", ID), e); } }


    16. Next step is to add a HyperLink to the Customers View, and set the URL to “/Customer/Remove/<CustomerID>” for each Customer. The name of the HyperLink should be “Remove” and can be placed before the CustomerID HyperLink. When you press the HyperLink, the Remove method of the CustomerController will be called and it will remove the Customer.


    <%
    @ Page Language="C#" AutoEventWireup="true" CodeBehind="Customers.aspx.cs" Inherits="CustomerApp.Views.Customer.Customers" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title></title> </head> <body> <div> <table> <% foreach ( var customer in ViewData) { %> <tr> <td> <a href="/Customer/Remove/<%=customer.CustomerID %>">Remove</a>&nbsp; <a href="/Customer/View/<%=customer.CustomerID %>"><%=customer.CustomerID %></a> </td> <td><%=customer.ContactName %></td> </tr> <% } %> </table> </div> </body> </html>


    Build the project and run the application, enter the URL to list Customers (By now you should know the URL). Press one of the Remove HyperLink. You will probably get a SQL Exception about reference problems. You aren’t going to resolve that exception. It will indicate that the remove was executed and in this lab you can be satisfied with that.

     

    17. Add an Update method to the CustomerController, make sure it takes a argument with the name customerID and of type String. Use the customerID to call the GetCustomerByID method to retrieve the Customer from the database. Update the Customer with the new information and use the RedirectToAction method to redirect to the “List” action.

    Note: When using LINQ to SQL we need to get a Customer which is attached to a DataContext, which is the reason why you need to get the Customer. Another reason is also to make it easy to handle concurrency issues etc.

    Use the BindingHelperExtension class’s UpdateFrom method to set the Customer objects properties out from the Reuqest.Form collection.

    Note: The BindingHelperExtension’s UpdateFrom method will get the value out from an input field with the same name as the property of the object passed to the UpdateFrom method and set the value to the object. In the Customer View, there were input fields with the same name as the properties of the Customer object, so those values will be “mapped” to the Customer object you will pass into the UpdateFrom method.


    public void
    Update(string customerID) { Customer customer = GetCustomerByID(customerID); BindingHelperExtensions.UpdateFrom(customer, Request.Form); northwindDataContext.SubmitChanges(); RedirectToAction("List"); }


    Note: Values of input fields on a View which has the same name as the arguments of the Update action method will be passed as a value to the respective argument. So in this case you have an input hidden field with the name CustomerID on the View, the value of that field, will be passed as a value to the Update method. So instead of using the UpdateFrom method of the BindingHelperExtensions, you could use arguments for all input fields.

    If you want to learn more about the BindingHeperExtension class, you can read my post "ASP.Net MVC Framework 2 - The BindingHelperClass (UI-mapper)".

     

    18. Open the Customer View and change the <form> element’s action argument to call the Update method of the CustomerController “/Customer/Update”. Add a Submit button to the View and give it the text value “Update”.

     

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Customer.aspx.cs" Inherits="CustomerApp.Views.Customer.Customer" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form method="post" action="/Customer/Update">
            <table>
                <tr>
                    <td>Customer ID:</td>
                    <td><%= ViewData.CustomerID %></td>
                </tr>
                <tr>
                    <td>Company name:</td>
                    <td><input type="text" name="CompanyName" value="<%= ViewData.CompanyName %>" /></td>
                </tr>
                <tr>
                    <td>Contact Name:</td>
                    <td><input type="text" name="ContactName" value="<%= ViewData.ContactName %>" /></td>
                </tr>
                <tr>
                    <td>Phone:</td>
                    <td><input type="text" name="Phone" value="<%= ViewData.Phone %>" /></td>
                </tr>
    
            </table>
    
            <input type="hidden" name="CustomerID" value="<%= ViewData.CustomerID %>" />
            
            <input type="submit" value="Update" />
        </form>
    </body>
    </html>
    

     

    Build the solution and run the application. Enter the URL to list all Customers “Customer/List” and select a Customer from the list. Update the Customer and press the Update button. When the Update button is pressed, the customer will be updated and you will be redirected to the List action so the List of Customer will be displayed.

    Note: Instead of using an input field with the name CustomerID, you could also set the action attribute to “/Customer/Update/<the customerID>”.

    The last thing to do now is to create an ActionFilter for the Remove action method. You only want to make sure the user with the name “student” will make a call to the Remove action method.

     

    19. Add a new class to the Model folder. Name it “AuthorizedFilterAttribute”. Make sure it inherits from the base class ActionFilterAttribute. The ActionFilterAttribute is located in the namespace “System.Web.Mvc”.

    The ActionFilter can be used to perform pre- and post action. You can use FilterAction to cancel an execution of an Action method, or you can use it to log information etc. The ActionFilter AuthorizedFilter will be used to only make sure the user “student” has access to the Remove method, if not a SecurityException will be thrown.

    To implement code for a pre action, override the OnActionExecuting method of the ActionFilterAttribute class. To perform a post action, override the OnActionExecuted method. To get the current user you can use the filterContext arugment’s HttpContext property and its User.Identity.name property. You don’t need to cancel the action of the AuthorizedFilter when you throw an exception. To cancel, you can set the filterContext Cancel property to true.


    using
    System.Security; namespace CustomerApp.Models { public class AuthorizedFilter : ActionFilterAttribute { public override void OnActionExecuting(FilterExecutingContext filterContext) { if (filterContext.HttpContext.User.Identity.Name != "student") throw new SecurityException(
    "You aren't allowed to call this action!"); } } }


    Apply the AuthorizedFilter to the Remove method.


    [AuthorizedFilter] public void Remove(string ID)


    Note: If you want to make sure the filter will be used for every Action method of a Controller, you can add the attribute to the Controller’s class.

    If you want to test if the filter works, add a breakpoint to the OnActionExecuting method and run the application in debug mode. List the Customers and press the Remove HyperLink, or you can simply run the application, list the customers and press the Remove Hyperink, it should probably give you a SecurityException. If you want to learn more about ActionFilter, you can read my post "ASP.Net MVC Framework 2 - Interception and ceating a Role Action Filter".

    In this step by step guide you have learned how to create a simple application by using the ASP.Net MVC Framework. You have learned how to create a Controller, Views and also ActionFilter to intercept pre actions to a Action method.

  • ASP.Net MVC Framework - Using Forms Authentication

    This post is about using Forms Authentication with the MVC Framework. I only want to let you know that this is a simple and fast “hack” only to show a simple solution to get it work. I decided to use the Membership feature for the validation of user name and password.

    The first thing we need to do is to enable Forms Authentication and specify a Login Url, I decided to use the Login URL in a MVC “friendly” format:

     

    <authentication mode="Forms">
       <forms loginUrl="Login/Login"></forms>
    </authentication>

     

    The value of the loginUrl has some redundant value, Login/Login, but this is because the first Login points to the LoginController and the last Login to the Action method Login in the LoginController.

    The next thing we need to do is to make sure anonymous users don’t have access to our site, this is done by adding the authorization section to the web.config:

     

    <authorization>
         <deny users="?"/>
    </authorization>

     

    If a user now try to get access to our site, he/she will be redirected to the ”Login/Login” URL, which will by using the default URL format in the RouteTable:


    Url = "[controller]/[action]"


    Call the Login Action method of the LoginController.

    In the Views folder we can add our Login View (We will look at the LoginController later in this post).


    /Views/Login/Login.aspx


    The following is a simple Login View:

     

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="MvcApplication4.Views.Login.Login" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml" >

    <head runat="server">
        <title>Untitled Page</title>
    </head>

    <body>
        <div>
            <% if (this.ViewData.ContainsDataItem("LoginFaild")) { %>
            <div style="color:Red"><%=this.ViewData["LoginFaild"] %></div>
            <% } %>

            <form method="post" action="/Login/Login">
            User name: <input type="text" name="userName" /><br />
            Password:&nbsp; <input type="password" name="password" /><br /><br />
            <input type="submit" value="Login" />
            </form>

        </div>

    </body>
    </html>

     

    If the ViewData in will contain an data item with the name LoginFaild it will display the value added to the LingFaild data item, in this case a message like “Login faild….”

    The <form> element’s action method will make sure the LoginController’s Login method will be called when the user hit the Login submit button. As you can see the same URL specified for the FormsAuthentication’s loginUrl is used here. It’s because the <location> section in the web.config will not work with the MVC Framework, so if we try to use another URL like “/Login/Authenticate” etc, we will be redirected to the “Login/Login” URL because the <authorization> section denies all anonymous user to access any URL in our app, besides the specified loginUrl.

    The username and password input fields values will be passed as arguments to the Login action of the LoginController. Here is the implementation of the LoginController and its Login action:

     

        public class LoginController : Controller
        {
            [ControllerAction]
            public void Login(string userName, string password, string ReturnUrl)
            {
                if (this.IsValidLoginArgument(userName, password))
                {
                    if (Membership.ValidateUser(userName, password))
                        this.RedirectFromLoginPage(userName, ReturnUrl);
                    else
                        this.ViewData["LoginFaild"] = "Login faild! Make sure you have entered the right user name and password!";
                }

                RenderView("Login");
            }

            private void RedirectFromLoginPage(string userName, string ReturnUrl)
            {
                FormsAuthentication.SetAuthCookie(userName, false);

                if (!string.IsNullOrEmpty(ReturnUrl))
                    Response.Redirect(ReturnUrl);
                else
                    Response.Redirect(FormsAuthentication.DefaultUrl);
            }

            private bool IsValidLoginArgument(string userName, string password)
            {
                return !(string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(password));
            }
        }

     

    As you can see the Login action method takes three arguments, the userName, password and ReturnUrl. The value for the userName and password, will came from the input fields located on the Login View, the ReturnUrl is a query string used by the FormsAuthentication, so the value of the ReutnrUrl argument will came from the ReturnUrl query string. If we have input fields with the same name as the arguments of an action method in the View, the value of the fields will be “mapped” to the argument of the action method with the same name as the field, the same thing with query strings.

    Note: There is a bug in the current CTP of the MVC Framework, so the value of a query string will not always be passed to the action method’s arguments.

    The firs lines of code in the Login action method will check if the username and password aren’t empty. The check is used to make sure the Login View will be displayed when FormsAuthentication will redirect to the LoginURL “Login/Login”. If a user tries to enter and URL and the user is an anonymous user (Not authenticated), the Login Action method of the LoginController will be executed. In this case the username and password argument will be null, and the Login View will be rendered. When the Login view is displayed, the user can now enter a username and a password and hit the Login button. When he/she have done that, the LoginController’s Action method will be called, and this time the username and password will not be empty, so the Membership feature will validate the user, if he/she is valid, the Authentication Cookie used by the FormsAutehtnicon will be set, and the user will be redirected to the URL he/she have requested, and the user is now authenticated and have access to the site.

    If the user enter a invalid user name or password, the Login View will be displayed and the “Login faild!.....” text added to the ViewData will be displayed.

    This was a simple solution to use the Forms Authentication, there are some other options where can simply create our own RouteHandler which can handle the authentication check and redirect to a login View, or we can handle it on a Controller level. I will probably write a blog post about that later.

    I have another post about the MVC Framework and security: http://weblogs.asp.net/fredriknormen/archive/2007/11/25/asp-net-mvc-framework-security.aspx. The post is created on the pre-CTP of the MVC Framework, so have that in mind, but basically the solution is the same.

  • ASP.Net MVC Framework - Assign a ViewFactory to a Controller

    I have seen several posts about how to assign a ViewFactory to a Controller, most of the case the code looks like this:


    public class HomeController : Controller
    {
        public HomeController()
        {
           this.ViewFactory = new MyViewFactory();
        }
    }

    The ViewFactory is assigned into the constructor of a Controller. If we want to use the same ViewFactroy for all Controllers in out app, we can for example create a new base class for our Controller:


    public class MyController : Controller
    {
         public MyContoroller()
         {
             this.ViewFactory = new MyViewFactory();
         }
    }

    public class HomeController : MyController
    {
         public HomeController() : base()
         {
         }
    }


    I’m a more fan of making this configurable via a configuration file, to map a ViewFacotry to a Controller, this can for example be done with Spring.Net:


    <?xml version="1.0" encoding="utf-8" ?>
    <objects xmlns="http://www.springframework.net"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">

      <object id="HomeController" type="MvcApplication.Controllers.HomeController, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <constructor-arg name="viewFactory" ref="MyFactory"/>
      </object>

      <object id="MyFactory" type="MyViewFactory, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
     
    </objects>


    Note: You can read my other post about using Spring.Net to create Controllers.


    public class HomeController : Controller
    {
       public HomeController() : this(new DefaultViewFactory ()) {}
       public HomeController(IViewFactory viewFactory)
       {
           this.ViewFactory = viewFactory;
       }
     }


    There a so many different solutions to set a ViewFactory during creation of a Controller, for example by creating an own ControllerFactory, the following code is from my post about writing your own IRoutHandler:


    public IController CreateController(RequestContext context, string controllerName)
     {
            //I should refactor out the following two lines to not only make sure to make the creation of the ObjectFactory once.
            IResource input = new FileSystemResource(context.HttpContext.Request.MapPath("objects.xml"));
            IObjectFactory factory = new XmlObjectFactory(input);

            IController controller = (IController)factory.GetObject(controllerName, typeof(IController));

            if (typeof(Controller).IsAssignableFrom(controller.GetType()))
            {
              N2MVCConfigurationHandler config = ConfigurationManager.GetSection("Nsquared2/N2MVCSection") as N2MVCConfigurationHandler;
              ((Controller)controller).ViewFactory = N2ViewFactoryBuilder.GetViewFactory(config.ViewFactory);
            }

            return controller;
    }


    What this code does, is to see if the Controller is assigned from the Controller class, ugly code, I know, but it’s only the Controller base class that has the ViewFactory property, and we can create a Controller which only implements the IController interface, and this interface don’t have the ViewFactory property.

    The code above will get the ViewFactory from a configuration file:


    <Nsquared2>
        <N2MVCSection
            controllerFactory="MvcApplication.Models.N2ControllerFactory, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
            viewFactory="MvcApplication.Models.N2ViewFactory, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    </Nsquared2>


    In this example I assume that every Controller use the same ViewFactory, and that may not always be the case, so in that case the earlier example by using Spring.Net an inject a ViewFactory would be better use, or creating a new ControllerFactory which will map a Controller to a ViewFactory, so when the ControllerFactory creates the Controller, the factory could look up in a config file to see, which ViewFactory should be used for the specific Controller, for example:


    <Controllers defaultViewFactory=”myViewFactory”>
        <add name=”HomeController” ViewFactory=”myOwnViewFactory” …>
    </Controllers>


    What is the main goal with these kinds of solutions? Well we can now simply change the ViewFactory for our Controller without recompiling our code, and we will also remove the dependency from a Controller and the ViewFactory by not hard code the instantiation within the Controllers. I prefer Interface Driven Programming and the use of factories, and the possibility to easy configure the use of classes etc in a configuration file. That is why I also loved the Provider based solution that was shipped with ASP.Net 2.0, like the Membership and Roles features etc.

  • ASP.NET MVC Framework - Ajax

    Take look at Nikhil's blog about how AJAX can be used with the ASP.Net MVC Framework:

    http://www.nikhilk.net/Ajax-MVC.aspx

  • ASP.Net MVC Framework - Handling Exception by using an Attribute

    Note: This is based on the pre-CTP version of the ASP.Net MVC Framework and it is in an early stage.

    I got a comment on one my previous post “ASP.Net MVC Framework - Exception Handling” that the Rescue style MonoRail is a nice solution to handle Exception thrown within a Controller. In this post I have added something similar to as the Rescue feature of MonoRail (at least I think so ;)) where an attribute can be added to a Controller or on an Action. The attribute can be used to specify which View that should be rendered when an exception is thrown. We can also specify different Views based on different exceptions. Something I also added to the code is to specify something I will call an ExceptionHanlder. The ExceptionHandler is a pre handler which will be executed before the specified exception View should be rendered. The idea of the ExceptionHandler is to log exception etc.

    By using an Attribute (ExceptionHandlerAttribute) on a Controller or an Action method, we don’t need to override the OnError method of the base Controller class. I made this solution simple by only creating my own custom Controller class (N2Controller) and override the OnError method of the Controller. In this method do my exception handling process. Before I show the code I will show you how we can use the ExceptionHandlerAttribute.

    The ExceptionHandlerAttribute has three argument, exceptionHandler, viewName, exceptionType. The exceptionHandler argument is where we pass the type of the pre ExceptionHandler we want to use when an Exception is thrown. The viewName is the name of the View to render the exception information (In this sample code the view specified must be located in the Views/Exceptions folder). The exceptionType takes a type of an Exception. If we only want to make sure the Exception Handler should only handle a specific exception thrown within the Controller or Action method, we use the exceptionType argument.

    Here is an example of different use of the ExcetpionHandlerAttribute, it can be used on both the Controller’s Class and Action methods:

    1. [ExceptionHandler(“Error”);
    2. [ExceptionHandler("Error", typeof(DivideByZeroException))]
    3. [ExceptionHandler(typeof(ExceptionHandler), "Error")]
    4. [ExceptionHandler(typeof(ExceptionHandler), "Error", typeof(DividedByZeroException))
    5. [ExceptionHandler(typeof(ExceptionHandler))
    6. [ExceptionHandler(typeof(ExceptionHandler), typeof(DividedByZeroException))

    1 Will who the Error view when an exception is thrown.
    2 Will only show the Error View is the exception thrown is of type DividedByZeroException.
    3 Will Show the Error View and use a pre ExceptionHandler before the View is rendered.
    4 Will Show the Error View only if the DividedByZeroException is thrown, and use a pre ExceptionHandler before the View is rendered.
    5 Will use the ExceptionHandler when an exception is thrown.
    6 Will use the ExceptionHandler if the DividedByZeroException is thrown.

    To use ExceptionHandler we simply create a Class that implements the IExceptionHandler interface.

    public interface IExceptionHandler
    {
        void PreExceptionHandling(string actionName, Exception exception);
    }

    In the PreExceptionHandling method we could for example put logic to log an exception. The PreExceptionHandling method will be called before a View will be rendered (A View will not be rendered if it’s not specified).

    Here is an example how the ExceptionHandlerAttribute can be used:

    [ExceptionHandler("DefaultError", typeof(DivideByZeroException))]   <- Will not be used
    public class HomeController : N2Controller
    {
       [ControllerAction, ExceptionHandler("Error")]  <- Will be used
       public void Index()
       {
        
       }
    }

    The attribute specified on the action method will have more to sat than the attribute on the Controller’s class.

    [ExceptionHandler("DefaultError", typeof(DivideByZeroException))]   <- Will be used
    public class HomeController : N2Controller
    {
        [ControllerAction, ExceptionHandler("Error",typeof(IndexOutOfRangeException),typeof(ArgumentException))]  <- Will be used
        public void Index()
        {
            ...
        }
    }

    In the code above, both Exception Handler will be used. For example, if the Action method throws a DidivedByZeroException the DefaultError View will be rendered. But if the IndexOurOfRangeException or ArgumetnException is thrown the Error View will be displayed.

    Here is the implementation of my custom Controller:

    public class N2Controller : Controller
    {
        protected override bool OnError(string actionName, System.Reflection.MethodInfo methodInfo, Exception exception)
        {
            ArrayList attributes = this.GetExceptionHandlerAttribute(methodInfo);

            foreach (ExceptionHandlerAttribute attribute in attributes)
            {
               foreach (Type exceptionType in attribute.ExceptionTypes)
               {
                  if (exceptionType.IsAssignableFrom(exception.InnerException.GetType()))
                  {
                     if (attribute.ExceptionHandler != null)
                        ((IExceptionHandler)Activator.CreateInstance(attribute.ExceptionHandler)).PreExceptionHandling(actionName, exception);

                     if (!string.IsNullOrEmpty(attribute.View))
                         RenderView("~/Views/Exceptions/" + attribute.View + ".aspx", exception);
                        
                     return true;
                  }
               }
           }

           return false;
       }

       private ArrayList GetExceptionHandlerAttribute(System.Reflection.MethodInfo methodInfo)
       {
           ArrayList attributes = new ArrayList();

           attributes.AddRange(
                               methodInfo.GetCustomAttributes(
                                     typeof(ExceptionHandlerAttribute),
                                     false));

           attributes.AddRange(
                               this.GetType().GetCustomAttributes(
                               typeof(ExceptionHandlerAttribute),
                               true));
                
           return attributes;
        }
    }

    The code above is only on a prototype stage and. If no ExceptionHandlerAttribute is specified for an Action or Controller, the original ASP.Net MVC Frameowork’s OnError behavior will be used.

  • ASP.Net MVC Framework - Create your own IRouteHandler

    In my previous post I wrote about how we can create our own IControllerFactory for the ASP.Net MVC Framework; in this post I will show you how we can create our own IRouteHandler.

    The RouteHandler in this post will replace the IControllerFactory with my own Controller Factory, and also set a default ControllerFactory and a ViewFactory specified in the web.config. This is something the current preview bits of the MVC Framework can’t.

    It’s quite easy to create our own RouteHandler, we only need to implement the IRouteHandler interface and implement the GetHttpHandler method ;)

    public class N2MVCRouteHandler : IRouteHandler
    {
       public IHttpHandler GetHttpHandler(RequestContext requestContext)
       {
           N2MVCHandler handler = new N2MVCHandler();
           handler.RequestContext = requestContext;
           return handler;
        }
    }

    The GetHttpHandler returns an IHttpHandler. I have created my own IHttpHandler (N2MVCHandler). The N2MVCHandler inherits the MvcHandler shipped with the ASP.Net MVC Framework. The reason why I inherit the MvcHandler is because I don’t want to rewrite stuff that isn’t necessarily. In my IHttpHandler I override the ProcessRequest method and added my own code to create a Controller for the current request.

    public class N2MVCHandler : MvcHandler
    {
        protected override void ProcessRequest(IHttpContext httpContext)
        {
           if (this.RequestContext == null)
              throw new InvalidOperationException("No RequestContext");

           string controllerName = this.GetRequiredString(this.RequestContext.RouteData, "controller");

           IController controller = this.CreateController(controllerName);
           ControllerContext controllerContext = new ControllerContext(base.RequestContext, controller);

           controller.Execute(controllerContext);
        }

        private IController CreateController(string controllerName)
        {
           N2MVCConfigurationHandler config = ConfigurationManager.GetSection("Nsquared2/N2MVCSection") as N2MVCConfigurationHandler;

           IN2ControllerFactory controllerFactory = N2ControllerFactoryBuilder.GetControllerFactory(config.ControllerFactory);

           IController controller = controllerFactory.CreateController(base.RequestContext, controllerName);

           return controller;
        }
    }

    In the ProcessRequest method I also create a Controller for the current request by calling the CreateController method. The CreateController method in the N2MvcHandler instantiates a ControllerFactroy which is specified in the web.config file. When the factory is instantiated I make a call to its CreateController method to create an instance of the Controller.

    In this example I have replace the IControllerFactory with my own interface, the reason is that I want to pass the name of the Controller and not the Type. The MvcHandler shipped with the MVC Framework will in the ProcessRequest method try to locate the Controller within the references assemblies and create the Type of the Controller and pass it to the IController.CreateController method. But I think it’s up to the ControllerFactory to look up the Controller. Here is my IControllerFactory interface:

    public interface IN2ControllerFactory
    {
        IController CreateController(RequestContext context, string controllerName);
    }

    Note: You can still create your own ControllerFactory, but you need to implement my interface instead of IControllerFactory, and you don’t need to make a call to the ControllerBuilder.Current.SetDefaultControllerFactory method in the Application_Start event in Global.asax to specify which ControllerFactory you want to use, instead you can do it in web.config. You will see how to do it later in this post.

    I decided to use Spring.Net in this post also to create my Controller within the ControllerFactory.

    public class N2ControllerFactory : IN2ControllerFactory
    {
        public IController CreateController(RequestContext context, string controllerName)
        {
            IResource input = new FileSystemResource(context.HttpContext.Request.MapPath("objects.xml"));
            IObjectFactory factory = new XmlObjectFactory(input);

            IController controller = (IController)factory.GetObject(controllerName, typeof(IController));

            if (typeof(Controller).IsAssignableFrom(controller.GetType()))
            {
              N2MVCConfigurationHandler config = ConfigurationManager.GetSection("Nsquared2/N2MVCSection") as N2MVCConfigurationHandler;
              ((Controller)controller).ViewFactory = N2ViewFactoryBuilder.GetViewFactory(config.ViewFactory);
            }

            return controller;
        }
    }

    It's the ControllerFactory's responsibility to create and fill the Controller with all it information it needs. In this case the ViewFactory.

    The IViewFactory is used to create a factory which has the responsibility to create a View. Because a Controller don’t need to implement the Controller base class I will in my code use a "code policy". I will check if the created Controller inherits the Controller class, if so I will create a IViewFactory and inject it to the Controller. The IController interface don’t have the ViewFactory property, it’s something we will get from the Controller base class. 

    If we take a look again at the CreateContoller method in the IHttpHandler (N2MVCHandler), we can see how I get the ControllerFactory from the web.config

    private IController CreateController(string controllerName)
    {
        N2MVCConfigurationHandler config = ConfigurationManager.GetSection("Nsquared2/N2MVCSection") as N2MVCConfigurationHandler;

        IN2ControllerFactory controllerFactory = N2ControllerFactoryBuilder.GetControllerFactory(config.ControllerFactory);

        IController controller = controllerFactory.CreateController(base.RequestContext, controllerName);

        return controller;
    }

    I use some other helper classes in my code to create an instance of the Controller- and ViewFactory specified in the web.config, the code of the helper methods is not relevant for this post. When the public CTP of the MVC Framework is released, you can drop me an e-mail and I can send you my source code.

    Here is the config section in the web.config where a conrollerFactory is specified and also a viewFactory. So we can now easy specify our ControllerFactory and ViewFactory in web.config.

    <Nsquared2>
        <N2MVCSection
            controllerFactory="MvcApplication.Models.N2ControllerFactory, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
            viewFactory="MvcApplication.Models.N2ViewFactory, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    </Nsquared2>

    By using the Route objects’ RouteHandler, we can easy change the RouteHandler the MVC Framework should use.

    RouteTable.Routes.Add(new Route
    {
        Url = "[controller]/[action]/[id]",
        Defaults = new { action = "Index", id = (string)null },
        RouteHandler = typeof(N2MVCRouteHandler)
    });

    In this post you have seen how we can create our own IRouteHandler, and also replace the ControllerFactory with our own.

  • ASP.Net MVC Framework - Create your own IControllerFactory and use Spring.Net for DI

    In this post I’m going to show you how we can easy create our own IControllerFactory for the ASP.Net MVC Framework and use Spring.Net to create our Controllers. I will in this post also use the Spring.Net Dependency Injection support to pass a Repository that should be used by our Controller.

    To create a ControllerFactory we need to implement the IControllerFactory interface. This interface has one method, CreateController. This method takes two arguments a RequestContext and the Type of the controller to create. The MVC Framework will locate our Controller and passed in the Type to our IControllerFactory. This is something I don’t like; I want to be responsible to look up my own Controller in my way, not let the MVC Framework locate the type in their way, It will only creates some magic. If we want to change this behavior in the current preview of the MVC Framework we can create our own RouteHandler, but this is out of topic in this post. When we use Spring.Net to get an instance of an object we use a string, so we can use the Name property or the Type passed as an argument to our CreateController method to get the name of the Controller.

    Here is the implementation of an IControllerFactory which will use Spring.Net to instantiate a Controller:

    public class SpringControllerFactory : IControllerFactory
    {
            public IController CreateController(RequestContext context, Type controllerType)
            {
                IResource input = new FileSystemResource(context.HttpContext.Request.MapPath("objects.xml"));
                IObjectFactory factory = new XmlObjectFactory(input);

                return (IController)factory.GetObject(controllerType.Name);
            }
     }

    In this code I use the XmlObjectFactory to create objects out from a specified XML file, in this case the "objects.xml" file. Within the objects.xml file we can specify the objects we want to be instantiated when we are using the Spring.Net framework. I will later in this post show the content of the "objects.xml" file. But first we can take a look at the implementation of a Controller.

    public class HomeController : Controller
    {
            IHomeRepository _homeRepository;

            public HomeController() : this(new HomeRepository()) {}

            public HomeController(IHomeRepository homeRepository)
            {
                this._homeRepository = homeRepository;
            }

            [ControllerAction]
            public void Index()
            {
                CompanyInfo companyInfo = this._homeRepository.GetCompanyInfo();
                RenderView("Index", companyInfo);
            }

            [ControllerAction]
            public void Contact()
            {
                CompanyInfo companyInfo = this._homeRepository.GetContact();
                RenderView("Contact", companyInfo);
            }

            [ControllerAction]
            public void About()
            {
                CompanyInfo companyInfo = this._homeRepository.GetCompanyInfo();
                RenderView("About", companyInfo);
            }
     }

    I decided to modify the HomeController created by the "MVC Web Application" template. My modification of the HomeController was to remove the code to fill a CompanyInfo object, and instead put it into a Repository with the name "HomeRepostory".

    public class HomeRepository : IHomeRepository
    {
            public CompanyInfo GetCompanyInfo()
            {
                CompanyInfo companyInfo = new CompanyInfo();
                companyInfo.CompanyName = "Your company name here";
                return companyInfo;
            }

            public CompanyInfo GetContact()
            {
                CompanyInfo companyInfo = new CompanyInfo();
                companyInfo.CompanyName = "Your company name here";
                companyInfo.AddressLine1 = "Company address Line 1";
                companyInfo.AddressLine2 = "Company address Line 2";
                companyInfo.City = "City";
                companyInfo.State = "State";
                companyInfo.Zip = "00000";
                companyInfo.Email = "
    email@yourcompany.com
    ";
               
                return companyInfo;
            }
     }

    The HomeRepository implements an interface with the name IHomeRepository. I made this design decision so I can easy mock my repositories. If we take a look at the HomeController again we can see that I have added a private field with the type IHomeRepository and also created a constructor which will take an IHomeRepository as an argument. The reason to this design is because of testability. For example if I mock the HomeRepository I can in my Test project easy inject the mock object for the Repository to my Controller.

    MockHomeRepository mockHomeRepository = new MockHomeRepository();
    HomeController homeController = new HomeController(mockRepository);

    homeController.About();

    Assert.AreEqual(((CompanyInfo)homeController.ViewData).companyInfo, "Your company name here");

    By using a constructor which will take our IHomeRepository as an argument, we can also do a Constructor Injection by using the Sprin.Net framework, and that is exactly what I’m going to do in this post.

    Now when we have our Controller and Repository we need to setup the “objects.xml” file with our objects and also setup a Constructor Injection. Here is the content of the “objects.xml” file:

    <?xml version="1.0" encoding="utf-8" ?>
    <objects xmlns="
    http://www.springframework.net"
                    xmlns:xsi="
    http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="
    http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">

      <object id="HomeController" type="MvcApplication.Controllers.HomeController, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <constructor-arg name="homeRepository" ref="HomeRepository"/>
      </object>

      <object id="HomeRepository" type="MvcApplication.Models.Repositories.HomeRepository, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
     
    </objects>

    The Object element specifies our objects we want to create with the Spring.Net framework. The id attribute specifies the name of the object and the type specifies the type of our object. When we use the Spring.Net framework’s GetObject method we pass in the name of the object to create, the GetObject method will look for an object with the id attribute set to the name, and instantiate the specified type. By using the <constructor-arg> element we can specify what object that should be passed into our constructor when we create the object. In this case I have specified the HomeRepository object.

    When our IControllerFactory now will create our HomeController, Spring.net will create an instance of our Controller and do a constructor injection and pass in the HomeRepository object to the constructor and return our controller.

    Now when we are done with all the implementation of our IControllerFactory, Controller and Repository we need to make sure our IControllerFactory should be used. This can be done in the Global.asax’s Application_Start event by using the SetDefaultControllerFactory method:

    protected void Application_Start(object sender, EventArgs e)
    {
       ControllerBuilder.Current.SetDefaultControllerFactory(
                               typeof(MvcApplication.Models.Infrastructure.SpringControllerFactory));
       ...
    }

    When we now run our application, our IControllerFactory will be used and all the creating of our Controllers will be handled by the Spring.Net framework.


Den här bloggen

Syndication