POCO controllers in ASP.NET vNext - StrathWeb

Strath

June 11th, 2014

POCO controllers in ASP.NET vNext

Because lightweight is better!

One of the very cool features of the vNext of ASP.NET MVC (a unified framework set to succeed MVC, Web API and Web Pages) is the ability use POCO classes as controllers. No base class, no interface to implement, 100% convention.

Let’s look a little bit into that.

POCO controllers in ASP.NET vNext

By default, as long as your class is public, non-abstract, has a Controller suffix and is defined in an assembly that references any of the MVC assemblies (Microsoft.AspNet.Mvc.Core, Microsoft.AspNet.Mvc etc), it will be discovered as a valid controller. An example is shown below.

Of course you cannot do much with just a POCO – since you typically need access to things like current HTTP request, View Data or current Principal. However, this is not a problem at all – POCO controllers support convention-based property injection. This is achieved through DefaultControllerFactory, which looks for the following properties:

  • ActionContext and injects an instance of ActionContext there
  • ViewData, and injects an instance of ViewDataDictionary there
  • Url, and injects an instance of IUrlHelper there

You can see the source code here.

So we could modify our controller accordingly:

And boom, just like that you have all the contextual information you might need – for example, HttpRequest and Principal are both hanging off ActionContext.

In addition to that, the helpers, such as the aforementioned IUrlHelper or IActionResultHelper, can also be constructor injected, since ASP.NET vNext has dependency injection all the way through.

Ditching the controller suffix

Now, what if you wanted to go a step further and free yourself from the mandatory Controller suffix? Well, it’s actually not that difficult to do. Inspection whether a given Type qualifies as a controller happens inside DefaultActionDiscoveryConventions, the default IActionDiscoveryConventions implementation, inside of the IsController method. So you could either implement that interface yourself, or extend the DefaultActionDiscoveryConventions, which exposes all of its methods as virtual.

In the following example we extend the base logic with our new extra suffix – “Endpoint”.

We could now change our class to

And it will work just fine – the only problem is that it will be publicly available as myapi.com/dummyendpoint – as the logic of stripping away the Controller suffix when exposing HTTP endpoints is embedded into ControllerDescriptor and handles only – surpise, surpise – Controller suffix.

We could remedy that by creating a custom ControllerDescriptorFactory – a factory class responsible for taking TypeInfo (desribing a controller) and producing an instance of a ControllerDescriptor out of it. The Name on the ControllerDescriptor will determine how a given endpoint is publicly visible. Unfortunately at this point ControllerDescriptor has private setters, so cannot be sublassed, and the default ControllerDescriptorFactory has no virtual members, so cannot be customized – but we can work around that with a bit of reflection.

So effectively we say, if our controller ends with “Endpoint”, we are going to strip that away.

All that’s left now is to register the new services against ServiceCollection used by the IBuilder. YOu have to do that after registering MVC, otherwise, MVC will overwrite your registrations.

And now we can go all crazy with our custom endpoints. So now not only we go full POCO, but we even don’t need the mandatory “Controller” suffix anymore.

2014-06-11_1122

Be Sociable, Share!

  • Dariusz Lenartowicz

    POCO Controllers looks like cool stuff. Without web context can be treated like service in application layer (not UI). Everything else can be infrastructural.

  • Pingback: Dew Drop – June 12, 2014 (#1796) | Morning Dew

  • John

    On the face of it I don’t really understand the benefit of this approach. As you say, I will almost certainly require access to ViewData, ActionContext etc so these properties will appear on most of my POCO controllers. To keep the code DRY I can imagine putting these properties on a base controller class, at which point am I not just reinventing the base controller concept? If I’m referencing classes such as ActionContext and ViewData in my controller then I am tied to the web framework anyway. Conceptually I can see how having controller classes be independent of any framework would allow them to be reused as services in other contexts/applications but if my POCO controllers reference framework classes such as ActionContext etc then it feels like they are somewhat tied to the MVC framework anyway and I may as well have just inherited the MVC base controller. Perhaps I’m missing the point?

  • Pingback: Les liens de la semaine – Édition #84 | French Coding

  • Rick Strahl

    John – I can see that as being useful for API endpoints, where all you really care about is the business logic to produce a typed output.

    For MVC and HTML output though I agree: Doing away with the Controller subclass probably doesn’t do you much good other than making extra work re-inventing the wheel. One nice thing about that though is that you can opt into just the things you actually need, not the dozens of methods that exist on Controller today.

    I suspect it’ll be good for framework builders that can completely customize the pipeline while providing the standard contract that you’d expect for MVC applications.

    • John

      Thanks Rick. The API scenario certainly makes sense. I was forgetting that ASP.Net has become as much an API technology as an MVC/HTML framework. Also, as you say it is nice to be able to opt in to framework features, especially when you are using a relatively small subset of what is exposed by the framework.

  • Edgard

    i just can’t see why get rid of the “Controller” suffix

    • http://www.strathweb.com/ Filip W

      to shrug off the chains. liberate yourself. feel free.

  • TheHulk

    This is bad idea. Stop adding more “voodoo” magic in the name of “conventions”. It gets really bad to find bugs. The same thing can be easily achieved via better coding patterns.