Closed Madd0g closed 12 years ago
Hi,
Caching is scoped per instance of a template service, which means that if you are spinning up new instances each time then you'll lose your cached templates. I'm investigating whether to make cached templates global, might add in a configuration option to enable this.
As for recommendations, you'll probably simply want to use the Razor
static type, which maintains a singleton instance of a template service for you. This singleton instance can be replaced with your own preconfigured instance of an ITemplateService
. To minimise the number of recompilations for your web-app, this is the route I would take.
Pre-compilation is still supported, but the Compile
method hasn't been implemented. Essentially making a call to GetTemplate
will achieve the same thing, but I will be adding the Compile
(and Run
) method(s) back in for backwards compatibility.
Thanks for the reply.
Is there a way to use the ITemplate
that is returned from GetTemplate
and CreateTemplate
to be filled with a model? Like if I want to keep a reference to the template I get from CreateTemplate
and use it directly.
I see Execute
and Run
, but one doesn't accept parameters and the other accepts ExecuteContext
and I haven't seen examples of how it can be used.
Hi,
ExecuteContext
is an implementation detail, you can easily spin up a new instance of it: new ExecuteContext()
. Normally the framework itself will handle this, as it is used to track sections and layouts (stacking) while the template is executing. We can't easily return an instance of ITemplate<T>
because we can't return a generic type when the model is anonymous or dynamic. This means we only return an instance of ITemplate
.
If you know your model type, you can easily cast back to the base template type, e.g.:
var tmp = (TemplateBase<MyModel>)instance;
tmp.Model = model;
The Execute
and Run
methods have specific purposes. The Execute
method is what is generated by the Razor parser, it is void
which means we can't return anything, and it essentially just builds the merged content of the result and sets it into an internal writer.
The Run
method simply executes the Execute
method, and returns the string
result.
When I get round to implementing Run
on the ITemplateService
, I'll take care of the ExecuteContext
instantiation for you
var tmp = (TemplateBase<MyModel>)instance; tmp.Model = model;
Ah... very nice, this should definitely be in the documentation. If there was a helper to do this work - it would feel a lot more natural.
LOL, as with the code, the documentation is work-in-progress ;-).
I think the Run
method will essentially suit your needs. 9/10 times you'll only ever be using the template service operations (Parse
, ParseMany
, etc.), so interacting with the template instances themselves should be a pretty sporadic use case.
Watch this space for updates :-)
The beta release that dropped last night (v3.0.0beta) includes support for the Compile
and Run
helper methods. These are defined in ITemplateService
, and also made part of the Razor
static type.
Let me know if you have any issues.
I just want to make sure, if I keep a reference to ITemplate - I don't have to worry about naming the template and such?
This is my usage, it's a bit weird, because of the double cast when parsing the template, but it works - but does it correctly cache?
Creation:
var type = Type.GetType(sModelType);
var typeInstance = (IMyInterface)Activator.CreateInstance(type);
template.CompiledTemplate = Service.CreateTemplate(sTemplate, typeInstance);
Usage:
var concreteTemplate = (TemplateBase<IMyInterface>) template.CompiledTemplate;
concreteTemplate.Model = model;
var result = ((ITemplate) concreteTemplate).Run(new ExecuteContext());
If I keep a reference to template.CompiledTemplate
and run it like this - will caching work?
Thank you!
Well, the type itself will not be cached, but you'll have an instance of ITemplate
that you hold onto anyway. It's important that you always use Run
instead of Execute
though, as Run
will create our buffer that content will be written into, which then returns the result.
This might get a little more complicated when you want to use Layouts/Sections or Includes, because it will need to call back to the ITemplateService
to resolve those templates.
Thanks, as long as it's going to be possible, I don't mind adding extra code to support Layouts (I will have a reference to the template service whenever I use the stored ITemplate
).
The other thing to watch out for, is that ITemplate
by itself is not threadsafe. You can't use the Run
method concurrently, as it only has a single buffer to write to. You'll end up with mangled content if you do...
that's not optimal - so my only option is to use the named templates?
that's a bit awkward, the choice is between creating and storing a guid for each and every thing I want to cache, I also thought of using glued names (model.id + model.categoryid) as a key - but I have a ienumerable member with it's own templates and it gets so much more complicated because they don't have ids. Not to mention that strings are horrible for maintenance.
I'm a bit disappointed, I can't see how to accomplish anything elegant with strings without writing a huge helper to abstract the strings away.
I think what you need to do, is expose template activation instead. The ITemplateService
supports the operation CreateTemplateType
which returns the Type
instance of your compiled type. You could store that, and perhaps if I make the protected method CreateTemplate<T>(Type type, T model)
public, you could then call back to the TemplateService
to activate the template, so essentially you create a new instance each time you want (thus bypassing any threading issue). Then perhaps if your code did something like:
public class ConcreteInstance
{
public Type TemplateType { get; set; }
pulibic TemplateService TemplateService { get; set; }
public ITemplate CreateTemplate<T>(T model)
{
return this.TemplateService.CreateTemplate(TemplateType, model);
}
}
? I'd need to make an update to support that, but if that helps it can be done...
Hi,
I want to use RazorEngine in an MVC app. I want to have several templates that could be used in several places in the application. What are the best practices for using the library in a web app - should I just be using using (var service = new TemplateService())
wherever I want to use the templates? Will caching work if the template service is called like that? Or should I have a singleton somewhere?
Also, I've read that the previous version had support for pre-compiling the templates, I couldn't find the method Compile in Razor, so is it not in the new version yet? What's the difference between caching and pre-compiling anyway?
Thank you