One of the advantages to using a custom IFilterProvider is how you gain total control over the instantiation of action filters in ASP.NET MVC 3. When you combine an IoC container with a filter provider and a custom IDependencyResolver (see Brad's posts for details), then all the pieces fall into place. Building on the code from the last post, we can change our filter provider to utilize Ninject for action filter instantiation.
public class ConfiguredFilterProvider : IFilterProvider { private readonly IKernel _kernel; public ConfiguredFilterProvider(IKernel kernel) { _kernel = kernel; } public IEnumerable<Filter> GetFilters( ControllerContext controllerContext, ActionDescriptor actionDescriptor) { var filters = FiltersSettings.Settings.Filters; foreach (var filter in filters.Cast<FilterAction>()) { var filterType = Type.GetType(filter.Type); yield return new Filter( _kernel.Get(filterType), FilterScope.Global, order:null ); } } }
Using Ninject to create the filters makes it trivially easy to inject dependencies into the filter. In case you didn't know, action filters in MVC 3 no longer need to derive from an attribute derived base class. All you need is a class that implements one or more of the action filter interfaces.
public class LoggingFilter : IActionFilter, IExceptionFilter, IResultFilter, IAuthorizationFilter { public LoggingFilter(ISomeDependency dependency) { } // ... }
If you are using Ninject, you'll also want to use a custom IDependencyResolver based on Ninject. It's easy to write your own, or you can add one already implemented for you from the NuGet gallery.
With the custom dependency resolver in place, you no longer need to register your filter provider in the FilterProvider.Providers collection. Instead, you can just register the provider as another service in the container.
public static class AppStart_NinjectMVC3 { public static void RegisterServices(IKernel kernel) { kernel.Bind<IFilterProvider>() .To<ConfiguredFilterProvider>(); kernel.Bind<ISomeDependency>() .To<SomeDependency>(); } public static void Start() { IKernel kernel = new StandardKernel(); RegisterServices(kernel); DependencyResolver.SetResolver( new NinjectServiceLocator(kernel)); } }
In previous versions of MVC, filters were notoriously difficult for injection, and mostly inflexible. MVC 3 is a great improvement.
In MVC 3 you can implement an IFilterProvider to create and feed action filters to the MVC runtime. Assuming you have the configuration classes in place from the last post, you can create a custom filter provider to add action filters to the MVC pipeline.
public class ConfiguredFilterProvider : IFilterProvider { public IEnumerable<Filter> GetFilters( ControllerContext controllerContext, ActionDescriptor actionDescriptor) { var filters = FiltersSettings.Settings.Filters; foreach (var filter in filters.Cast<FilterAction>()) { var filterType = Type.GetType(filter.Type); yield return new Filter( Activator.CreateInstance(filterType), FilterScope.Global, order:null ); } } }
Notice a filter provider receives context parameters it can use to determine if it should create a filter, or not. In this case we are creating global filters from whatever we find in the web.config file, so the parameters are left untouched.
To plug your filter provider into the MVC runtime, you'll need to execute a bit of code during application start up:
FilterProviders.Providers.Add(new ConfiguredFilterProvider());
In the next post we'll combine the filter provider with Ninject to take advantage of MVC 3's new dependency resolution features.
ASP.NET MVC 3.0 introduces global action filters - an easy way to apply an action filter to every action in an MVC application. All you need to do is register the filters during application startup:
protected void Application_Start()
{
...
GlobalFilters.Filters.Add(new HandleErrorAttribute()); GlobalFilters.Filters.Add(new FooFilter()); GlobalFilters.Filters.Add(new BarFilter());... }
But what if you wanted to add (or remove) filters through configuration?
<configSections> <section name="filters" type="ConfigurableFilters.FiltersSettings, AssemblyName "/> </configSections> ... <filters> <add type="System.Web.Mvc.HandleErrorAttribute, System.Web.Mvc..." /> <add type="ConfigurableFilters.BarFilter, AssemblyName" /> <add type="ConfigurableFilters.FooFilter, AssemblyName" /> </filters>
In that case you'll need a ConfigurationElement.
public class FilterAction : ConfigurationElement { [ConfigurationProperty("type", IsRequired = true, IsKey = true)] public string Type { get { return base["type"] as string; } set { base["type"] = value; } } }
And a ConfigurationElementCollection.
public class FilterActionCollection : ConfigurationElementCollection { protected override ConfigurationElement CreateNewElement() { return new FilterAction(); } protected override object GetElementKey(ConfigurationElement element) { return ((FilterAction) element).Type; } }
And a ConfigurationSection.
public class FiltersSettings : ConfigurationSection { public static FiltersSettings Settings { get { var section = ConfigurationManager.GetSection("filters") as FiltersSettings; return section ?? new FiltersSettings(); } } [ConfigurationProperty("", IsDefaultCollection = true)] public FilterActionCollection Filters { get { return base[_filtersProperty] as FilterActionCollection; } set { base[_filtersProperty] = value; } } private readonly ConfigurationProperty _filtersProperty = new ConfigurationProperty( null, typeof (FilterActionCollection), null, ConfigurationPropertyOptions.IsDefaultCollection); }
One way to apply the configured filters is to use the following code during application startup:
var filters = FiltersSettings.Settings.Filters; foreach (var filter in filters.Cast<FilterAction>()) { var filterType = Type.GetType(filter.Type); GlobalFilters.Filters.Add(Activator.CreateInstance(filterType)); }
Another approach is to implement a filter provider. We'll take a look at the provider approach tomorrow.
ASP.NET MVC has action filters, while ASP.NET has HTTP modules. Inside their respective processing pipelines, these abstractions serve similar purposes, and I've heard the following question a few times:
When should I write an HTTP module and when should I write an action filter?
If you are creating an MVC application then I'll almost always recommend going with an action filter instead of an HTTP module. This is my recommendation even if the functionality you are creating is generic enough to work from inside a module (in other words, it doesn't depend on MVC specific pipeline stages, like the pre and post processing of action invocation). Filters are closely aligned with the MVC infrastructure and vocabulary, and filters are the first thing someone will look for when they want to see how you've implemented cross-cutting functionality in MVC.
On the other hand, an HTTP module is something you can reuse in any type of ASP.NET application. Witness the flexibility of elmah - just drop an assembly in the bin directory, tweak a config file, and wait for elmah to record errors across an entire application. This configurable flexibility isn't something you can do with filters out of the box, but we’ll see tomorrow how easy it is to add.
You can change the base class of a Razor view in ASP.NET MVC using an @inherits directive.
@inherits MyWebViewPage<dynamic>
The default base class for a Razor view in ASP.NET MVC is WebViewPage<T>, and in most cases you don't need to fiddle with @inherits - it's more common to use the @model directive and just strongly type the default base class with a specific ViewModel type.
But, when you really need to set a different base class for a view, you'll probably need to set the same base class everywhere. This is where configuration comes into play.
In the ~/Views folder, you'll find a web.config with the following section inside.
<system.web.webPages.razor> <host factoryType="MvcWebRazorHostFactory ..."/> <pages pageBaseType="WebViewPage"> <namespaces> <add namespace="System.Web.Mvc" /> <add namespace="System.Web.Mvc.Ajax" /> <add namespace="System.Web.Mvc.Html" /> <add namespace="System.Web.Routing" /> </namespaces> </pages> </system.web.webPages.razor>
The razor configuration allows you to include additional default namespaces for view compilation, as well as set the pageBaseType attribute, which will change the base class for all the razor views in an application.
<pages pageBaseType="Infrastructure.MyWebViewPage">
...
</pages>
Note the base type must be a generic type, but you don't need to specify the type parameters in the pageBaseType attribute. The MVC framework doesn't appear to interpret this value, but instead copies the value verbatim into the generated code for the view and appends a generic type parameter (<dynamic> by default). For the value specified above, the base class would look like the following:
public abstract class MyWebViewPage<T> : WebViewPage<T> { ... }
Remember, views are simple and singularly focused on rendering a model. If you think you need a base class to add logging, business calculations, or other services along those line, then take a step back. There are plenty of opportunities to perform work during execution of the controller action, in an action filter, or in the model itself. Keep your views simple!
If you do use a base class, remember you have the opportunity to inject dependencies into properties with MVC3.
public abstract class MyWebViewPage<T> : WebViewPage<T> { [Inject] public ILocalizationHelper Localizer { get; set; } }
To pull this off, all you need is a dependency resolver and an inversion of control container that supports property injection. See Brad Wilson's series of posts for some details.
Mehul Harry interviewed me at DevConnections 2010 about MVC 3.
I'm not sure why my picture makes me think of Hannibal Lecter, but maybe Mehul had a Jodie Foster thing going on that day.
MonoDroid allows you to write Android applications using C# and Visual Studio.
Compare the code in the last post to the following:
[Activity(Label = "My Activity", MainLauncher = true)] public class Activity1 : Activity { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.layout.main); Button button = FindViewById<Button>(Resource.id.button); TextView view = FindViewById<TextView>(Resource.id.text); button.Click += (s, args) => view.Text = "Clicked!"; button.LongClick += (s, args) => { view.Text = "Long click!"; args.ReturnValue = false; }; } }
MonoDroid looks promising. It does all the hard work of mapping Android APIs and idioms into a proper C# representation (notice how the long click Java listener in the last post returns a value, while the long click C# event handler in this post sets a flag in the event arguments). It's little touches like these that make a difference.
MonoDroid also includes a Visual Studio plugin to deploy and debug on the Android simulator, or a real device (even over Wifi). That's a lot of great magic behind the F5 button.