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)

Wednesday, March 4, 2009

Windsor facility for Quartz.NET

A few months ago I wrote a couple of adapters to integrate Quartz.Net into Windsor. These were loose components that you had to register yourself and the configuration wasn't very friendly. So I decided to wrap them in a facility to make things cleaner and easier.

This is what the facility provides:

Here's what it doesn't support:

  • These entities are NOT Windsor-managed (they are instantiated normally by Quartz instead)
  • Trigger and job group names

Note that it's up to you to register jobs with the appropriate lifestyle. Listeners can only have singleton lifestyle since they're injected in IScheduler which is itself a singleton.

Here are the bits: