What would you like to see?

Coordinator
Feb 10, 2011 at 7:29 AM

Simple question :)

We're looking at the featureset to introduce as part of the v2.2 build.  What features would you like to see? Either reply here, or tweet #razorengine.

Feb 10, 2011 at 4:33 PM
Edited Feb 10, 2011 at 4:36 PM

I would be very interested to see built-in support for child templates, and a number of helper methods, as seen in this topic.

Another one at the top of my list is recompiling modified templates (see here).

Also, I came across some problems regarding thread safety when running the same compiled template multiple times. I wrapped your code a bit to allow registering templates, but in my unit tests I basically created two templates that render some text and a value passed in the model. The output was garbled in such way that sometimes multiple values were inserted in the same rendering after each other.
I solved it for me in my own code by putting a lock around the "RazorEngine.Razor.Run(model, templateName)" call based on the templatename (which I store in a list of known templates in my wrapper), but it would be more correct if this was handled in your code. Here's what my unit test roughly looks like:

// My template is very basic and looks like this. I use String.Format
// in order to supply two templates with a different base text.
var templateText = "This is template '{0}' with value '@(Model.Value)'";

// I do some wrapping in my code, but two templates get registered.
RazorEngine.Razor.CompileWithAnonymous(templateA, "a");
RazorEngine.Razor.CompileWithAnonymous(templateB, "b");

// I prepare an action.
var action = new Action<object>( o => {
    var iteration = (string) o;
    var result = RazorEngine.Razor.Run(new {Value = iteration}, "a");
    Assert.IsTrue(result.Contains("Template 'A'"));
    Assert.IsTrue(result.EndsWith("value '" + iteration + "'", result));

    result = RazorEngine.Razor.Run(new {Value = iteration}, "b");
    Assert.IsTrue(result.Contains("Template 'B'"));
    Assert.IsTrue(result.EndsWith("value '" + iteration + "'", result));
});

// I create tasks.
var tasks = new List<Task>();
for (int i = 0; i < 5000; i++)
    tasks.Add(new Task(action, i.ToString()));

// I start the tasks. (Separated from the definition, because I
// want to start a stopwatch right before starting the rendering.)
tasks.ForEach(t => t.Start());

try {
    Task.WaitAll(tasks.ToArray());
}
catch(AggregateException ex){
    // An exception gets thrown, and you can investigate the 
    // .InnerExceptions property for all faulty threads.
    ex.InnerExceptions.ToList().ForEach(inner => console.WriteLine(inner));
    throw;
}

My output often contained '1'1'3', '3984'3983' or similar, or sometimes only the wrong value at the location of the model value.

Coordinator
Feb 11, 2011 at 7:27 AM

Could you clarify what you mean by child templates? v2 supports injecting subtemplates using @Include(templateName, model), etc. Is this what you mean?

The thread safety issue is a good point. Currently when a template executes in writes the result to a Result property of the template, which is then read. These are two distinct operations. What we may have to do is wrap both calls in a GetResult method which we can make thread safe. This is a breaking change as it means redefining our interface ITemplate. Very possible to do though! :)

Feb 11, 2011 at 7:40 AM
AntarisZX wrote:

Could you clarify what you mean by child templates? v2 supports injecting subtemplates using @Include(templateName, model), etc. Is this what you mean?

I guess so. :) Sorry, it's a bit hard to figure without real documentation and basing myself on partly out-of-date discussions.
I took a look at the code itself now (TemplateBase), and noticed the available properties and functions. Thanks.

Mar 23, 2011 at 5:42 PM

Been loving Razor so far. I have one caveat I would like to point out: use of the <text/> markup feels like a hack to me.

I would rather use this: @text { My text is here. }

Than this: <text>My text is here.</text>

Since the <text/> markup is removed and not rendered, why use HTML markup for a specific need of Razor?

Stay consistent with the Razor syntax to reduce confusion.

You can leave <text/> support in there, but please add @text{} ... then we can pick which style we want to use.

Coordinator
Mar 23, 2011 at 6:19 PM

Hi,

The <text> stems from Razors inherit support for xml-like markup languages. As it's parsing content, it transitions between code and markup states. In certain scenarios, it is difficult for the parser to know whether the section is code or text. The <text> element is actually part of the core Razor library, written by Microsoft, not anything we've added in ourselves.  Luckily they've introduced and alternative 1-line syntax:

@foreach (var item in Model.Items) {
    @: Some Text
}

Mar 23, 2011 at 8:00 PM

Okay, should I contact Scott Gu or someone else with the suggestion?

I saw the @: syntax, but again, that's only useful for 1 line at a time... not so great for multiple lines.

Coordinator
Mar 24, 2011 at 4:18 PM
Edited Mar 24, 2011 at 4:30 PM

Ok, I have implemented a test syntax with your suggested @text { }. I'm not sure how well it works, as of yet, but it does work. Additionally it allows for code within the block. I'm going to do more testing before I submit a patch so don't hold your breath just yet :)

 

<html>
<body>
    @text {
        This is going to be output as plain text
    }

    @text {
        <li>@DateTime.Now.ToString()</li>
    }

    @if(true) {
        @text { This will be output because it's true }
    } else {
        @text { This won't be output }
    }

    @text {
         this is a 
         multi-line
         text block
    }
</body>
</html>

Mar 24, 2011 at 4:25 PM

Whoa, awesomeness! Thanks dude.

Coordinator
Mar 24, 2011 at 4:36 PM
Edited Mar 24, 2011 at 4:42 PM

Not a problem :) I was glad to do it...I don't get to work on this project as much as I'd like to anymore. Once I confer with Antaris we'll see if there are any changes to be made to the code and then I think I'll push it out as a patch by next week. One problem I see is that if you want to output { or } it seems to throw an error (even when wrapped in a html element or <text> element...Something I'll have to fix :)

Mar 24, 2011 at 4:41 PM

Is your patch just for RazorEngine are is it something you're going to submit to http://aspnet.codeplex.com as well? I'm confused about the core engine versus this project, etc.

Coordinator
Mar 24, 2011 at 4:46 PM
Edited Mar 24, 2011 at 4:47 PM

It's only for RazorEngine. According to Phil Haack they don't accept patches for aspnet. (it might have been Jon Galloway that said that)

Mar 28, 2011 at 4:14 PM

I sent the suggestion to Scott Guthrie and he replied. Just FYI:

Thanks for the suggestion Jonathan.

I'm looping in some folks from the Razor team so that they can see the feedback as well and comment on it.

Thanks!

Scott

Coordinator
Mar 28, 2011 at 9:31 PM

Cool, I mentioned it to Andrew Nurse (who works on the parser) and he said he doubted they would do anything. So we'll see...Scott is higher in the food chain! :)

Mar 30, 2011 at 12:19 PM

Hello! I've tested the RazorEngine a bit to see if it could be used for a project I'm involved in.

One thing that came up, was that I wanted to set some properties on the template-instance, before executing the template.
But since both Parse and Run returns strings, and not the actual ITemplate this was not possible.

I found the IActivator interface, that seemed perfect for my purpose.
But since the IActivator is non-typed and my TemplateBase class is typed i couldn't typecast it (without workarounds).

Would it be possible to add a second method to the IActivator interface that is typed? Something like this:

interface IActivator 
{
   ITemplate CreateInstance(Type type);
   ITemplate<T> CreateInstance<T>(Type type);
}
That way it will be typed all way through, and I can cast it to TemplateBase<T> after creating the template instance from the type.
Also, it would be nice to add some kind of state somewhere, that is sent to the CreateInstance-method.
This state could be used to set properties on the template-instance before Execute is called.
These properties is things that I want to have access to in my TemplateBase, but don't want to add to my model.
The interface then would look like this:
interface IActivator 
{
   ITemplate CreateInstance(Type type, object state);
   ITemplate<T> CreateInstance<T>(Type type, object state);
}

and my activator could look something like:

public class MyActivator : DefaultActivator
{
   public override ITemplate<T> CreateInstance<T>(Type t, object state)
   {
       var template = (MyTemplateBase<T>)base.CreateInstance(t);
       template.MyContext = (MyContext)state;
       return template;
   }
}

This is only suggestions, alternative ways of achieving what I want is very welcomed.

Cheers, and thank you for a very nice library!

Coordinator
Mar 31, 2011 at 4:06 PM

Verran, @Text support patch has been uploaded and is patched against the most recent source versions. Please feel free to test it - note, you *can't* have any {} within the block or it will break. This includes @for(;;) {} or anything similar. I will continue to look at this to see where the problem lies.

Apr 24, 2011 at 9:00 AM

@model and multi-line @text are great extensions, thanks.

Apr 24, 2011 at 4:39 PM

About new features for 2.2, I would like to know if template includes, or shared template patterns are part of the expected Layout mechanism?

Also, could you explain (or give me a pointer to) the difference between RazorEngine, System.Web.Razor and MSVC 3 Razor... It's confusing, as I don't fully understand what features are part of each?

Thanks

Coordinator
Apr 26, 2011 at 7:49 AM

Hi,

The first thing to remember is that RazorEngine is not the same as the Razor View Engine bundled with ASP.NET MVC3. We specifically wanted to differentiate between the services support by both to highlight this fact. So when you are expecting methods such as @Url, @Html, @RenderBody etc, these are not supported by default with RazorEngine as you would need to implement a customised base template to handle this for you. We currently support @Include as an alternative to MVC3's @Partial method, and we are looking to bring support for Layouts (Master Templates) in the next revision of RazorEngine.

As far as the relationship between the three technologies, its a little like this:

  • System.Web.Razor contains the Razor parser, which is used to actually parse through a stream of text to generate code for templates.
  • MVC3's Razor View Engine is a layer that sits on top of the Razor parser to handle the conversion of .cshtml and .vbhtml files into executable web pages.
  • RazorEngine is also a layer that sits on top of the Razor parser, but is geared up for text templating, in a similar vein to NVelocity, StringTemplate, etc.

I hope that answers your questions.

Apr 26, 2011 at 8:30 AM
AntarisZX wrote: I hope that answers your questions.

Thanks for your detailed answer. If RazorEngine is targeting to provide a general purpose templating engine, that's perfect.

Keep up the good work!

Apr 27, 2011 at 10:45 AM

After using it more intensively, there is a feature that might be great to enhance sharing building blocks of helpers.

I'm looking for a way to import helpers methods defined from other template files (kind of helpers library). In MVC3 Razor View Engine, they have a App_Code solution, but I don't feel like It's a robust solution (you can only share to the whole application).

I was thinking about a kind of @inherits that would take a template name. It would be possible to use several inherits, like this:

@inherits("global_template_lib")

@inherits("local_template_lib")

It's seems feasible technically in RazorEngine (using inheritance from other generated templates instead of directly inheriting from TemplateBase<T>.

What do you think? I would like to try it, where do you suggest me to start into RazorEngine code for this particular case?