Kurser och utbildning av IT-proffs och systemutvecklare

i Sök

Cornerstones utvecklarblogg

Alla Taggar

All Tags » ASP.NET MVC   (RSS)

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

    The latest source code for my Fluent-API to add Action Filters to Controllers is no available here. If you haven’t read about my test project, you can find the other posts here:

    Fluent-API to add ActionFilters to Controller in ASP.NET MVC

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

    To use the Fluent-API, you only need to change the Default Controller factory to the ActionFilterConfigControllerFactory, then just configure your controller within the Global.asax. Here is an example where the OutputCache is added to two Action methods, and also an example how to add one or many ActionFilters to one Action method. You can also add Action Filters on a Controller level so they will be used on every Action methods. If you also want a specific Action Filter for all Controllers, you can just Configure the Controller type and add the filters to it.

    protected void Application_Start()
    {
                ControllerBuilder.Current.SetControllerFactory(typeof(ActionFilterConfigControllerFactory));
    
                RegisterRoutes(RouteTable.Routes);
                RegisterActionFilters();
    }
    
            
    private void RegisterActionFilters()
    {
                // Add OutputCache to all Action Methods for all Controllers which inherits the Contoller class
                //ConfigActionFilter.ConfigController<Controller>()
                //  .AddFilterToController(new OutputCacheAttribute() { Duration = 10, VaryByParam = "none" });
    
                ConfigActionFilter.ConfigController<HomeController>()
                                .AddFilterToController(new HandleErrorAttribute())
                                .AddFilterToActions(new OutputCacheAttribute() { Duration = 10, VaryByParam = "none" },
                                                    c => c.About(),
                                                    c => c.Index());
    
                ConfigActionFilter.ConfigController<AccountController>()
                                  .AddFilterToAction(c => c.LogOn(), 
    new HandleErrorAttribute(),
    new MyCustomFilterAttribute()); }

    .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 is as I wrote only a test project, and I do like the idea to add cross-cutting concerns by not adding attributes directly to the Controllers source code.

  • 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


Den här bloggen

Syndication