Most people coming to ASP.NET MVC tend to have similar problems. Most of them are related to refreshed understanding of HTML+HTTP limitations. Some are mitigated by ASP.NET MVC (checkboxes), but a lot require custom solutions.

One of the more common problems I get asked about by every second person learning MVC is how to make a form with two independent submit buttons, where both should submit the same data, but a have a different processing logic. For example, form may have a Save Draft button and Publish button.

Until now, I always said that it is possible, Google/StackOverflow and find out. But I just had to build such kind of thing myself, so I finally took some time to find/build the final solution.

So, let’s look at the solutions available online.

StackOverflow question “How do you handle multiple submit buttons in ASP.NET MVC Framework?” is the starting point, but it does not provide a good solution for the situation where you need to send same data for both buttons, except the solution to switch by button name, which is duct-taping.

Post “ASP.NET MVC – Multiple buttons in the same form” by David Findley is much more interesting, since his AcceptParameterAttribute is very similar to my solution. However, this has several (small) shortcomings: first, you have to specify what is actually an action you want to do in an attribute. So even if you name your action “SaveDraft”, you will still need to specify AcceptParameter(Name=“button”, Value=“saveDraft”). Another thing is need to put [ActionName] on your actions, which is understandable, but a bit confusing for people who do not yet know the idea.

So, I wanted to build the solution that would require at most one attribute, and where the name of action method corresponds to the attributes of the button. Also, since <input> value is the thing being shown in the button, and <button> value has issues in IE, I decided to go with name attribute and ignore value completely (which seems to be consistent with David Findley’s commenters).

Now, the solution. Basically, instead of using ActionMethodSelectorAttribute, I am using ActionNameSelectorAttribute, which allows me to pretend the action name is whatever I want it to be. Fortunately, ActionNameSelectorAttribute does not just make me specify action name, instead I can choose whether the current action matches request.

So there is my class (btw I am not too fond of the name):

public class HttpParamActionAttribute : ActionNameSelectorAttribute {
    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) {
        if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
            return true;

        if (!actionName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
            return false;
        
        var request = controllerContext.RequestContext.HttpContext.Request;
        return request[methodInfo.Name] != null;
    }
}

How to use it? Just have a form similar to this:

<% using (Html.BeginForm("Action", "Post")) { %>
  <!— …form fields… -->
  <input type="submit" name="saveDraft" value="Save Draft" />
  <input type="submit" name="publish" value="Publish" />
<% } %>

and controller with two methods

public class PostController : Controller {
    [HttpParamAction]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult SaveDraft() {
        //…
    }

    [HttpParamAction]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Publish() {
        //…
    }
}

As you see, the attribute does not require you to specify anything at all. Also, name of the buttons are translated directly to the method names. Additionally (I haven’t tried that) these should work as normal actions as well, so you can post to any of them directly.

There is some room for improvements (hardcoded “action” as a default action name), but in general I’m satisfied with this solution.

There are situations when you do not need to get the fully tracked entities using NHibernate — you know you wouldn’t ever edit them and need minimum overhead for this specific scenario. One example is AJAX auto-completion. On the other hand, if you are as obsessed with architectural purity as me, you probably do not have Id properties in your entities, since they are artefacts of (relational) DBs.

So there is the question: in UI layer we want to do

repository.Query().Select(x => new ListItem { Key = x.Id, Name = x.Name })

But there are no “Id” properties in our entities. How can we do this (without returning to the dark ages of untyped criteria)?

There is a very simple (and working) answer. Let’s start with how I do it for individual entities. When I need a key/id in the UI to identify the entity between requests, I use repository.GetKey(entity), which internally calls session.GetIdentifier(entity). Simple and not intrusive into domain logic. Now,

repository.Query().Select(x => new ListItem { Key = GetKey(x), Name = x.Name })

is obviously impossible, since HQL/DB can not understand GetKey call.

Ok, so the solution is to pre-process the call before Linq-to-NHibernate and replace GetKey call with reference to fake property named “id”, which is a magic name NHibernate understands as identifier reference. Linq-to-NHibernate even provides public expression visitor, so it was trivial to create KeyMethodToIdRewritingVisitor (the fake PropertyInfo took most effort, which had to have some stuff to fool Expression.Property).

You can get resulting code below.
It is not perfect, but it works and flaws are really easy to polish out.

  1. Repository
  2. KeyMethodToIdRewritingVisitor
  3. KeyEnabledQueryProvider

Is a same method you would use with NMock2, but with “DynamicProxyGenAssembly2″ in InternalsVisibleTo.
This strange name is a default dynamic assembly name used by Castle.DynamicProxy.

Do not forget that the attribute should be added to the assembly with internal types, not to the tests assembly (obvious, but I got it wrong the first time).

Update: yesthatmcgurk has pointed me to the fact that for strongly named assemblies you have to specify the public key as well. So the correct attribute for the strongly named assembly should be

[assembly:InternalsVisibleTo("DynamicProxyGenAssembly2, 
PublicKey=002400000480000094000000060200000024000052534
1310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b
3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d926665
4753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb
4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c486
1eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] 
// without line-breaks