Skip to content

Commit 9d75d64

Browse files
committed
Get Razor views to use new instances, Dispose when done.
1 parent 6a0a42e commit 9d75d64

10 files changed

Lines changed: 199 additions & 96 deletions

File tree

lib/ServiceStack.Interfaces.dll

87.5 KB
Binary file not shown.

src/ServiceStack.Razor/CSharpRazorBuildProvider.cs

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
using System.Globalization;
77
using System.Web.Compilation;
88
using System.Web.Razor;
9+
using ServiceStack.Logging;
910

1011
namespace ServiceStack.Razor
1112
{
1213
[BuildProviderAppliesTo(BuildProviderAppliesTo.Code | BuildProviderAppliesTo.Web)]
1314
public class CSharpRazorBuildProvider : BuildProvider
14-
{
15+
{
16+
private static readonly ILog Log = LogManager.GetLogger(typeof (CSharpRazorBuildProvider));
17+
1518
private readonly RazorEngineHost host;
1619

1720
private readonly CompilerType compilerType;
@@ -47,10 +50,17 @@ public override CompilerType CodeCompilerType
4750
/// <param name="assemblyBuilder">The assembly builder that references the source code generated by the build provider.</param>
4851
public override void GenerateCode(AssemblyBuilder assemblyBuilder)
4952
{
50-
assemblyBuilder.AddCodeCompileUnit(this, this.GetGeneratedCode());
53+
try
54+
{
55+
assemblyBuilder.AddCodeCompileUnit(this, this.GetGeneratedCode());
5156

52-
assemblyBuilder.GenerateTypeFactory(string.Format(CultureInfo.InvariantCulture, "{0}.{1}", new object[] { this.host.DefaultNamespace, this.host.DefaultClassName }));
53-
}
57+
assemblyBuilder.GenerateTypeFactory(string.Format(CultureInfo.InvariantCulture, "{0}.{1}", new object[] { this.host.DefaultNamespace, this.host.DefaultClassName }));
58+
}
59+
catch (Exception ex)
60+
{
61+
Log.Error("GenerateCode(): ", ex);
62+
}
63+
}
5464

5565
/// <summary>
5666
/// Returns a type generated by the build provider from the virtual path.
@@ -64,24 +74,33 @@ public override Type GetGeneratedType(CompilerResults results)
6474

6575
private CodeCompileUnit GetGeneratedCode()
6676
{
67-
if (this.generatedCode == null)
68-
{
69-
var engine = new RazorTemplateEngine(this.host);
70-
GeneratorResults results;
71-
using (var reader = this.OpenReader())
72-
{
73-
results = engine.GenerateCode(reader);
74-
}
75-
76-
if (!results.Success)
77-
{
78-
throw new InvalidOperationException(results.ToString());
79-
}
80-
81-
this.generatedCode = results.GeneratedCode;
82-
}
83-
84-
return this.generatedCode;
77+
try
78+
{
79+
80+
if (this.generatedCode == null)
81+
{
82+
var engine = new RazorTemplateEngine(this.host);
83+
GeneratorResults results;
84+
using (var reader = this.OpenReader())
85+
{
86+
results = engine.GenerateCode(reader);
87+
}
88+
89+
if (!results.Success)
90+
{
91+
throw new InvalidOperationException(results.ToString());
92+
}
93+
94+
this.generatedCode = results.GeneratedCode;
95+
}
96+
97+
return this.generatedCode;
98+
}
99+
catch (Exception ex)
100+
{
101+
Log.Error("GetGeneratedCode(): ", ex);
102+
return null;
103+
}
85104
}
86105
}
87106
}

src/ServiceStack.Razor/Templating/ITemplate.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,11 @@ public interface ITemplate
3838
/// </summary>
3939
/// <param name="string">The string to write.</param>
4040
void WriteLiteral(string @string);
41+
42+
/// <summary>
43+
/// Create new instance of template
44+
/// </summary>
45+
/// <returns></returns>
46+
object Clone();
4147
}
4248
}

src/ServiceStack.Razor/Templating/TemplateBase.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,5 +269,20 @@ public static void WriteTo(TextWriter writer, object obj)
269269

270270
writer.Write(obj);
271271
}
272+
273+
public int Counter { get; set; }
274+
275+
public object Clone()
276+
{
277+
return base.MemberwiseClone();
278+
}
279+
}
280+
281+
public static class TemplateExtensions
282+
{
283+
public static ITemplate CloneTemplate(this ITemplate template)
284+
{
285+
return (ITemplate)template.Clone();
286+
}
272287
}
273288
}

src/ServiceStack.Razor/Templating/TemplateService.ServiceStack.cs

Lines changed: 57 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -23,42 +23,45 @@ public IRazorTemplate ExecuteTemplate<T>(T model, string name, string defaultTem
2323
if (instance == null)
2424
throw new ArgumentException("No compiled template exists with the specified name.");
2525

26-
SetService(instance, this);
27-
SetModel(instance, model);
28-
TemplateBase.ViewBag = new ExpandoObject();
26+
using (instance as IDisposable)
27+
{
28+
SetService(instance, this);
29+
SetModel(instance, model);
30+
TemplateBase.ViewBag = new ExpandoObject();
2931

30-
var razorTemplate = (IRazorTemplate)instance;
31-
razorTemplate.Init(viewEngine, new ViewDataDictionary<T>(model), httpReq, httpRes);
32-
33-
instance.Execute();
32+
var razorTemplate = (IRazorTemplate)instance;
33+
razorTemplate.Init(viewEngine, new ViewDataDictionary<T>(model), httpReq, httpRes);
3434

35-
var template = httpReq.GetTemplate();
36-
if (!string.IsNullOrEmpty(template))
37-
template = viewEngine.HasTemplate(template) ? template : null;
35+
instance.Execute();
3836

39-
if (template == null && !razorTemplate.Layout.IsNullOrEmpty())
40-
template = razorTemplate.Layout.MapServerPath();
37+
var template = httpReq.GetTemplate();
38+
if (!string.IsNullOrEmpty(template))
39+
template = viewEngine.HasTemplate(template) ? template : null;
4140

42-
if (template == null)
43-
template = defaultTemplatePath;
41+
if (template == null && !razorTemplate.Layout.IsNullOrEmpty())
42+
template = razorTemplate.Layout.MapServerPath();
4443

45-
var layoutTemplate = GetTemplate(template ?? RazorFormat.DefaultTemplate);
46-
if (layoutTemplate != null)
47-
{
48-
layoutTemplate.ChildTemplate = razorTemplate;
49-
SetService(layoutTemplate, this);
50-
SetModel(layoutTemplate, model);
51-
layoutTemplate.Execute();
44+
if (template == null)
45+
template = defaultTemplatePath;
5246

53-
return layoutTemplate;
54-
}
55-
else if (defaultTemplatePath != null)
56-
{
57-
throw new ArgumentException(
58-
"No template exists with the specified Layout: " + defaultTemplatePath);
59-
}
47+
var layoutTemplate = GetTemplate(template ?? RazorFormat.DefaultTemplate);
48+
if (layoutTemplate != null)
49+
{
50+
layoutTemplate.ChildTemplate = razorTemplate;
51+
SetService(layoutTemplate, this);
52+
SetModel(layoutTemplate, model);
53+
layoutTemplate.Execute();
6054

61-
return razorTemplate;
55+
return layoutTemplate;
56+
}
57+
else if (defaultTemplatePath != null)
58+
{
59+
throw new ArgumentException(
60+
"No template exists with the specified Layout: " + defaultTemplatePath);
61+
}
62+
63+
return razorTemplate;
64+
}
6265
}
6366

6467
readonly Dictionary<string, string> pagePathAndNames = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
@@ -86,7 +89,8 @@ public ITemplate GetAndCheckTemplate(string name)
8689

8790
ITemplate instance;
8891
templateCache.TryGetValue(name, out instance);
89-
return instance;
92+
93+
return instance.CloneTemplate();
9094
}
9195

9296
public IRazorTemplate GetTemplate(string name)
@@ -103,7 +107,7 @@ public IRazorTemplate GetTemplate(string name)
103107
//Re-check after all templates have been compiled
104108
viewEngine.EnsureAllCompiled();
105109
if (templateCache.TryGetValue(name, out instance))
106-
return instance as IRazorTemplate;
110+
return instance.CloneTemplate() as IRazorTemplate;
107111

108112
view = viewEngine.GetView(name);
109113
if (view == null)
@@ -113,33 +117,36 @@ public IRazorTemplate GetTemplate(string name)
113117

114118
templateCache.TryGetValue(name, out instance);
115119
}
116-
return instance as IRazorTemplate;
120+
return instance as IRazorTemplate; //Cloned in GetAndCheckTemplate()
117121
}
118122

119123
public IRazorTemplate RenderPartial<T>(T model, string name)
120124
{
121125
var template = GetTemplate(name);
122-
SetService(template, this);
123-
SetModel(template, model);
126+
using (template as IDisposable)
127+
{
128+
SetService(template, this);
129+
SetModel(template, model);
124130

125-
//TODO: make less ugly,
126-
//since executing templates clears the buffer we need to capture
127-
//what's been rendered and prepend after.
128-
var capture = template.Result;
131+
//TODO: make less ugly,
132+
//since executing templates clears the buffer we need to capture
133+
//what's been rendered and prepend after.
134+
var capture = template.Result;
129135

130-
try
131-
{
132-
template.Execute();
133-
}
134-
catch (Exception ex)
135-
{
136-
throw new InvalidOperationException(
137-
"Could not execute partial: " + name + ", model: " + model);
138-
}
136+
try
137+
{
138+
template.Execute();
139+
}
140+
catch (Exception ex)
141+
{
142+
throw new InvalidOperationException(
143+
"Could not execute partial: " + name + ", model: " + model);
144+
}
139145

140-
template.Prepend(capture);
141-
142-
return template;
146+
template.Prepend(capture);
147+
148+
return template;
149+
}
143150
}
144151
}
145152
}

src/ServiceStack.Razor/Templating/TemplateService.cs

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,15 @@ internal ITemplate GetTemplate(string template, Type modelType, string name)
157157
{
158158
if (!string.IsNullOrEmpty(name))
159159
if (templateCache.ContainsKey(name))
160-
return templateCache[name];
160+
return templateCache[name].CloneTemplate();
161161

162162
var instance = CreateTemplate(template, modelType);
163163

164164
if (!string.IsNullOrEmpty(name))
165165
if (!templateCache.ContainsKey(name))
166166
templateCache.Add(name, instance);
167167

168-
return instance;
168+
return instance.CloneTemplate();
169169
}
170170

171171
/// <summary>
@@ -177,11 +177,13 @@ internal ITemplate GetTemplate(string template, Type modelType, string name)
177177
public string Parse(string template, string name = null)
178178
{
179179
var instance = GetTemplate(template, null, name);
180+
using (instance as IDisposable)
181+
{
182+
SetService(instance, this);
183+
instance.Execute();
180184

181-
SetService(instance, this);
182-
instance.Execute();
183-
184-
return instance.Result;
185+
return instance.Result;
186+
}
185187
}
186188

187189
/// <summary>
@@ -195,12 +197,14 @@ public string Parse(string template, string name = null)
195197
public string Parse<T>(string template, T model, string name = null)
196198
{
197199
var instance = GetTemplate(template, typeof(T), name);
200+
using (instance as IDisposable)
201+
{
202+
SetService(instance, this);
203+
SetModel(instance, model);
204+
instance.Execute();
198205

199-
SetService(instance, this);
200-
SetModel(instance, model);
201-
instance.Execute();
202-
203-
return instance.Result;
206+
return instance.Result;
207+
}
204208
}
205209

206210
/// <summary>
@@ -217,10 +221,13 @@ public string Run(string name)
217221
if (instance == null)
218222
throw new ArgumentException("No compiled template exists with the specified name.");
219223

220-
SetService(instance, this);
221-
instance.Execute();
224+
using (instance as IDisposable)
225+
{
226+
SetService(instance, this);
227+
instance.Execute();
222228

223-
return instance.Result;
229+
return instance.Result;
230+
}
224231
}
225232

226233
/// <summary>
@@ -239,11 +246,14 @@ public string Run<T>(T model, string name)
239246
if (instance == null)
240247
throw new ArgumentException("No compiled template exists with the specified name.");
241248

242-
SetService(instance, this);
243-
SetModel(instance, model);
244-
instance.Execute();
249+
using (instance as IDisposable)
250+
{
251+
SetService(instance, this);
252+
SetModel(instance, model);
253+
instance.Execute();
245254

246-
return instance.Result;
255+
return instance.Result;
256+
}
247257
}
248258

249259
/// <summary>
@@ -277,6 +287,6 @@ private static void SetService(ITemplate template, TemplateService service)
277287
{
278288
template.Service = service;
279289
}
280-
281290
}
291+
282292
}

0 commit comments

Comments
 (0)