3
Vote

Calling Run or Parse with Interface or Abstract Type

description

When calling the Razor.Parse or Razor.Run with an object that is an interface or an abstract class, it does not use the generic type of the object. Can you expose another method for this, or handle appropriately?
 
We had to do something like this to get it to work:
 
return (string) typeof (Razor).GetMethods()
                                .Where(m => m.Name == "Run" && m.ContainsGenericParameters)
                                .First()
                                .MakeGenericMethod(model.GetType())
                                .Invoke(null, new[] {(object) model, compiledTemplateName});
 
Sample Test:
[Test]
    public void Renders_correctly_when_model_is_supplied_as_base_type_or_interface()
    {
        _templateLoader.Stub(m => m.Load("ParentTemplate")).Return("Parent Template -- @Include(\"ChildTemplate\", Model.Val2) --");
        _templateLoader.Stub(m => m.Load("ChildTemplate")).Return("Child Template and @Model");

        var message = new MailMessage("test1@glgroup.com", "Test2@glgroup.com", "Test3@glgroup.com", "");
 
        try
        {
            _emailer.SendEmail(message, (IEmailModel)new TestEmailModel(), "ParentTemplate");
        }
        catch (Exception e)
        {
            if (e is TemplateCompilationException)
            {
                Console.WriteLine(string.Join(",", ((TemplateCompilationException)e).Errors));
            }
            else
            {
                Console.WriteLine(e);
            }
        }
 
        Assert.That(message.Body, Is.EqualTo("Parent Template -- Child Template and It's a val2 --"));
    }

comments

chrisvenus wrote May 2, 2012 at 1:28 PM

I've encountered a similar problem with a base class trying to pass "this" into the Razor.Parse method.

From what I can see the problem stems from the fact that the Parse method (and I assume the run method) use typeof(T) to get the type rather than model.GetType() so it binds to the compile time type (eg the interface, abstract class, etc.) rather than the actual runtime type of the object.

I'm not familiar enough with Razor to know if there is a good reason for this or if it is just a bug. In general though I would have thought youw ant to use the specific runtime type - if the template expects the compiletime type then it will still work if it expects the runtime type then it will work.

Conversely as it currently stands if the template expects the compile time type then it works but if it expects the runtime type then it throws errors.

It looks like it should be easy enough to fix though I guess the harder bit is updating all the tests that are run to make sure it does the right thing to include this new scenario. :)