I’m proud to announce a new open source project named EPiMVC which is hosted on CodePlex. My ambition with EPiMVC is to create a framework for building websites with EPiServer CMS and ASP.NET MVC, addressing problems such as routing, URL rewriting, access filtering and rendering properties. Today I’m introducing a first preview of the framework which primarily focuses on routing and URL rewriting.
Since ASP.NET MVC 1.0 was released there has been a few attempts to use it with EPiServer CMS, both by me and by others. There is also at least two sites that have gone live using one of those approaches and this is one of them. However all approaches that I’ve seen so far has been limited in one way or another. Mine didn’t support hierarchical URLs for instance, and Fabio’s didn’t support multiple actions (although you could define specific routes for that). Also, all of our previous attempts has also been limited to addressing the problem with creating and handling URLs. That’s natural since it’s the first and biggest problem that we run into when we try to use EPiServer with MVC. But it’s far from the only one.
With that in mind my stance towards using MVC when building EPiServer sites has been that it’s not worth the hassle for typical EPiServer sites. It’s however a very good idea if you are building a site where the CMS is playing a very small part, such as for a community. But for your typical, content heavy site I’ve been favoring sticking with Web Forms (preferably using the MVP pattern) for the time being until EPiServer adds official support for MVC. I’m also quite confident that we’ll see that happening pretty soon.
A couple of weeks ago Emil suggested that we would do a workshop about using EPiServer with MVC at Valtech and I found myself arguing against it due to my experiences from previous attempts. My arguments we’re primarily that handling URL’s had proven to be so problematic in the past and that it would probably take a very complex solution to handle that. And even if we did manage to solve that we would still loose so many built in features that are tightly integrated with Web Forms that it wouldn’t be worth it.
We left the discussion without either of us having been really convinced. But like so many times before (he’s annoyingly often right) I started thinking about Emil’s and my own arguments and mine started to sound more and more hollow and Emil’s more and more challenging. And since I love a challenge, meet EPiMVC :-)
When we try to use EPiServer with MVC we a number of problems. There might be others, but so far I’ve identified five.
EPiServer has it’s Friendly URL system which gives the illusion of each page having a URL based on it’s place in the content hierarchy, such as /en/news/2010/sales-are-improving/. ASP.NET MVC on the other hand has URL’s that describes behavior, such as /article/view/sales-are-improving/. If we need to pass other information using the URL we usually do this with a query string parameter with EPiSever, such as /en/news/2010/sales-are-improving/?viewcomments=1 while with MVC we would change the action part of the URL, rendering something like /article/viewwithcomments/sales-are-improving/.
I think the hierarchical URL structure is very good for content heavy sites which is what EPiServer CMS is usually used for. But if we find a way to handle those types of URL’s while using MVC we need a way to include other information such as other actions than the default and other parameters without using query string parameters. And on top of that we need to be able to use both MVC and Web Forms at the same time, so anything we do with EPiServers URL rewriting has to be context aware, only interfering with the standard rewriting functionality for page that are supposed to be handled by an MVC controller.
While many of use only use the Property web control that is shipped with EPiServer sporadically it is almost a must when it comes to rendering XForms and to a lesser degree when rendering XHTML properties with dynamic content in them. While I haven’t used XForms much myself it’s my understanding that it’s quite widely used so being able to use XForms is important. Unfortunately the current rendering functionality in EPiServer is very tightly integrated with Web Forms so being able to render properties like XForms is definitely a challenge.
When we use ASPX pages that inherit from TemplatePage to render EPiServer pages we gain the ability to edit properties that are rendered using the Property web control directly in view mode, a concept called Direct On Page Edit, or DOPE. We also get the right click menu. From what I’ve seen the Direct On Page Edit isn’t very widely used, but the right click menu is. Naturally we loose this functionality if we use MVC.
EPiServer CMS ships with a set of base classes for pages and user controls that helps us with common and easily forgotten tasks such as verifying access rights and publish status for pages. It also comes with a collection of web controls, such as MenuList, NewsList, Calendar, PageList etc. Using these web controls we can very quickly build commonly used features. While we can live without these components it would definitely be valuable to have some of this functionality out of the box when we build a site with MVC as well.
It’s a common practice for many to start a new EPiServer project by installing the basic template set shipped with EPiServer, the Public Templates. By doing so we gain basic functionality that we can build upon and quite easily modify to suite our needs. Clearly it would be good with some basic set of templates for an EPiServer site built with MVC. It could however be even less feature rich than the Public Templates.
Handling URLs is what I’ve been focusing on so far. While there are a few things left to do I think the current solution shows a lot of promise. It mixes EPiServers hierarchical URL structure with MVC’s functional in a good way and it’s possible to use it along side Web Forms. That is you can have some page types rendered by MVC and some by Web Forms should you want to. I have also tried to make it easy to work with, using a set of default conventions, while still being fully configurable. There’s so much more to say on this subject that I’ll have to do it in a separate post.
I’m quite confident that we can find a way to render dynamic content without the Property control. XForms on the other hand might prove more difficult. The current release ships with a solution, almost exclusively copied from this great set of blog posts by Hugo Bonacci, for rendering Web Forms controls inside a MVC view. So far I’ve been able to use this approach to render a XForm but I’ve been able to make it handle postbacks correctly. This is definitely a priority for future investigation and development. Worst case scenario we’ll have to look at rendering XForms in an IFrame (god I hope not!).
As for editing features in view mode I’d say that the right click menu, or something similar to it is doable. It’s not something that I’ve looked into yet however and it doesn’t feel like it needs a very high priority. Direct On Page Edit is nothing I have any plans at all to support with EPiMVC. Contributions are much welcomed though :-)
The current release already ships with a base class for controllers and I plan to extend that with some basic security and publication state checks. I would also like to create some HtmlHelper extensions or similar to mimic some of the often used features of the web controls shipped with EPiServer.
Unfortunately the current release doesn’t ship with any templates and I don’t foresee that changing in the immediate future as they are dependent on the features described above. But hopefully, and perhaps with some help, it should definitely be possible to get there in a few weeks or months.
Let’s look at how to use EPiMVC! We begin by setting up a project.
<urlRewrite defaultProvider="MvcUrlRewriteProvider">
<providers>
<add name="MvcUrlRewriteProvider"
description="EPiMVC URL rewriter"
type="EPiMVC.UrlRewriting.MvcUrlRewriteProvider, EPiMVC" />
</providers>
</urlRewrite>
protected void Application_Start(Object sender, EventArgs e)
{
RouteTable.Routes.Add(new EPiServerRoute());
}
Once the project set up we create a page type, Article, for our first page that will be rendered with MVC. We do this using Page Type Builder. If you aren’t familiar with how Page Type Builder works I recommend my tutorial on how to use it.
There’s one thing extra that we need to do when we create the page type. We need to add a MvcPage attribute to it. EPiMVC’s URL rewriter and the EPiServerRoute class needs that as they need to know which requests it should handle and which it shouldn’t so that they wont tamper with request for pages that should be handled by Web Forms pages.
Anyhow, let’s add a page type named Article with a single property, MainBody.
[PageType]
[MvcPage]
public class Article : TypedPageData
{
[PageTypeProperty]
public virtual string MainBody { get; set; }
}
Next up we need to create a controller that will handle requests for pages of the Article page type. To do that right click on the Controllers folder in your project and choose Add –> Controller. Name the controller ArticleController. I’d go into more details about the routing in another blog post but in case you are wondering EPiMVC has a default convention to map pages of type A to controllers named AController. This convention is fully customizable and you can also change the controller mapping for individual page types by using a DefaultController attribute.
Once Visual Studio has added the controller it will probably look something like this:
public class ArticleController : Controller
{
public ActionResult Index()
{
return View();
}
}
Visual Studio adds an Action for us named Index. This suites us well as that is the default action name that EPiMVC will map requests to if the URL doesn’t contain an action. You can configure a different default action name, either for all request or for a single page type, more on that in a coming post.
If you change it’s inheritance from Controller to EPiServerControllerBase<Article> it will get a CurrentPage property of type Article. Do that and pass it to the view.
public class ArticleController : EPiServerControllerBase<Article>
{
public ActionResult Index()
{
return View(CurrentPage);
}
}
Last but not least we need to create a view. Right click on the call to View() in the Index method in the controller and then choose Add View from the context menu. Create a strongly typed view with your page type class as the “View data class”. You can now easily display the current page’s PageName and MainBody property as the current page is the view’s Model property.
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<EPiServer.PageTypes.Article>" %> <h1><%= Model.PageName %></h1> <%= Model.MainBody %>
That’s it! Try it out by adding a page of type Article anywhere in the structure of your site.
You can specify a different action than the default by appending the action name to the end of a page’s URL. EPiMVC will also handle extra parameters. As default they will be added to a list of strings which you can access in a controller using ControllerContext.RouteData.Values["parameters"]. However, you can also add a RouteEnding attribute to your page type specifying how the should be mapped. I’ll come back to this subject later :)
EPiMVC ships with extension methods for the HtmlHelper (the PageLink method) and UrlHelper (the PageUrl method) classes for linking to EPiServer pages. This is yet another subject I’ll have to cover in greater detail later, but here’s a few usage examples:
<%@ Import Namespace="EPiMVC" %>
<%@ Import Namespace="EPiMVC.Html" %>
<%@ Import Namespace="EPiServer.Core" %>
<%= Html.PageLink("Link", Model.SomeOtherPage.PageLink)%>
<%= Html.PageLink("Link with Action and parameters.",
"comment", Model.SomeOtherPage.PageLink, new {test = 123})%>
<%= Html.PageLink("Link to the start page",
"Index", PageReference.StartPage) %>
<%= Url.PageUrl("actionName", Model.SomeOtherPage.PageLink) %>
There is still A LOT of work to do, the code is far from perfect and some of it is still experimental but I think it shows promise. I would love it if you try it and let me know what you think! Also, feel free to grab the source code and play with it. Hopefully the tests can provide some guidance to what it does :-)
PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.
This blog is built with EPiServer Community, EPiServer CMS, ASP.NET MVC and a bunch of other great products. The source code is available for download at the projects page, where you also can read more about this site and my other projects.
read more
Comments
Marthin Freij 2 years ago
Standing ovation! Amazing job Joel!
Markus Ullmark 2 years ago
Amazing! I will try it out asap :)
Did I misunderstand you when you wrote that MVC views and WebForms will work side by side. Why not just use WebForms for XForms, and forget about that IFrame last resort :)
Joel Abrahamsson 2 years ago
They will work side by side in the sense that you can render some page types with Web Forms and some with MVC. But if you want to include a XForm in a page rendered by MVC it's a bit problematic.
Rizo 2 years ago
Well done Joel! This looks tasty! :)
Paul Smith 2 years ago
Joel, fantastic work, keep it up!
MVC Support is very much high up on EPiServer's backlog but I can't give any timescales yet.
Paul Smith
Developer Evangelist, EPiServer.
Mark Everard 2 years ago
Awesome stuff Joel.
I had a quick peek at the code the other night and am looking forward to playing with it in more detail!
Fabio 2 years ago
Hey Joel, great stuff, as usual!
I'm planning a commercial project with EPi6 and EPiMVC. Shall I raise potential issues (and fixes) in codeplex?
Fabio
Joel Abrahamsson 2 years ago
Hey Fabio!
Please do! You can also add me on msn (joel@webpoesi.com).
Really looking forward to your feedback!
Erik Lidälv 1 years ago
Hi Joel! Great work! :) However I'm receiving an error trying to set up an EPiMVC project as described above.
Any idea what might be wrong? Thanks!
Erik
Joel Abrahamsson 1 years ago
Hi Erik,
Sorry for the late reply. I'm just back from an awesome time at the NDC 2010 conference :)
Anyhow, I'm not quite sure what could be causing that. One good (or rather the best I can come up with) starting point could be to verify that the project is set up to use .NET 3.5 and MVC 1.0.
Erik Lidälv 1 years ago
Ahh.. I see :) Well I'm using .net 4 and mvc 2 so I guess this is the problem. Any plans on releasing EPiMVC on .net4/mvc2?
See you soon at the EPiServer-utvecklare-meetup!
Chears, Erik
Erik Lidälv 1 years ago
..with 2 ee's ;)
Joel Abrahamsson 1 years ago
Unfourtunately EPiServer doesn't support MVC 2. It doesn't officially support .NET 4 either, but with MVC 2 SiteCenter will crash and burn :/
My current plan (which does tend to change quite often) is probably to focus on other aspects of being to work with EPiServer in a testable manner (EPiAbstractions, EPiMVP etc) and wait for EPi to officially support MVC.
Mikael Söderström 1 years ago
So EPiServer doesn´t support MVC 2? Sounds really strange since it should be (at least almost) fully backwards compatible.
What errors do you get with ASP.NET MVC 2 and EPiServer? It would be really interesting to see.
Joel Abrahamsson 1 years ago
I think the main thing is that MVC 2 doesn't support multiple controller factories which it did in 1.0 and which the current version of EPiServer utilizes.
I'm not a hundred percent sure that that's the problem (it's a bit late and I'm tired :-)) but it does seem that EPiServer where unfortunate to utilize one of the few things that wasn't backwards compatible in MVC 2.
It's definitely not a big deal to fix (and they already have) it but it requires a new release of the product :(
Mikael Söderström 1 years ago
You could create a new ControllerFactory, which inherits the DefaultControllerFactory and override the CreateController method where you add the EPiServer specific logic.
Why do they have to use multiple controller factories?
Joel Abrahamsson 1 years ago
Yeah. I think the work around is very simple but the problem is that there's a few things going on in the assemblies that ships with EPiServer that relies on things that have changed in MVC 2 and implementing the work around requires a new release.
I haven't dug deeper into this myself but I'll check if someone who has can give a better answer.
Mikael Söderström 1 years ago
I guess it´s because of a "fulhack". ;-)
Would be great if they could fix this. There is a lot of awesome features in ASP.NET MVC 2 that could save a lot of time while developing.
Cristian Libardo 1 years ago
@mikael: There are a few breaking changes in MVC2 that prevents a simple assembly redirect. If I recall correctly it's related to string to MvcHtmlString parameter changes, the route handler change joel mentions and some changes to json and http get. There is a (not huge) risk of breaking user code when upgrading this so I don't know for certain when this will happen.
Mikael Söderström 1 years ago
@Christian
Okay, I see. Will there be a hotfix for that? It shouldn´t be that hard to fix, and for example the Json problem can be fixed with just adding another parameter to the call.
Arve Systad 1 years ago
Looks really sweet, this. Combine it with http://sparkviewengine.com/ and you've got a winner. :-)
Andrei 1 years ago
Is this same process required with the new R2 of CMS 6, or is the new version more MVC friendly?