Dynamic ASP.NET MVC 3 models using Mono’s Compiler as a Service

I had an idea for an interactive MVC sample which will let you see the scaffolded editor and display for a model. I thought about a few ways to do this, the first being Mono’s compiler as a service. So far it’s a partial success – the model scaffolding works, but the attributes don’t (very possibly my mistake).

Getting the latest Mono Compiler

Miguel recommended that I grab the latest Mono build off Github, since there have been some improvements and bug fixes in the compiler. He wasn’t kidding, there’s a lot of active work going on in the compiler, and the Evaluator class (the main compiler service class) is no exception. Here’s the commit history just for the Evaluator (eval.cs):

https://github.com/mono/mono/commits/871ad48b98346cffc194b6ebe458a93d88dba009/mcs/mcs/eval.cs

A quick skim of the mcs (mono csharp compiler) commit messages in general shows a lot of work going on, including work on async: https://github.com/mono/mono/tree/master/mcs/mcs

The biggest change since the last Mono release, as Miguel blogged about in February, is the change from static API to an instance API:

…we turned the static API Evalutor.Eval (string expression), into an instance API. The instance API allows developers that are embedding the C# compiler-as-a-service in their application to have different contexts.

This required the entire compiler to be updated from being a giant set of static classes that could safely use global variables and state into a state that was properly encapsulated.

The API is now richer, we provide a way to configure the compiler settings using a settings class. This can be populated either manually, or by using the build-in command-line parser for compiler options.

This is good news – I find the new API easier to work with. The magical static stateful service API pattern works great for the REPL sample but causes problems if you’re doing anything much more complex. The downside, though, is that just about all the code samples and blog posts on the the Mono compiler service stuff don’t work as-is against the new compiler.

I didn’t want to set up a build environment for Mono. Fortunately, most established open source projects offer regular builds if you look around a bit. I grabbed the most recent MonoCharge package from http://mono.ximian.com/daily/, which includes all the precompiled binaries. They’re packaged as a .tar.gz, which opens up just fine in 7-zip. I just grabbed the Mono.CSharp.dll assembly.

2011-05-20 22h14_44

Using the Mono Compiler Service in an application

It’s pretty easy to put use the compiler in an app:

  1. Reference Mono.CSharp.dll
  2. Add a using statement for Mono.CSharp
  3. New up an Evaluator and get to work:
var report = new Report(new ConsoleReportPrinter());
var evaluator = new Evaluator(new CompilerSettings(), report);
evaluator.Run("DateTime.Now");

The evaluator constructor requires a ConsoleReportPrinter and CompilerSettings, neither of which we care about, so we’re just passing them in to make everyone happy. Are you happy? Me, too. Onward!

Using Mono Compiler in an ASP.NET MVC Application

ASP.NET MVC 3 views are typed as ViewPage<dynamic> and the Html.EditorForModel scaffolding stuff works via reflection, so theoretically we could create a new model on the fly and lob it over to the view and things should just work, right? Let’s see.

First, we’ll create the controller that builds a dynamic class. I’m throwing in a property with a dynamic name just to make it clear that this entire model is coming from a string.

using System.IO;
using System.Reflection;
using System.Web.Mvc;
using Mono.CSharp;
using System;

namespace DynamicModelCompilation.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            string modelDefinition =
            @"
              public class Monkey
              {
                    public string Name { get; set; }
                    public string Password { get; set; }
                    public bool IsHappy { get; set; }
                    public bool GetsDownOn" 
                        + DateTime.Now.DayOfWeek + 
                    @" { get; set; }
                }
            ";

            var report = new Report(new ConsoleReportPrinter());
            var evaluator = new Evaluator(new CompilerSettings(), report);
            evaluator.Run(modelDefinition);
            dynamic model = evaluator.Evaluate("new Monkey();");

            return View(model);
        }
    }
}

The above code:

  1. Builds a string which we’ll be passing to the compiler
  2. Creates a compiler (as we saw earlier)
  3. Calls Evaluator.Run, which executes the code string passed to it
  4. Calls Evaluator.Evaluate on “new Monkey()” which news up an instance of our model and returns it

Next, we’ll set up a simple Index view that displays an editor template for based on whatever model object is passed to it:

@{
    ViewBag.Title = "Home Page";
}
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true, "Account creation was unsuccessful. Please correct the errors and try again.")

    <fieldset>
        @Html.EditorForModel()
    </fieldset>
    <input type="submit" value="Processinate Form" />
}

Running the application show that our nefarious plan has succeeded:

2011-05-21 01h51_12

Pushing Our Luck – Trying Out Some Attributes

A lot of the goodness in the MVC templates comes from the way the helpers work with data annotations. You can set display names for properties (e.g. changing the display label for IsHappy to "Is this monkey happy?"), setting validation requirements for edit forms, etc. I took a shot at adding those in to the model class.

First off, I needed to include a reference to the System.ComponentModel.DataAnnotations assembly in my Mono Evaluator session. That's easy enough to do with a call to LoadAssembly, presuming you have the full path to the assembly. I copied System.ComponentModel.DataAnnotations to my /lib file and referenced it like this:

System.Uri uri = new System.Uri(Assembly.GetExecutingAssembly().CodeBase);
string path = Path.GetDirectoryName(uri.LocalPath).Replace("\\DynamicModelCompilation\\bin", "\\lib");
evaluator.LoadAssembly(Path.Combine(path, "System.ComponentModel.DataAnnotations.dll"));

That adds the reference to the compiler session, but you still need to run a using statement, like this:

evaluator.Run("using System.ComponentModel.DataAnnotations;");

Finally, I added some annotations to my model:

public class Monkey
{
	[Required]
	[StringLength(3, ErrorMessage = ""The {0} must be at least {2} characters long."", MinimumLength = 1)]
	public string Name { get; set; }

	[Required]
	[DataType(DataType.Password)]
	public string Password { get; set; }
	
	[Display(Name = ""Is this monkey happy?"")]
	public bool IsHappy { get; set; }
}

Note: if you get any of those things wrong, you get a slightly cryptic exception on your call to evaluator.Evaluate() - Argument Exception: The expression did not set a result.

Here's how the entire controller looks:

Unfortunately, the attributes didn't have any effect. It's not like anything broke, but the form looked the exact same:

2011-05-21 01h51_12

I dug into the model properties using model.GetType().GetProperties() - it looks like they're present, but maybe there's something about them that ASP.NET MVC finds repugnant? Spelunking through the model properties' custom attributes shows that they are indeed present.

The end? We'll see - I'll ask / poke around and see why the attributes aren't being picked up.

References / more info on using the Mono compiler as a service:

http://docs.go-mono.com/Mono.CSharp.Evaluator.Compile%20(string)

http://docs.go-mono.com/Mono.CSharp.Evaluator.Evaluate%20(string)

http://docs.go-mono.com/Mono.CSharp.Evaluator.Run%20(string)

http://www.amazedsaint.com/2010/09/how-to-host-monos-csharp-compiler-in.html

http://tirania.org/blog/archive/2011/Feb-24.html

10 Comments

  • Hi Jon,

    I'm seeing the Display Attribute showing up in the UI (i.e. the Label for IsHappy says "Is this monkey happy?").

    Also, the DataType=Password causes blobs to be used when typing.

    However, for the moment, the Required/StringLength Attributes do not behave as expected.

    Martin


  • Hi Jon,

    Update - I had JavaScript disabled earlier...

    With JavaScript enabled, the Required/StringLength Attributes get applied in the client browser, i.e. it works on my machine.

    Martin

  • I think:

    "I copied System.ComponentModel.DataAnnotations to my /lib file and referenced it like this"

    Might explain it? The Mono container and .NET are actually referencing two different DLLs and so when MVC is looking for attributes it is recognizes, its falling short?

    Worth a look anyway :)

    Kieran

  • @Martin, @Kiernan - Interesting. Maybe something changed in a newer Mono release? I'll try to check it out over the weekend.

  • I found that if I use the System.ComponentModels.DataAnnotations.dll in MonoCharge, it worked fine.

  • Did you ever get this working ?

  • Nefarious Rhinoceros - a large and powerful animal. he did not as weighty as the white rhinoceros, but still impressive - reaches the majority 2-2, 2 m, lengths of up to 3, 15 m in acme shoulders of 150-160 cm.

  • Thanks for the auspicious writeup. It in truth was a leisure account it.

    Look advanced to far delivered agreeable from you!
    By the way, how could we communicate?

  • Excellent goods from you, man. I have understand your stuff previous to
    and you're just too excellent. I really like what you have acquired here, certainly like what you're saying and the way in which you say it.
    You make it entertaining and you still take care
    of to keep it sensible. I cant wait to read much more from
    you. This is actually a tremendous web site.

  • Yes! Finally something about weight loss".

Comments have been disabled for this content.