<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://devlicio.us/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Jak Charlton - Insane World : MVC, Windsor</title><link>http://devlicio.us/blogs/casey/archive/tags/MVC/Windsor/default.aspx</link><description>Tags: MVC, Windsor</description><dc:language>en</dc:language><generator>CommunityServer 2008.5 SP1 (Build: 31106.3070)</generator><item><title>Single Action per Controller in ASP.NET MVC</title><link>http://devlicio.us/blogs/casey/archive/2011/07/11/single-action-per-controller-in-asp-net-mvc.aspx</link><pubDate>Mon, 11 Jul 2011 01:08:00 GMT</pubDate><guid isPermaLink="false">40756a8b-6212-4073-9d98-6c26781577de:67963</guid><dc:creator>Jak Charlton</dc:creator><slash:comments>10</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://devlicio.us/blogs/casey/rsscomments.aspx?PostID=67963</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://devlicio.us/blogs/casey/commentapi.aspx?PostID=67963</wfw:comment><comments>http://devlicio.us/blogs/casey/archive/2011/07/11/single-action-per-controller-in-asp-net-mvc.aspx#comments</comments><description>&lt;p&gt;As I keep doing this on MVC projects, thought I may as well blog it, so the next time I need it I can find it myself :)&lt;/p&gt;
&lt;p&gt;To keep ASP.NET MVC controllers clean and organised, I find it much easier to split them so they handle only a single action each. This helps significantly with maintaining some semblance of the Single Responsibility Principle, allowing each controller to deal with a single action, and not have concerns like Logon, Logoff and ChangePassword all start globbing together under a single controller called &amp;quot;Account&amp;quot;&lt;/p&gt;
&lt;p&gt;With a little bit of magic in the ControllerFactory you can tell ASP.NET MVC3 to resolve controllers from your own directory and class structure, meaning we can have controllers that look like:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; IndexController : Controller
{
   [Authorize]
   &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Index()
   {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; View();
   }
}&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://gist.github.com/1075169"&gt;https://gist.github.com/1075169&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;The Controller Factory&lt;/h3&gt;
&lt;p&gt;I tend to use Castle Windsor to do this magic for me, though any IoC container would do, and you could even do it without a container if you were really masochistic!&lt;/p&gt;
&lt;p&gt;In Windsor the ControllerFactory looks like:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;pre class="csharpcode"&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; WindsorControllerFactory : DefaultControllerFactory
    {
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; IKernel kernel;

        &lt;span class="kwrd"&gt;public&lt;/span&gt; WindsorControllerFactory(IKernel kernel)
        {
            &lt;span class="kwrd"&gt;this&lt;/span&gt;.kernel = kernel;
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ReleaseController(IController controller)
        {
            kernel.ReleaseComponent(controller);
        }

        &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; IController GetControllerInstance(RequestContext context, Type controllerType)
        {
            var baseNs = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(SecureController).Namespace;

            var ns = context.RouteData.GetRequiredString(&lt;span class="str"&gt;&amp;quot;controller&amp;quot;&lt;/span&gt;).ToLower();
            var controllerName = context.RouteData.GetRequiredString(&lt;span class="str"&gt;&amp;quot;action&amp;quot;&lt;/span&gt;).ToLower() + &lt;span class="str"&gt;&amp;quot;controller&amp;quot;&lt;/span&gt;;
            &lt;span class="kwrd"&gt;try&lt;/span&gt;
            {
                var controller = kernel.Resolve&amp;lt;IController&amp;gt;(baseNs.ToLower() + &lt;span class="str"&gt;&amp;quot;.&amp;quot;&lt;/span&gt; + ns + &lt;span class="str"&gt;&amp;quot;.&amp;quot;&lt;/span&gt; + controllerName);
                &lt;span class="kwrd"&gt;return&lt;/span&gt; controller;
            }
            &lt;span class="kwrd"&gt;catch&lt;/span&gt; (ComponentNotFoundException ex)
            {
                &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; HttpException(404, &lt;span class="kwrd"&gt;string&lt;/span&gt;.Format(&lt;span class="str"&gt;&amp;quot;The controller for path &amp;#39;{0}&amp;#39; could not be found.&amp;quot;&lt;/span&gt;, context.HttpContext.Request.Path));
            }
        }
    }
&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://gist.github.com/1075171"&gt;https://gist.github.com/1075171&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;The Controller Installer&lt;/h3&gt;
&lt;p&gt;So that Windsor has some way of resolving our controllers, you need to register them in the container. This installer will scan for all classes implementing IController and add them into the container against a key matching their namespace:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Install(IWindsorContainer container, IConfigurationStore store)
{
     &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (var type &lt;span class="kwrd"&gt;in&lt;/span&gt; Assembly.GetExecutingAssembly().GetTypes())
     {
      &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;typeof&lt;/span&gt;(IController).IsAssignableFrom(type) &amp;amp;&amp;amp; !type.IsAbstract)
      container.Register(Component.For(type).ImplementedBy(type).Named(type.FullName.ToLower()).LifeStyle.Transient);
     }
}
&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://gist.github.com/1075176"&gt;https://gist.github.com/1075176&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Which leaves your controller folder structure looking like:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/casey/controllers.png"&gt;&lt;img src="http://devlicio.us/resized-image.ashx/__size/550x0/__key/CommunityServer.Blogs.Components.WeblogFiles/casey/controllers.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;GET and POST&lt;/h3&gt;
&lt;p&gt;Where an action has a Get and a Post action, for example where you display a form, then take some actions based on the form, you just put the two actions in the same Controller, you aren&amp;#39;t breaking SRP here as both actions are inherently linked anyway:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;pre class="csharpcode"&gt;   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ChangePasswordController : Controller
    {
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; IUsersDB users;

        &lt;span class="kwrd"&gt;public&lt;/span&gt; ChangePasswordController(IUsersDB users)
        {
            &lt;span class="kwrd"&gt;this&lt;/span&gt;.users = users;
        }

        [Authorize]
        &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult ChangePassword()
        {
            &lt;span class="kwrd"&gt;return&lt;/span&gt; View();
        }

        [Authorize]
        [HttpPost]
        &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult ChangePassword(ChangePasswordViewModel model)
        {
            &lt;span class="kwrd"&gt;if&lt;/span&gt; (ModelState.IsValid)
            {
                &lt;span class="kwrd"&gt;try&lt;/span&gt;
                {
                    users.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword);
                    &lt;span class="kwrd"&gt;return&lt;/span&gt; RedirectToAction(&lt;span class="str"&gt;&amp;quot;ChangePasswordSuccess&amp;quot;&lt;/span&gt;);
                }
                &lt;span class="kwrd"&gt;catch&lt;/span&gt; (DataAccessException ex)
                {
                    ModelState.AddModelError(&lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;The current password is incorrect or the new password is invalid.&amp;quot;&lt;/span&gt;);
                }
            }

            &lt;span class="kwrd"&gt;return&lt;/span&gt; View(model);
        }
    }&lt;/pre&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://devlicio.us/aggbug.aspx?PostID=67963" width="1" height="1"&gt;</description><category domain="http://devlicio.us/blogs/casey/archive/tags/Windsor/default.aspx">Windsor</category><category domain="http://devlicio.us/blogs/casey/archive/tags/MVC/default.aspx">MVC</category><category domain="http://devlicio.us/blogs/casey/archive/tags/asp.net/default.aspx">asp.net</category></item></channel></rss>