May 2010 - Posts

One of the thing that people often complains is dependency injection in Action Filters. Since the standard way of applying action filters is to either decorate the Controller or the Action methods, there is no way you can inject dependencies in the action filter constructors. There are quite a few posts on this subject, which shows the property injection with a custom action invoker, but all of them suffers from the same small bug (you will find the BuildUp is called more than once if the filter implements multiple interface e.g. both IActionFilter and IResultFilter). The MvcExtensions supports both property injection as well as fluent filter configuration api. There are a number of benefits of this fluent filter configuration api over the regular attribute based filter decoration.

You can pass your dependencies in the constructor rather than property. Lets say, you want to create an action filter which will update the User Last Activity Date, you can create a filter like the following:

public class UpdateUserLastActivityAttribute : FilterAttribute, IResultFilter
{
    public UpdateUserLastActivityAttribute(IUserService userService)
    {
        Check.Argument.IsNotNull(userService, "userService");

        UserService = userService;
    }

    public IUserService UserService
    {
        get;
        private set;
    }

    public void OnResultExecuting(ResultExecutingContext filterContext)
    {
        // Do nothing, just sleep.
    }

    public void OnResultExecuted(ResultExecutedContext filterContext)
    {
        Check.Argument.IsNotNull(filterContext, "filterContext");

        string userName = filterContext.HttpContext.User.Identity.IsAuthenticated ?
                          filterContext.HttpContext.User.Identity.Name :
                          null;

        if (!string.IsNullOrEmpty(userName))
        {
            UserService.UpdateLastActivity(userName);
        }
    }
}

As you can see, it is nothing different than a regular filter except that we are passing the dependency in the constructor. Next, we have to configure this filter for which Controller/Action methods will execute:

public class ConfigureFilters : ConfigureFiltersBase
{
    protected override void Configure(IFilterRegistry registry)
    {
        registry.Register<HomeController, UpdateUserLastActivityAttribute>();
    }
}

You can register more than one filter for the same Controller/Action Methods:

registry.Register<HomeController, UpdateUserLastActivityAttribute, CompressAttribute>();

You can register the filters for a specific Action method instead of the whole controller:

registry.Register<HomeController, UpdateUserLastActivityAttribute, CompressAttribute>(c => c.Index());

You can even set various properties of the filter:

registry.Register<ControlPanelController, CustomAuthorizeAttribute>( attribute => { attribute.AllowedRole = Role.Administrator; });

The Fluent Filter registration also reduces the number of base controllers in your application. It is very common that we create a base controller and decorate it with action filters and then we create concrete controller(s) so that the base controllers action filters are also executed in the concrete controller. You can do the  same with a single line statement with the fluent filter registration:

Registering the Filters for All Controllers:

registry.Register<ElmahHandleErrorAttribute>(new TypeCatalogBuilder().Add(GetType().Assembly).Include(type => typeof(Controller).IsAssignableFrom(type)));

Registering Filters for selected Controllers:

registry.Register<ElmahHandleErrorAttribute>(new TypeCatalogBuilder().Add(GetType().Assembly).Include(type => typeof(Controller).IsAssignableFrom(type) && (type.Name.StartsWith("Home") || type.Name.StartsWith("Post"))));

You can also use the built-in filters in the fluent registration, for example:

registry.Register<HomeController, OutputCacheAttribute>(attribute => { attribute.Duration = 60; });

With the fluent filter configuration you can even apply filters to controllers that source code is not available to you (may be the controller is a part of a third party component).

That’s it for today, in the next post we will discuss about the Model binding support in MvcExtensions. So stay tuned.

Shout it

In the previous post, we have seen the BootstrapperTask which executes when the application starts and ends, similarly there are times when we need to execute some custom logic when a request starts and ends. Usually, for this kind of scenario we create HttpModule and hook the begin and end request events. There is nothing wrong with this approach, except HttpModules are not at all IoC containers friendly and tight coupled with HttpContext and its tail, also defining the HttpModule execution order is bit cumbersome, you either have to modify the machine.config or clear the HttpModules and add it again in web.config. Instead, you can use the PerRequestTask which is very much container friendly as well as supports execution orders. Lets few examples where it can be used.

Remove www Subdomain

Lets say we want to remove the www subdomain, so that if anybody types http://www.mydomain.com it will automatically redirects to http://mydomain.com.

public class RemoveWwwSubdomain : PerRequestTask
{
    public RemoveWwwSubdomain()
    {
        Order = DefaultOrder - 1;
    }

    protected override TaskContinuation ExecuteCore(PerRequestExecutionContext executionContext)
    {
        const string Prefix = "http://www.";

        Check.Argument.IsNotNull(executionContext, "executionContext");

        HttpContextBase httpContext = executionContext.HttpContext;

        string url = httpContext.Request.Url.ToString();

        bool startsWith3W = url.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase);
        bool shouldContinue = true;

        if (startsWith3W)
        {
            string newUrl = "http://" + url.Substring(Prefix.Length);

            HttpResponseBase response = httpContext.Response;

            response.StatusCode = (int)HttpStatusCode.MovedPermanently;
            response.Status = "301 Moved Permanently";
            response.RedirectLocation = newUrl;
            response.SuppressContent = true;
            shouldContinue = false;
        }

        return shouldContinue ? TaskContinuation.Continue : TaskContinuation.Break;
    }
}

As you can see, first, we are setting the order so that we do not have to execute the remaining tasks of the chain when we are redirecting, next in the ExecuteCore, we checking the whether www is present, if present we are sending a permanently moved http status code and breaking the task execution chain otherwise we are continuing with the chain.

Blocking IP Address

Lets take another scenario, your application is hosted in a shared hosting environment where you do not have the permission to change the IIS setting and you want to block certain IP addresses from visiting your application. Lets say, you maintain a list of IP address in database/xml files which you want to block, you have a IBannedIPAddressRepository service which is used to match banned IP Address.

public class BlockRestrictedIPAddress : PerRequestTask
{
    protected override TaskContinuation ExecuteCore(PerRequestExecutionContext executionContext)
    {
        bool shouldContinue = true;
        HttpContextBase httpContext = executionContext.HttpContext;

        if (!httpContext.Request.IsLocal)
        {
            string ipAddress = httpContext.Request.UserHostAddress;

            HttpResponseBase httpResponse = httpContext.Response;

            if (executionContext.ServiceLocator.GetInstance<IBannedIPAddressRepository>().IsMatching(ipAddress))
            {
                httpResponse.StatusCode = (int)HttpStatusCode.Forbidden;
                httpResponse.StatusDescription = "IPAddress blocked.";

                shouldContinue = false;
            }
        }

        return shouldContinue ? TaskContinuation.Continue : TaskContinuation.Break;
    }
}

Managing Database Session

Now, let see how it can be used to manage NHibernate session, assuming that ISessionFactory of NHibernate is already registered in our container.

public class ManageNHibernateSession : PerRequestTask
{
    private ISession session;

    protected override TaskContinuation ExecuteCore(PerRequestExecutionContext executionContext)
    {
        ISessionFactory factory = executionContext.ServiceLocator.GetInstance<ISessionFactory>();

        session = factory.OpenSession();

        return TaskContinuation.Continue;
    }

    protected override void DisposeCore()
    {
        session.Close();
        session.Dispose();
    }
}

As you can see PerRequestTask can be used to execute small and precise tasks in the begin/end request, certainly if you want to execute other than begin/end request there is no other alternate of HttpModule.

That’s it for today, in the next post, we will discuss about the Action Filters, so stay tuned.

Shout it

[Special Note: I have just released SP1 Preview which contains the newly added Autofac Adapter and minor Windsor changes]

In this post, I will show you how you can register your custom services in your preferred IoC container and how the MvcExtensions will integrate it with the rest of the application. One of the goal of MvcExtensions is, unlike the other mvc add-on library which provides its own custom interface for registration/resolution, it will let you use your container syntax that you are familiar with. In case, if you have to refer your container in your application you should use the CommonServiceLocator(or CSL in short) which is now a days a standard to abstract your underlying container from the rest of the application. Yes, I know some of the ASP.NET MVC expert is saying that CSL is not adequate, but let me remind them, the goal of the CSL is laid into its name, if it suppose to give service registration or nested container support, it should be named as Common Service Registrar/Container Factory or something similar. Beside that referring your Container/Locator other than the bootstrapping phase is considered to be a design smell. Although behind the scene, the MvcExtensions extends the CSL with IServiceRegistrar and IServiceInjector interface but you never have to refer those in your code. MvcExtensions comes with most of the popular IoC container adapters that includes Autofac(added in the SP1 Preview), Ninject, StructureMap, Unity and Windsor.

Lets take a fictitious blogging application as an example to see how the registration works and assume that blogging application contains Database, DatabaseFactory, UnitOfWork, Repository, few Domain Services and Mappers(converts domain objects into data transfer objects) as custom services.

Lets start with the Ninject, first you have to add reference of Ninject 2, which you can find from Ninject official site, next you have to add reference of CSL and at last the MvcExtensions and the Ninject Adapter. Next, we will create a Ninject Module which is the standard way of registering services in Ninject. The following shows the module that registers all of the above services:

Ninject

namespace MyBlog
{
    using Ninject;
    using Ninject.Modules;

    public class NinjectServiceRegistrationModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IDatabaseFactory>().To<DatabaseFactory>().InRequestScope();
            Bind<IDatabase>().ToMethod(c => c.Kernel.Get<IDatabaseFactory>().Get());
            Bind<IUnitOfWork>().To<UnitOfWork>();

            Bind(typeof(IRepository<>)).To(typeof(Repository<>));

            Bind<IMapper<Blog, BlogInfo>>().To<BlogMapper>();
            Bind<IMapper<Post, PostInfo>>().To<PostMapper>();
            Bind<IMapper<Tag, TagInfo>>().To<TagMapper>();
            Bind<IMapper<Comment, CommentInfo>>().To<CommentMapper>();

            Bind<IBlogService>().To<BlogService>();
            Bind<IPostService>().To<PostService>();
            Bind<ITagService>().To<TagService>();
            Bind<ICommentService>().To<CommentService>();
        }
    }
}

As you can see there is no nothing special from MvcExtensions, you are creating the Ninject Module in the same way as you do where MvcExtensions is not used. Behind the scene,  The MvcExtensions will scan all the available assemblies of your application and register those Ninject Modules in the Kernel. Also check that we are not registering anything specific to ASP.NET MVC, those are handled by the BootstrapperTask which I have discussed in my last post.

Now, lets see how we can use StructureMap to register the above services. First, we will add reference of StructureMap 2.6.1, then we will add reference of CSL, MvcExtensions, StructureMap Adapter. Next, we will create a Registry which serves same as Ninject Module. The following shows the StructureMap registration:

StructureMap

namespace MyBlog
{
    using StructureMap.Configuration.DSL;

    public class StructureMapServiceRegistry : Registry
    {
        public StructureMapServiceRegistry()
        {
            For<IDatabaseFactory>().HttpContextScoped().Use<DatabaseFactory>();
            For<IDatabase>().Use(c => c.GetInstance<IDatabaseFactory>().Get());
            For<IUnitOfWork>().Use<UnitOfWork>();

            For(typeof(IRepository<>)).Use(typeof(Repository<>));

            For<IMapper<Blog, BlogInfo>>().Use<BlogMapper>();
            For<IMapper<Post, PostInfo>>().Use<PostMapper>();
            For<IMapper<Tag, TagInfo>>().Use<TagMapper>();
            For<IMapper<Comment, CommentInfo>>().Use<CommentMapper>();

            For<IBlogService>().Use<BlogService>();
            For<IPostService>().Use<PostService>();
            For<ITagService>().Use<TagService>();
            For<ICommentService>().Use<CommentService>();
        }
    }
}

I know, we can use the StructureMap convention based api to make the above registration much more simpler and short, but that is a different topic. Same as Ninject, the MvcExtensions will scan for the SM registry and automatically register those in the container.

Next, the Autofac (it has been re-added in the SP1 Preview), Autofac also has a Module which acts same as Ninject Module and StructureMap Registry. Before creating the Module, you have add reference of Autofac 2.2.3 Preview, CSL, MvcExtensions and Autofac Adapter. The following shows the Autofac registration:

Autofac

namespace MyBlog
{
    using Autofac;

    public class AutofacServiceRegistrationModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<DatabaseFactory>().As<IDatabaseFactory>().PerRequestScoped();
            builder.Register(c => c.Resolve<IDatabaseFactory>().Get()).As<IDatabase>();
            builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();

            builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));

            builder.RegisterType<BlogMapper>().As<IMapper<Blog, BlogInfo>>();
            builder.RegisterType<PostMapper>().As<IMapper<Post, PostInfo>>();
            builder.RegisterType<TagMapper>().As<IMapper<Tag, TagInfo>>();
            builder.RegisterType<CommentMapper>().As<IMapper<Comment, CommentInfo>>();

            builder.RegisterType<BlogService>().As<IBlogService>();
            builder.RegisterType<PostService>().As<IPostService>();
            builder.RegisterType<TagService>().As<ITagService>();
            builder.RegisterType<CommentService>().As<ICommentService>();
        }
    }
}

As you can see In line-9 I am using the PerRequestScoped, this is an extension method that has been added in the Autofac Adapter, behind the scene it creates a new scope when a new request begins, sets up the current service locator and all the wacky stuffs that you like to avoid.

Now lets see the most widely used container the Windsor, Windsor has IWindsorInstaller (which I think has not been highlighted that much) which can be used for modular registration. To create a new installer you have add reference of the Windsor 2.1 binaries, CSL, MvcExtensions and Windsor adapter. The following shows the installer which register the services in the container:

Windsor

namespace MyBlog
{
    using Castle.MicroKernel;
    using Castle.MicroKernel.Registration;
    using Castle.Windsor;

    public class WindsorServiceInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(Component.For<IDatabaseFactory>().ImplementedBy<DatabaseFactory>().LifeStyle.PerWebRequest)
                     .Register(Component.For<IDatabase>().UsingFactoryMethod(k => k.Resolve<IDatabaseFactory>().Get()))
                     .Register(Component.For<IUnitOfWork>().ImplementedBy<UnitOfWork>());

            container.Register(Component.For(typeof(IRepository<>)).ImplementedBy(typeof(Repository<>)));

            container.Register(Component.For<IMapper<Blog, BlogInfo>>().ImplementedBy<BlogMapper>())
                     .Register(Component.For<IMapper<Post, PostInfo>>().ImplementedBy<PostMapper>())
                     .Register(Component.For<IMapper<Tag, TagInfo>>().ImplementedBy<TagMapper>())
                     .Register(Component.For<IMapper<Comment, CommentInfo>>().ImplementedBy<CommentMapper>());

            container.Register(Component.For<IBlogService>().ImplementedBy<BlogService>())
                     .Register(Component.For<IPostService>().ImplementedBy<PostService>())
                     .Register(Component.For<ITagService>().ImplementedBy<TagService>())
                     .Register(Component.For<ICommentService>().ImplementedBy<CommentService>());
        }
    }
}

I am using the new syntax but you can also use the regular one, when the container is created the ArrayResolver and FactorySupportFacility are added, so you do not have to add those, if you need any other facilities you can add those creating new installer.

And at last, the Unity. Unlike the other containers the Unity does not have the Modular registration support . This is the reason Unity Adapter comes with an special interface IModule which you have to implement in order to support the modular registration. Let us see the example first, like above you have add to reference of Unity 2, CSL, MvcExtensions and Unity Adapter. Next, create a class which implements the IModule and override the Load method like the following.

Unity

namespace MyBlog
{
    using Microsoft.Practices.Unity;

    using MvcExtensions.Unity;

    public class UnityServiceRegistrationModule : IModule
    {
        public void Load(IUnityContainer container)
        {
            container.RegisterType<IDatabaseFactory, DatabaseFactory>(new PerRequestLifetimeManager())
                     .RegisterType<IDatabase, Database>(new InjectionFactory(c => c.Resolve<IDatabaseFactory>().Get()))
                     .RegisterType<IUnitOfWork, UnitOfWork>();

            container.RegisterType(typeof(IRepository<>), typeof(Repository<>));

            container.RegisterType<IMapper<Blog, BlogInfo>, BlogMapper>()
                     .RegisterType<IMapper<Post, PostInfo>, PostMapper>()
                     .RegisterType<IMapper<Tag, TagInfo>, TagMapper>()
                     .RegisterType<IMapper<Comment, CommentInfo>, CommentMapper>();

            container.RegisterType<IBlogService, BlogService>()
                     .RegisterType<IPostService, PostService>()
                     .RegisterType<ITagService, TagService>()
                     .RegisterType<ICommentService, CommentService>();
        }
    }
}

And like other adapters the MvcExtensions will scan all the assemblies for IModule and pass the container for your registration. One special thing in line-11, for IDatabaseFactory we are using PerRequestLifetimeManager which does not exists in the actual Unity. The issue is like the other containers the Unity does not have any built-in lifetime management support which returns the same service in the same request no matter how many times it has been requested. But it does have the support to create custom lifetime managers, the PerRequestLifetimeManager is a custom class which is included in the Unity adapter.

You will find the above code in the bottom of this post. Just make sure the HttpApplication inherits from you preferred container’s HttpApplication, currently it is using Autofac.

In conclusion, as you have seen working with MvcExtensions adapters is nothing different working with the original container. In the next post, we will see how to use the PerRequestTask and in which scenario it is suitable.

So, stay tuned.

Download: MyBlog.zip

Shout it

When you create a new ASP.NET MVC application you will find that the global.asax contains the following lines:

namespace MvcApplication1
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

        }

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterRoutes(RouteTable.Routes);
        }
    }
}

As the application grows, there are quite a lot of plumbing code gets into the global.asax which quickly becomes a design smell. Lets take a quick look at the code of one of the open source project that I recently visited:

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute("Default","{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" });
        }        
        protected override void OnApplicationStarted()
        {
            Error += OnError;
            EndRequest += OnEndRequest;
            
             var settings = new SparkSettings()
                .AddNamespace("System")
                .AddNamespace("System.Collections.Generic")
                .AddNamespace("System.Web.Mvc")
                .AddNamespace("System.Web.Mvc.Html")
                .AddNamespace("MvcContrib.FluentHtml")
                .AddNamespace("********")
                .AddNamespace("********.Web")
                .SetPageBaseType("ApplicationViewPage")
                .SetAutomaticEncoding(true);
                               
#if DEBUG
            settings.SetDebug(true);
#endif
            var viewFactory = new SparkViewFactory(settings);
            ViewEngines.Engines.Add(viewFactory);
#if !DEBUG            
            PrecompileViews(viewFactory);
#endif            

            RegisterAllControllersIn("********.Web");

            log4net.Config.XmlConfigurator.Configure();            
            RegisterRoutes(RouteTable.Routes);

            Factory.Load(new Components.WebDependencies());
            ModelBinders.Binders.DefaultBinder = new Binders.GenericBinderResolver(Factory.TryGet<IModelBinder>);

            ValidatorConfiguration.Initialize("********");
            HtmlValidationExtensions.Initialize(ValidatorConfiguration.Rules);
        }

        private void OnEndRequest(object sender, System.EventArgs e)
        {
            if (((HttpApplication)sender).Context.Handler is MvcHandler)
            {
                CreateKernel().Get<ISessionSource>().Close();
            }
        }
        private void OnError(object sender, System.EventArgs e)
        {
            CreateKernel().Get<ISessionSource>().Close();
        }
        protected override IKernel CreateKernel()
        {
            return Factory.Kernel;
        }
        
        private static void PrecompileViews(SparkViewFactory viewFactory)
        {
            var batch = new SparkBatchDescriptor();
            batch.For<HomeController>().For<ManageController>();
            viewFactory.Precompile(batch);                  
        }

As you can see there are quite a few of things going on in the above code, Registering the ViewEngine, Compiling the Views, Registering the Routes/Controllers/Model Binders, Settings up Logger, Validations and as you can imagine the more it becomes complex the more things will get added in the application start.

One of the goal of the MVCExtensions is to reduce the above design smell. Instead of writing all the plumbing code in the application start, it contains BootstrapperTask to register individual services. Out of the box, it contains BootstrapperTask to register Controllers, Controller Factory, Action Invoker, Action Filters, Model Binders, Model Metadata/Validation Providers, ValueProvideraFactory, ViewEngines etc and it is intelligent enough to automatically detect the above types and register into the ASP.NET MVC Framework. Other than the built-in tasks you can create your own custom task which will be automatically executed when the application starts. When the BootstrapperTasks are in action you will find the global.asax pretty much clean like the following:

public class MvcApplication : UnityMvcApplication
{
    public void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs e)
    {
        Check.Argument.IsNotNull(e, "e");

        HttpException exception = e.Exception.GetBaseException() as HttpException;

        if ((exception != null) && (exception.GetHttpCode() == (int)HttpStatusCode.NotFound))
        {
            e.Dismiss();
        }
    }
}

The above code is taken from my another open source project Shrinkr, as you can see the global.asax is longer cluttered with any plumbing code. One special thing you have noticed that it is inherited from the UnityMvcApplication rather than regular HttpApplication. There are separate version of this class for each IoC Container like NinjectMvcApplication, StructureMapMvcApplication etc.

Other than executing the built-in tasks, the Shrinkr also has few custom tasks which gets executed when the application starts. For example, when the application starts, we want to ensure that the default users (which is specified in the web.config) are created. The following is the custom task that is used to create those default users:

public class CreateDefaultUsers : BootstrapperTask
{
    protected override TaskContinuation ExecuteCore(IServiceLocator serviceLocator)
    {
        IUserRepository userRepository = serviceLocator.GetInstance<IUserRepository>();
        IUnitOfWork unitOfWork = serviceLocator.GetInstance<IUnitOfWork>();
        IEnumerable<User> users = serviceLocator.GetInstance<Settings>().DefaultUsers;

        bool shouldCommit = false;

        foreach (User user in users)
        {
            if (userRepository.GetByName(user.Name) == null)
            {
                user.AllowApiAccess(ApiSetting.InfiniteLimit);

                userRepository.Add(user);
                shouldCommit = true;
            }
        }

        if (shouldCommit)
        {
            unitOfWork.Commit();
        }

        return TaskContinuation.Continue;
    }
}

There are several other Tasks in the Shrinkr that we are also using which you will find in that project. To create a custom bootstrapping task you have create a new class which either implements the IBootstrapperTask interface or inherits from the abstract BootstrapperTask class, I would recommend to start with the BootstrapperTask as it already has the required code that you have to write in case if you choose the IBootstrapperTask interface. As you can see in the above code we are overriding the ExecuteCore to create the default users, the MVCExtensions is responsible for populating the  ServiceLocator prior calling this method and in this method we are using the service locator to get the dependencies that are required to create the users (I will cover the custom dependencies registration in the next post). Once the users are created, we are returning a special enum, TaskContinuation as the return value, the TaskContinuation can have three values Continue (default), Skip and Break. The reason behind of having this enum is, in some  special cases you might want to skip the next task in the chain or break the complete chain depending upon the currently running task, in those cases you will use the other two values instead of the Continue. The last thing I want to cover in the bootstrapping task is the Order. By default all the built-in tasks as well as newly created task order is set to the DefaultOrder(a static property), in some special cases you might want to execute it before/after all the other tasks, in those cases you will assign the Order in the Task constructor. For Example, in Shrinkr, we want to run few background services when the all the tasks are executed, so we assigned the order as DefaultOrder + 1. Here is the code of that Task:

public class ConfigureBackgroundServices : BootstrapperTask
{
    private IEnumerable<IBackgroundService> backgroundServices;

    public ConfigureBackgroundServices()
    {
        Order = DefaultOrder + 1;
    }

    protected override TaskContinuation ExecuteCore(IServiceLocator serviceLocator)
    {
        backgroundServices = serviceLocator.GetAllInstances<IBackgroundService>().ToList();

        backgroundServices.Each(service => service.Start());

        return TaskContinuation.Continue;
    }

    protected override void DisposeCore()
    {
        backgroundServices.Each(service => service.Stop());
    }
}

That’s it for today, in the next post I will cover the custom service registration, so stay tuned.

Shout it

I am proud to announce the v1.0 of MvcExtensions (previously known as System.Web.Extensibility). There has been quite a few changes and enhancements since the last release. Some of the major changes are:

  • The Namespace has been changed to MvcExtensions from System.Web.Mvc.Extensibility to avoid the unnecessary confusion that it is in the .NET Framework or part of the ASP.NET MVC.
  • The Project is now moved to CodePlex from the GitHub. The primary reason to start the project over GitHub was distributed version control which is no longer valid as CodePlex recently added the Mercurial support. There is nothing wrong with GitHub, it is an excellent place for managing your project. But CodePlex has always been the native place for .NET project.
  • MVC 1.0 support has been dropped.

I will be covering each features in my blog, so stay tuned!!!

Shout it
More Posts