-
Notifications
You must be signed in to change notification settings - Fork 579
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
"ExceptionMessage":"The same key was already used for another template!" #232
Comments
It looks like you used the same template key (/name) for multiple different templates, this is not supported. RunCompile(template1, "name1", typeof(Model), model)
RunCompile(template2, "name2", typeof(Model), model) Or even better implement your own RunCompile("name1", typeof(Model), model)
RunCompile("name2", typeof(Model), model) |
Hi, I have run into a similar issue when updating from 3.2. I was using Razor.Parse() which seems to check if its compiled and if not compile or update it. I wish to do that without calling Razor.Parse(). My reasoning is that my templates can be changed will the application is running from within the application so when a user updates the cshtml i need to reload it upon next request with the changes. How is this possible in the current version? Btw awesome code. :) |
It is. What you want to do is provide your own ICachingProvider implementation. You can take the one from the RazorEngine codebase and edit it to your needs. You can now even recompile a template file when it was changed and silently replace it, so the next Run or RunCompile call will use it (without impacting the application performance while compiling). If you manage to write a generic caching approach which others might find useful, I'd love to see that integrated in RazorEngine. Hope this helps, if you have any problems with this approach feel free to ask. |
Hey, Here is my solution for anyone wanting to update specific templates on the fly. I created my own itemplate manager which almost a straight copy from delegate. I changed the following code /// <param name="source">the source-code of the template</param>
public void AddDynamic(ITemplateKey key, ITemplateSource source)
{
_dynamicTemplates.AddOrUpdate(key, source, (k, oldSource) =>
{
// This code is from the original template manager implementation.
//if (oldSource.Template != source.Template)
//{
//source.Template
//throw new InvalidOperationException("The same key was already used for another template!");
//}
return source;
});
} And in the cases where i need to update i know to call compile in the code. Otherwise i call run. All is well. :) Hope this helps someone. |
Just for reference: Yes this works, but the reason the commented code is there is because without it you get weird behavior in the I would not recommend depending on this specific behavior. It is possible that future versions throw on Instead I would encourage you to add an event to your ITemplateManager implementation and let a custom ICachingManager implementation depend on this event and invalidate the cache. The current |
I agree its not the best i just needed a quick fix. I dont need to invalidate the entire cache just the template in question. Its interesting to me so i might take a look at implementing a solution more along the lines you suggest. |
I believe there is still an issue around this. The following code throws the exception above. (I realize this causes a slow memory leak)
Is there something wrong with the above code? (Uses version 3.7.0) |
The problem is that you only invalidate the Compile Cache and not the Template Manager. The following would work and compile the template twice (note that this leaks as you already noted):
To get what you want you either need to use the |
This whole caching and template manager stuff seems to be overly complex. Was hoping to simply read some values from a database and compile them..when they change..clear the cache. But it seems you have to write a template manager and a caching provider, etc. Most examples are around a 'file system'..do people use a file system a lot when using this product? I will try this DelagateTemplateManager....but seems such overkill. I guess it is all around you trying to 'resolve references' to other templates inside the RunCompile method. |
Most of the Complexity is a direct result of several CLR limitations we run into all the time:
Sorry but AFAIK there is no way to make a simple API that "just works" (at least I cannot see it). It always boils down to carefully look at your performance/environment requirements and build a solution/workaround you can live with. Once you are at this point you can see why the complexity is necessary to get the perfect implementation you are searching for. I would love to be proven wrong, just fix everything and send a pull request :). One thing to keep in mind is that we should provide a API that fails as fast as possible and not allow users to do things that will fail once the memory runs out...
I don't know. I do and it makes sense to save templates in files to have a hierarchical structure... |
+1 for Caching/Template Manager overkill. We have an app where templates used for emails, letters and memos stored in the DB. They also presented to the user, so some users can change templates on-the-fly. Another question, which I might miss finding answer for - how to implement logic of either adding template to the manager or using template id?
Above code will fail if I will try to run it for the second time with the same template Id/template. |
The exception is exactly for your case, because if it would "just work" out of the box you would run out of memory or into some other weird and hard to debug issues. So it works as designed by showing you: "You have a problem!". Maybe the message itself could be improved ... You are exactly in the case where you need to think about memory and assembly unloading (the only exception is when your application is short-lived). Once you figured out a solution you can easily use one of the existing implementations if they fit your needs: https://antaris.github.io/RazorEngine/TemplateManager.html . We have the I don't think implementing one interface |
Thank you for the quick response! Our application while being moderate in size actually used mostly by one user at a time and not continuously. That is why I guess we never experienced memory leaking issues. I guess I can follow this guide. Thank you. |
You can completely reset the cache easily by using a new And none of these answers actually answer the question which specifically talk about the memory leak. In fact the only possible and correct answer is that you need to unload the AppDomain to free the memory (none of the answers actually free memory, they "just work" by consuming more!). Once you realize how nasty this problem is, you can think about your memory requirements and how long you can keep the current AppDomain before recycling. Once you reached this point the "intended" way of course would be to write your own Hope this helps. |
How do I replace a template? Assuming I don't mind the leaking memory? When I try to replace it by recompiling I get 'The same key was already used for another template!'. I expect to do this once in a couple blue moons with the server resetting every few days. |
Hi, I'm pretty new to this. I just want a template engine that I can use to send emails with some dynamic database content. So, my app is running in production and let's say I want to fix a typo in the template. I FTP the new template file to the server and instead of it just working, I get this error. That doesn't make sense. I don't want to change the name of the key and re-compile and re-publish my whole app! I just want to make a change to my template and have a happy day. Could you please explain why this VERY common scenario doesn't work out of the box? Is there a SIMPLE solution? Many thanks, |
I agree with Ron. This is a VERY common scenario and there should be a way to clear this template out of memory without the need to recreate an instance and create a memory leak. A simple RemoveChachedTemplate method on the razor engine would solve the problem for most people. I see the IsTemplateCached Method, but it's hardly any use to know you've got a cached template without the ability to remove that single cached template. the only option I have is to create a memory leak for altered templates, which is not a good solution. Regards, |
Hi @leedavi , Are there any updates on this please, where you can remove chached template or else delete all templates in cache? |
Why aren't we allowed to just parse the template, without involving caching at all? |
In the end I found an idea of using a MD5Hash, that someone posted as a solution. (Sorry not to give praise to the guy who had the idea, I can';t find the post where I found the idea,) Here's the code I use in my NBrightBuy project. You'll obviously need to alter it to match your environment. It does cause a duplicate in memory when you edit the template, but in my standard situation I only alter templates during development, so this works fine.
|
Our application is going to be quite busy, we require caching. We'll have 3 templates when we move to production, that number will grow to ~6 in the coming year. My 'skull thickness' issue was that my stubborn brain wanted to name & add the template every time. If it's already cached, the solution was to not add it. Controllerstring razorTemplateName = "Layout " + sm.name;
bool isTemplateCached = Engine.Razor.IsTemplateCached(razorTemplateName, null);
if (!isTemplateCached) {
var layoutSource = new LoadedTemplateSource(File.ReadAllText(Path.Combine(@"Templates\", sm.layoutName)));
Engine.Razor.AddTemplate(razorTemplateName, layoutSource);
} View@{
string layoutName = "Layout " + Model.sm.name;
Layout = layoutName;
} Thanks for your hard work @matthid! |
I encourage with error when duplicate a page in another tab in the browser.
Any point to resole it will be appreciated.
The text was updated successfully, but these errors were encountered: