Showing posts with label asp.net mvc. Show all posts
Showing posts with label asp.net mvc. Show all posts

Monday, June 27, 2011

Refactoring to functional ActionResults in Figment

This is a refactoring tale about Figment, the web framework I've been writing. As with most refactoring tales, I'll be extra verbose and explicit and maybe a little dramatic about each step and its rationale.

Figment is, as I explained when I first wrote about it, based on ASP.NET MVC. As such, it uses many ASP.NET MVC types, like ControllerContext and ActionResult.

Let's say we wanted to create a new ActionResult to model a HTTP "method not allowed" response. The RFC says that this response has a status code 405 and "the response MUST include an Allow header containing a list of valid methods for the requested resource".

Such a response is appropriate when the client issues a request with a HTTP method that is not supported by the server, i.e. not every application can handle a DELETE method. But pretty much every application can handle GET and POST, so when receiving a DELETE request the server could respond with 405 and an Allow: GET, POST header (supporting HEAD is pretty common too, but for this post let's assume only GET and POST are supported).

If we were working with objects and classes we would write a class inheriting ActionResult to encapsulate this, something like (using F#):

type MethodNotAllowed(validMethods: string seq) = 
    inherit ActionResult() with 
        override x.ExecuteResult ctx = 
            ctx.Response.StatusCode <- 405 
            ctx.Response.AppendHeader("Allow", String.Join(", ", validMethods))

and we would use this in Figment like this:

action (ifMethodIs "DELETE") (fun _ -> MethodNotAllowed ["GET"; "POST"])

Thanks to object expressions in F#, we can get away without writing an explicit class:

let methodNotAllowed(validMethods: #seq<string>) = 
    { new ActionResult() with 
        override x.ExecuteResult ctx = 
            ctx.Response.StatusCode <- 405 
            ctx.Response.AppendHeader("Allow", String.Join(", ", validMethods)) }

Now in Figment there's not much difference:

action (ifMethodIs "DELETE") (fun _ -> methodNotAllowed ["GET"; "POST"])

But we can be more concise and functional in the definition of this response.

The first thing to realize is that ActionResult has a single method ExecuteResult with signature ControllerContext -> unit. So we could easily represent it as a regular function ControllerContext -> unit and then build the actual ActionResult whenever we need it:

let inline result r = 
    {new ActionResult() with 
        override x.ExecuteResult ctx = 
            r ctx }

result here is (ControllerContext -> unit) -> ActionResult

This is a pretty common pattern in F# to "functionalize" a single-method interface or class.

Let's also write a little function to execute an ActionResult:

let inline exec ctx (r: ActionResult) = 
    r.ExecuteResult ctx

Setting a status code and header are pretty common things to do. We should encapsulate them into their own ActionResults, and then we can compose them:

let status code = 
    result (fun ctx -> ctx.Response.StatusCode <- code) 

let header name value = 
    result (fun ctx -> ctx.Response.AppendHeader(name, value))

Now we'd like to define methodNotAllowed by composing these two ActionResults, for example:

let allow (methods: #seq<string>) = header "Allow" (String.Join(", ", methods))

let methodNotAllowed methods = status 405 >>. allow methods

Notice how the ControllerContext and Response are implicit now. We can define the >>. operator  like this:

let concat a b = 
    let c ctx = 
        exec ctx a 
        exec ctx b 
    result c

let (>>.) = concat 

That is, concat executes two ActionResults sequentially.

But wait a minute... "sequencing actions"... where have we seen this before? Yup, monads. We have just reinvented the Reader monad. Let's not reinvent it and instead make it explicit, but first we have to refactor Figment to use ControllerContext -> unit instead of ActionResult (it will be still used, but under the covers). This is a simple, mechanical, uninteresting refactor, so I won't show it. Just consider it done. Now we can define:

type ReaderBuilder() =
    member x.Bind(m, f) = fun c -> f (m c) c
    member x.Return a = fun _ -> a
    member x.ReturnFrom a = a

let result = ReaderBuilder()
let (>>.) m f = r.Bind(m, fun _ -> f)
let (>>=) m f = r.Bind(m,f)

>>. is just like Haskell's >> operator, described like this:

Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such as the semicolon) in imperative languages.

We can still define methodNotAllowed as above, or using computation expression syntax:

let methodNotAllowed methods = 
    result { 
        do! status 405 
        do! allow methods 
    }

Or, if you don't want to use monads, you can just pass the context explicitly:

let methodNotAllowed allowedMethods = 
    fun ctx -> 
        status 405 ctx 
        allow allowedMethods ctx

They're all equivalent definitions.

Now, after the last refactor ("unpacking" ActionResult) we changed the Figment action type from

ControllerContext -> ActionResult

to

ControllerContext -> (ControllerContext -> unit)

which is a pretty silly type if you think about it... but that's a refactoring tale for another day ;-)

I hope this post served to contrast object-oriented and functional code in a familiar environment (ASP.NET MVC), and to show how monads can arise "naturally", it's just a matter of recognizing them.

I should say that this is of course not the only way to do it, and I don't claim this is the best way to do it.

In the next post I'll show more uses and conveniences of having the action as a Reader monad.

Tuesday, April 12, 2011

Formlets in C# and VB.NET (part 2)

In my last post I introduced CsFormlets, a wrapper around FsFormlets that brings formlets to C# and VB.NET. I showed how to model a typical registration form with CsFormlets. Now let's see how we can use formlets in ASP.NET MVC.

To recap, here's the form we're modeling:

form_cs

and here's the top-level formlet's signature, the one we'll reference in our ASP.NET MVC controller:

static Formlet<RegistrationInfo> IndexFormlet() { ... }

RegistrationInfo contains all information about this form. You can place this anywhere you want. I chose to put all formlets related to this form in a static class SignupFormlet, in a Formlets directory and namespace. In MVC terms, formlets fulfill the responsibilities of view (for the form), validation and binding.

We'll start with a regular controller with an action to show the formlet:

public class SignupController : Controller {
    [HttpGet]
    public ActionResult Index() {
        return View(model: SignupFormlet.IndexFormlet().ToString());
    }
}

Our view is trivial (modulo styles and layout), as it's only a placeholder for the formlet:

Views/Signup/Index.cshtml

@using (Html.BeginForm()) {
    @Html.Raw(Model)
    <input type="submit" value="Register" />
}

Now we need an action to handle the form submission. The simplest thing to do is handling the formlet manually (let this be case 1):

[HttpPost]
public ActionResult Index(FormCollection form) {
    var result = SignupFormlet.IndexFormlet().Run(form);
    if (!result.Value.HasValue())
        return View(model: result.ErrorForm.Render());
    var value = result.Value.Value;
    return RedirectToAction("ThankYou", new { name = value.User.FirstName + " " + value.User.LastName });
}

The problem with this approach is, if you want to test the action you have to build a test form, so you're actually testing both binding and the processing of the bound object. No problem, just extract methods to the desired level, e.g. (case 2)

[HttpPost]
public ActionResult Index(FormCollection form) {
    var result = SignupFormlet.IndexFormlet().Run(form);
    return Signup(result);
}

[NonAction]
public ActionResult Signup(FormletResult<RegistrationInfo> registration) {
    if (!registration.Value.HasValue())
        return View(model: registration.ErrorForm.Render());
    return Signup(registration.Value.Value);
}

[NonAction]
public ActionResult Signup(RegistrationInfo registration) {
    return RedirectToAction("ThankYou", new { name = registration.User.FirstName + " " + registration.User.LastName });
}

So we can easily test either Signup method.

Alternatively we can use some ASP.NET MVC mechanisms. For example, a model binder (case 3):

[HttpPost]
public ActionResult Index([FormletBind(typeof(SignupFormlet))] FormletResult<RegistrationInfo> registration) {
    if (!registration.Value.HasValue())
        return View(model: registration.ErrorForm.Render());
    var value = registration.Value.Value;
    return RedirectToAction("ThankYou", new { name = value.User.FirstName + " " + value.User.LastName });
}

By convention the method used to get the formlet is [action]Formlet (you can of course override this).

We can take this even further with an action filter (case 4):

[HttpPost]
[FormletFilter(typeof(SignupFormlet))]
public ActionResult Index(RegistrationInfo registration) {
    return RedirectToAction("ThankYou", new {name = registration.User.FirstName + " " + registration.User.LastName});
}

In this case the filter encapsulates checking the formlet for errors and automatically renders the default action view ("Index" in this case, but this is an overridable parameter) with the error form provided by the formlet. The formlet and filter ensure that the registration argument is never null or invalid when it hits the action.

However convenient they may be, the filter and model binder come at the cost of static type safety. Also, in a real-world application, case 4 is likely not flexible enough, so I'd probably go for the integration case 2 or 3.

Testing formlets

Testing formlets themselves is simple: you just give the formlet a form (usually a NameValueCollection, unless a file is involved), then assert over its result. Since formlets are composable you can easily test some part of it independently of other parts. For example, here's a test for the credit card expiration formlet checking that it correctly validates past dates:

[Fact]
public void CardExpiration_validates_past_dates() {
    var twoMonthsAgo = DateTime.Now.AddMonths(-2);
    var result = SignupFormlet.CardExpiration().Run(new NameValueCollection { 
        {"f0", twoMonthsAgo.Month.ToString()},
        {"f1", twoMonthsAgo.Year.ToString()},
    });
    Assert.False(result.Value.HasValue());
    Assert.True(result.Errors.Any(e => e.Contains("Card expired")));
}

Conclusion

CsFormlets provides composable, testable, statically type-safe validation and binding to C# / VB.NET web apps. Here I showed a possible ASP.NET MVC integration which took less than 200 LoC; integrating with other web frameworks should be similarly easy.

One thing I think I haven't mentioned is that CsFormlets' FormElements generates HTML5 form elements, just like FsFormlets. In fact, pretty much everything I have written about formlets applies to CsFormlets as well, since it's just a thin wrapper.

All code shown here is part of the CsFormlets sample app. Code repository is on github. CsFormlets depends on FsFormlets (therefore also the F# runtime) and runs on .NET 3.5 SP1.

Tuesday, March 17, 2009

Block components in ASP.NET MVC (part 2)

In my last post I explored the possibility of block components in ASP.NET MVC. Actually, these posts should be titled "Block components in ASP.NET MVC / WebFormViewEngine". But WebFormViewEngine is the default view engine (a strong default) so whenever I say ASP.NET MVC it's WebFormViewEngine. Of course we could replace it and implement block components any way we want, but today we'll stick to defaults as much as we can, trying to keep it simple.

So far, I have only considered a single block. Let's now see what happens when trying to introduce several named blocks. We want to map the name of the block (a string) to the block itself (an Action). So... IDictionary<string, Action>:

box.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IDictionary<string, Action>>" %>
<div class="box">
    <div class="top"></div>
    <div class="title"><% Model["title"](); %></div>
    <div class="content">
        <% Model["content"](); %>
    </div>
    <div class="bottom"></div>
</div>

OK, that wasn't so bad... now let's see how we define the sections.

First attempt

It's a dictionary, so we could just give it a dictionary:

<% Html.RenderPartial("box", new Dictionary<string, Action> {
   {"title", () => {%>
<h1>the title</h1>
<%}}, {"content", () => { %>
<div>some content</div>
<%}}}); %>

This doesn't require any extra code but it's very ugly... we could try defining a small fluent interface for this:

Second attempt

<% Html.Partial("box")
.WithSection("title", () => {%>
    <h1>the title</h1>
<% })
.WithSection("content", () => {%>
    <div>some content</div>
<% })
.Render(); %>
Implementation:
public static class HtmlHelperExtensions {
    public static PartialWithSectionsRendering Partial(this HtmlHelper html, string viewName) {
        return new PartialWithSectionsRendering(html, viewName);
    }

    public class PartialWithSectionsRendering {
        private readonly HtmlHelper html;
        private readonly string viewName;
        private readonly IDictionary<string, Action> sections = new Dictionary<string, Action>();

        public PartialWithSectionsRendering(HtmlHelper html, string viewName) {
            this.html = html;
            this.viewName = viewName;
        }

        public PartialWithSectionsRendering WithSection(string section, Action render) {
            sections[section] = render;
            return this;
        }

        public void Render() {
            html.RenderPartial(viewName, sections);
        }
    }
}

Still too awkward... plus we could easily forget to call the final Render().

Third attempt

<% Html.RenderPartialWithSections("box", section => {
    section("title", () => { %>
        <h1>the title</h1>
    <% });
    section("content", () => { %>
        <div>some content</div>
    <% });
}); %>

This certainly looks better... the intention is expressed quite clearly, and the implementation is very simple:

public static class HtmlHelperExtensions {
    public static void RenderPartialWithSections(this HtmlHelper html, string viewName, Action<Action<string, Action>> sections) {
        var dict = new Dictionary<string, Action>();
        sections.Invoke((section, render) => dict[section] = render);
        html.RenderPartial(viewName, dict);
    }
}

Let's try this one more time:

Fourth attempt

<% Html.RenderPartialWithSections2("box", 
title => {%>
    <h1>the title</h1>
<% },
content => {%>
    <div>some content</div>
<% }); %>

This is shorter that the last attempt, but what's really that title variable? What would happen if we use it in the Action block? Let's see the implementation first:

public static class HtmlHelperExtensions {
    public static void RenderPartialWithSections2(this HtmlHelper html, string viewName, params Action<object>[] sections) {
        var dict = new Dictionary<string, Action>();
        foreach (var a in sections) {
            var name = a.Method.GetParameters()[0].Name;
            var render = a; // copied to avoid access to modified closure
            dict[name] = () => render.Invoke(null);
        }
        html.RenderPartial(viewName, dict);
    }
}

So if you use the title variable in your title Action block, Bad Things Will Happen. Namely, a NullReferenceException since it's just a dummy null object!

Conclusion

I'll stop there for now, but I have to confess that I don't really like any of these solutions :-) They work, but this problem can be expressed more elegantly in Brail. DSLs win here... unless I'm missing something!

Tuesday, March 10, 2009

Block components in ASP.NET MVC

A couple of days ago I was browsing stackoverflow for some questions to answer when I hit this one:

I have repetitious markup for elements, like:

<div class="box">
    <div class="top"></div>

    <div class="content">
      content goes here
    </div>
    <div class="bottom"></div>
</div>

Top and bottom are styled with CSS to include images for borders.

I could use JQuery to inject tags, but is there a better way to template controls in ASP.NET MVC?

MonoRail users might recognize this as a block component, that is, a ViewComponent (ViewUserControl in ASP.NET MVC / WebFormViewEngine terms) that accepts a block (or several blocks, also called sections) of HTML to be inserted somewhere within the ViewComponent.

The author of the question goes on to answer himself with two HtmlHelper extensions methods:

  public static void BeginBox(this HtmlHelper htmlHelper)
  {
      StringBuilder box = new StringBuilder();

      box.AppendLine("<div class=\"box\">");
      box.AppendLine("    <div class=\"top\"></div>");
      box.AppendLine("        <div class=\"content\">");

      htmlHelper.ViewContext.HttpContext.Response.Write(box.ToString());
  }

  public static void EndBox(this HtmlHelper htmlHelper)
  {
      StringBuilder box = new StringBuilder();

      box.AppendLine("        </div>");
      box.AppendLine("    <div class=\"bottom\"></div>");
      box.AppendLine("</div>");

      htmlHelper.ViewContext.HttpContext.Response.Write(box.ToString());
  }

This works just fine, but I strongly dislike having markup in code. Plus, we could forget to call Html.EndBox() which would break everything. Can we somehow move this to a ViewUserControl? By using an Action as the Model of the ViewUserControl, we can:

box.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Action>" %>
<div class="box">
    <div class="top"></div>
    <div class="content">
        <% Model(); %>
    </div>
    <div class="bottom"></div>
</div>

Then in the view:

<% Html.RenderPartial("box", Lambda.Action(() => { %>
content goes here
<% })); %>

Note the Lambda.Action helper method, which is defined as:

public static class Lambda {
    public static Action Action(Action a) {
        return a;
    }
}

This keeps the Action as an Action, otherwise the runtime tries to convert it to a ViewDataDictionary with the obvious conversion error that causes.

Also note that you could write any markup, call any helper, or reference any ViewData or Model property of the view, instead of the simple text "content goes here".

The thing gets more interesting as we add support for multiple sections, which I'll blog about next. (see second part)

Tuesday, February 17, 2009

ASP.NET MVC postback support

In my unwanted quest to port a MonoRail app to ASP.NET MVC, I have to use existing master pages and user controls.

Bad news is, some of these user controls post back and then all hell breaks loose. Specifically, I started getting this exception on every postback:

Line 338: <% if (Model.PaymentOptions.Count > 0) {%>
Line 339: <div class="fleft" style="margin:2px 20px 2px 0px; padding: 0px;">
Line 340: <% Html.RenderPartial("ListingDetailPaymentOption", Model.PaymentOptions[0]);%>
Line 341: </div>
Line 342: <% }%>
[HttpException]: Unable to validate data.
   at System.Web.Configuration.MachineKeySection.GetDecodedData(Byte[] buf, Byte[] modifier, Int32 start, Int32 length, Int32& dataLength)
   at System.Web.UI.ObjectStateFormatter.Deserialize(String inputString)
[ViewStateException]: Invalid viewstate. 
	Client IP: 127.0.0.1
	Port: 18559
	User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6 (.NET CLR 3.5.30729)
	ViewState: /wEPDwUKLTc0NTAwNTg1NA9kFgJmD2QWAmYPZBYCZg9kFgICBw9kFgICBQ9kFgICAw9kFgICAw9kFgICAg9kFgJmDxYCHgtfIUl0ZW1Db3VudAIFFgpmD2QWAmYPFQk8LzIwMDZfTGFuZF9Sb3Zlcl9SYW5nZV9Sb3Zlcl9TcG9ydF9TdXBlcmNoYXJnZWRfMTEyNjg3LnhodG1sBDIwMDYpTGFuZCBSb3ZlciBSYW5nZSBSb3ZlciBTcG9ydCBTdXBlcmNoYXJnZWQ1L3Bob3Rvcy9pbWdzaG93Uy5hc3B4P2lkPTI0MTgmc2l6ZT10XzEyMHg5MCZZZWFyPTIwMDY8LzIwMDZfTGFuZF9Sb3Zlcl9SYW5nZV9Sb3Zlcl9TcG9ydF9TdXBlcmNoYXJnZWRfMTEyNjg3LnhodG1sBDIwMDYpTGFuZCBSb3ZlciBSYW5nZSBSb3ZlciBTcG9ydCBTdXBlcmNoYXJnZWQCMTkHJDk1MC4wMGQCAQ9kFgJmDxUJJy8yMDA2X01lcmNlZGVzX0UzNTBfU2VkYW5fXzExMjk4MC54aHRtbAQyMDA2FE1lcmNlZGVzIEUzNTAgU2VkYW4gKy9waG90b3MvaW1nc2hvdy5hc3B4P2lkPTY4MjE4JnNpemU9dF8xMjB4OTAnLzIwMDZfTWVyY2VkZXNfRTM1MF9TZWRhbl9fMTEyOTgwLnhodG1sBDIwMDYUTWVyY2VkZXMgRTM1MCBTZWRhbiACMTIHJDQ4OC4wMGQCAg9kFgJmDxUJLy8yMDA2X01lcmNlZGVzX0NMUzUwMF9Db3VwZV80X0Rvb3JfMTA4NjI4LnhodG1s...
[HttpException]: Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.
   at System.Web.UI.ViewStateException.ThrowError(Exception inner, String persistedState, String errorPageMessage, Boolean macValidationError)
   at System.Web.UI.ViewStateException.ThrowMacValidationError(Exception inner, String persistedState)
   at System.Web.UI.ObjectStateFormatter.Deserialize(String inputString)
   at System.Web.UI.ObjectStateFormatter.System.Web.UI.IStateFormatter.Deserialize(String serializedState)
   at System.Web.UI.Util.DeserializeWithAssert(IStateFormatter formatter, String serializedState)
   at System.Web.UI.HiddenFieldPageStatePersister.Load()
   at System.Web.UI.Page.LoadPageStateFromPersistenceMedium()
   at System.Web.UI.Page.LoadAllState()
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest()
   at System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context)
   at System.Web.UI.Page.ProcessRequest(HttpContext context)
   at System.Web.Mvc.ViewPage.RenderView(ViewContext viewContext)
   at System.Web.Mvc.ViewUserControl.RenderViewAndRestoreContentType(ViewPage containerPage, ViewContext viewContext)
   at System.Web.Mvc.ViewUserControl.RenderView(ViewContext viewContext)
   at System.Web.Mvc.WebFormView.RenderViewUserControl(ViewContext context, ViewUserControl control)
   at System.Web.Mvc.WebFormView.Render(ViewContext viewContext, TextWriter writer)
   at System.Web.Mvc.HtmlHelper.RenderPartialInternal(String partialViewName, ViewDataDictionary viewData, Object model, ViewEngineCollection viewEngineCollection)
   at System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper htmlHelper, String partialViewName, Object model)
   at ASP.views_listingdetail_index_aspx.__Render__control2(HtmlTextWriter __w, Control parameterContainer) in c:\trabajo\web\WEB2\Views\ListingDetail\Index.aspx:line 340
   at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
   at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
   at System.Web.UI.Control.Render(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
   at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
   at System.Web.UI.Control.Render(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
   at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
   at System.Web.UI.Control.Render(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
   at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
   at System.Web.UI.HtmlControls.HtmlForm.RenderChildren(HtmlTextWriter writer)
   at System.Web.UI.HtmlControls.HtmlContainerControl.Render(HtmlTextWriter writer)
   at System.Web.UI.HtmlControls.HtmlForm.Render(HtmlTextWriter output)
   at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.HtmlControls.HtmlForm.RenderControl(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
   at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
   at System.Web.UI.Control.Render(HtmlTextWriter writer)
   at Templates.master.Render(HtmlTextWriter writer) in C:\trabajo\web\WEB2\templates\master.master.vb:line 29
   at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
   at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
   at System.Web.UI.Control.Render(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
   at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
   at System.Web.UI.Control.Render(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
   at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
   at System.Web.UI.Page.Render(HtmlTextWriter writer)
   at System.Web.Mvc.ViewPage.Render(HtmlTextWriter writer)
   at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest()
   at System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context)
   at System.Web.UI.Page.ProcessRequest(HttpContext context)
   at ASP.views_listingdetail_index_aspx.ProcessRequest(HttpContext context) in c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\ffa83e12\a51f62df\App_Web_index.aspx.1cc8d514.vtl-v5gs.0.cs:line 0
   at System.Web.Mvc.ViewPage.RenderView(ViewContext viewContext)
   at System.Web.Mvc.WebFormView.RenderViewPage(ViewContext context, ViewPage page)
   at System.Web.Mvc.WebFormView.Render(ViewContext viewContext, TextWriter writer)
   at System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass11.b__e()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass11.<>c__DisplayClass13.b__10()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
   at System.Web.Mvc.Controller.ExecuteCore()
   at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
   at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
   at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext)
   at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext)
   at System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

As usual, first thing I did was googling the exception, but the standard answer was: "Postback is not supported in MVC". I also found this Microsoft Connect issue that confirmed that this is a bug.

What I found weird is that the exception was actually throwing in the ViewUserControl (MVC's user control), not the legacy WebForms user controls. I took a look at the source of ViewUserControl.RenderView() and found that the rendering is actually done by creating a fake ViewPage, and that the ViewState validation in this ViewPage was the one failing. But, since in MVC we don't use ViewState, we can just turn it off. So I created this little base class to do that, just inherit from this one instead of ViewUserControl<T>:

public class ViewUserControlWithoutViewState<T> : ViewUserControl<T> where T : class {
    protected override void LoadViewState(object savedState) {}

    protected override object SaveControlState() {
        return null;
    }

    protected override void LoadControlState(object savedState) {}

    protected override object SaveViewState() {
        return null;
    }

    /// <summary>
    /// extracted from System.Web.Mvc.ViewUserControl
    /// </summary>
    /// <param name="viewContext"></param>
    public override void RenderView(ViewContext viewContext) {
        viewContext.HttpContext.Response.Cache.SetExpires(DateTime.Now);
        var containerPage = new ViewUserControlContainerPage(this);
        ID = Guid.NewGuid().ToString();
        RenderViewAndRestoreContentType(containerPage, viewContext);
    }

    /// <summary>
    /// extracted from System.Web.Mvc.ViewUserControl
    /// </summary>
    /// <param name="containerPage"></param>
    /// <param name="viewContext"></param>
    public static void RenderViewAndRestoreContentType(ViewPage containerPage, ViewContext viewContext) {
        string contentType = viewContext.HttpContext.Response.ContentType;
        containerPage.RenderView(viewContext);
        viewContext.HttpContext.Response.ContentType = contentType;
    }

    /// <summary>
    /// Extracted from System.Web.Mvc.ViewUserControl+ViewUserControlContainerPage
    /// </summary>
    private sealed class ViewUserControlContainerPage : ViewPage {
        // Methods
        public ViewUserControlContainerPage(ViewUserControl userControl) {
            Controls.Add(userControl);
            EnableViewState = false;
        }

        protected override object LoadPageStateFromPersistenceMedium() {
            return null;
        }

        protected override void SavePageStateToPersistenceMedium(object state) {}
    }
}
DISCLAIMER: it worked for my specific case, I'm not claiming this covers all possible cases of this exception! Hopefully this problem will be addressed soon by Phil et al...

Tuesday, February 10, 2009

ARFetch for ASP.NET MVC

I'm currently in the unfortunate position of having to merge part of a MonoRail + ActiveRecord + Windsor (the full Castle stack) project with a huge existing Webforms project. I have to use existing master pages and lots of user controls, and MonoRail's support for these is rather scarce, so I thought of using ASP.NET MVC instead of trying to cram things into webforms. I already had Windsor working on the webforms project, setting up ActiveRecord is a snap, and integrating ASP.NET MVC with Windsor is a breeze thanks to MvcContrib. But I couldn't find anything to integrate ASP.NET MVC with ActiveRecord.

Luckily, MonoRail's IParameterBinder and ASP.NET MVC's IModelBinder aren't that different in spirit. In order to implement ARFetching, I only had to make a couple of minor changes to the ARFetcher and ARFetchAttribute classes.

Here's what a simple CRUD controller would look like using ARFetch:

public class PersonController : Controller {
  //
  // GET: /Person/

  public ActionResult Index() {
      return View(ActiveRecordMediator<Person>.FindAll());
  }

  //
  // GET: /Person/Details/5

  public ActionResult Details([ARFetch("id")] Person person) {
      if (person == null)
          return RedirectToAction("Index");
      return View(person);
  }

  //
  // GET: /Person/Create

  public ActionResult Create() {
      return View();
  }

  //
  // POST: /Person/Create

  [AcceptVerbs(HttpVerbs.Post)]
  public ActionResult Create(FormCollection collection) {
      try {
          var p = new Person();
          UpdateModel(p, collection.ToValueProvider());
          ActiveRecordMediator<Person>.Save(p);
          return RedirectToAction("Index");
      } catch {
          return View();
      }
  }

  //
  // GET: /Person/Edit/5

  public ActionResult Edit([ARFetch("id")] Person person) {
      return View(person);
  }

  //
  // POST: /Person/Edit/5

  [AcceptVerbs(HttpVerbs.Post)]
  public ActionResult Edit([ARFetch("id")] Person person, FormCollection collection) {
      try {
          UpdateModel(person, collection.ToValueProvider());
          ActiveRecordMediator<Person>.Update(person);
          return RedirectToAction("Index");
      } catch {
          return View();
      }
  }
}

Here's the full source code, all merit goes to the Castle team, any bugs are mine:

Monday, February 2, 2009

Repeater with separator for ASP.NET MVC

Today I needed to repeat over a list of items in my views, separating each item with some html. A simple foreach is no good in this case. I really liked Phil Haack's code-based repeater syntax, but it handles only alternating items, not separators. I didn't need the alternating items, so I just wrote this extension with separator support:

public static class HtmlHelperRepeatExtensions {
    private static Func<T, U> ActionToFunc<T, U>(Action<T> a) {
        return t => {
            a(t);
            return default(U);
        };
    }

    private static Func<T> ActionToFunc<T>(Action a) {
        return () => {
            a();
            return default(T);
        };
    }

    public static void Repeat<T>(this HtmlHelper html, IEnumerable<T> items, Action<T> render, Action separator) {
        var frender = ActionToFunc<T, int>(render);
        var fseparator = ActionToFunc<int>(separator);
        items.Select<T, Func<int>>(e => () => frender(e)).Intersperse(fseparator).Select(f => f()).ToArray();
    }

    // From http://blogs.msdn.com/wesdyer/archive/2007/03/09/extending-the-world.aspx
    private static IEnumerable<T> Intersperse<T>(this IEnumerable<T> sequence, T value) {
        bool first = true;
        foreach (var item in sequence) {
            if (first)
                first = false;
            else
                yield return value;
            yield return item;
        }
    }
}

Sample usage:

<% Html.Repeat(Model.Products, p => { %> 
    <div><%= p.Id%></div>
    <div><%= p.Name%></div>
<%}, () => { %> 
    <br /> <%-- separator --%>
<% }); %>

It's worth mentioning that I had to convert the rendering Actions into Funcs in order to write this in a functional style... in F# this would definitely look prettier :-)

Tuesday, February 5, 2008

Trying out ASP.NET MVC

A couple of weeks ago I wrote a web front-end for managing faxes on a fax server using Castle.Facilities.WindowsFax and ASP.NET MVC, mostly to get a feel for the latter. Although it's a very simple app (a couple of features are still missing), I got some impressions:

  • Windsor integration is a breeze thanks to MVCContrib.
  • I missed MonoRail's AccesibleThrough property. There is another option, setting the routing... but I kind of like the property better. Matter of taste, I guess.
  • Ajax is trivial thanks to the normal MVC routing, just like in MonoRail. I'm not a big fan of JS generation, I prefer to cut out the middle man and use directly jQuery, so I used the great taconite plugin to render ajax responses.
  • I like the possibility of having public methods on my controllers without them being exposed to the client. It would make testing easier in some cases. I know there is quite a controversy about testing private methods... or breaking encapsulation in order to enhance testability, but what if I wanted to test method GetRecords() on this controller? Sometimes it is convenient. No, I'm NOT saying this is good practice, just that it's nice to have the choice. Choice is good. And if you don't like [ControllerAction], you can always use Phil Haack's ConventionController. See? Choice :-)

Code is here.