diff --git a/ExampleAppCore/LayoutAndSections.cs b/ExampleAppCore/LayoutAndSections.cs
new file mode 100644
index 0000000..545125f
--- /dev/null
+++ b/ExampleAppCore/LayoutAndSections.cs
@@ -0,0 +1,180 @@
+using Microsoft.AspNetCore.Razor.Language.Extensions;
+using RazorEngineCore;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+
+
+namespace ExampleApp
+{
+
+ public static class LayoutAndSections
+ {
+ ///
+ /// Provides an example of using @section and @RenderSection, based on the layout implementation
+ /// at https://github.com/adoconnection/RazorEngineCore/wiki/@Include-and-@Layout
+ ///
+ public static void Test()
+ {
+
+ string template = @"
+ @{
+ Layout = ""MyLayout"";
+ }
+
+
@Model.Title
+
+ @Include(""outer"", Model)
+
+ @section mysection
+ {
+ This is in a section
+ }
+
+ ";
+
+ IDictionary parts = new Dictionary()
+ {
+ {"MyLayout", @"
+ LAYOUT HEADER
+ @RenderBody()
+ Before section
+ @RenderSection(""mysection"")
+ After section
+ LAYOUT FOOTER
+ "},
+ {"outer", "This is Outer include, <@Model.Title>, @Include(\"inner\")"},
+ {"inner", "This is Inner include"}
+ };
+
+ var razorEngine = new RazorEngine();
+
+ MyCompiledTemplate compiledTemplate = razorEngine.Compile(template, parts, (builder) =>
+ {
+ builder.Options.ProjectEngineBuilderAction = (x) => SectionDirective.Register(x);
+ });
+
+ string result = compiledTemplate.Run(new { Title = "Hello" });
+
+ Console.WriteLine(result);
+ Console.ReadKey();
+ }
+ }
+
+
+ public class MyTemplateBase : RazorEngineTemplateBase
+ {
+ public Func IncludeCallback { get; set; }
+ public Func RenderBodyCallback { get; set; }
+ public string Layout { get; set; }
+
+ public string Include(string key, object model = null)
+ {
+ return this.IncludeCallback(key, model);
+ }
+
+ public string RenderBody()
+ {
+ return this.RenderBodyCallback();
+ }
+
+ public Dictionary Sections = new Dictionary();
+
+ public void DefineSection(string name, Action a)
+ {
+ Debug.WriteLine("DefineSection " + name);
+ BeginCapture();
+ a();
+ string content = EndCapture();
+ Sections.Add(name, content);
+ Debug.WriteLine("Section content:");
+ Debug.WriteLine(content);
+ }
+
+ public string RenderSection(string name, bool required = false)
+ {
+ if (Sections.TryGetValue(name, out var content))
+ return content;
+ else if (required)
+ throw new Exception("Required section not found: " + name);
+ return string.Empty;
+ }
+ }
+
+
+ public class MyCompiledTemplate
+ {
+ private readonly IRazorEngineCompiledTemplate compiledTemplate;
+ private readonly Dictionary> compiledParts;
+
+ public MyCompiledTemplate(IRazorEngineCompiledTemplate compiledTemplate, Dictionary> compiledParts)
+ {
+ this.compiledTemplate = compiledTemplate;
+ this.compiledParts = compiledParts;
+ }
+
+ public string Run(object model)
+ {
+ return this.Run(this.compiledTemplate, model);
+ }
+
+ public string Run(IRazorEngineCompiledTemplate template, object model)
+ {
+ MyTemplateBase templateReference = null;
+
+ string result = template.Run(instance =>
+ {
+ if (!(model is AnonymousTypeWrapper))
+ {
+ model = new AnonymousTypeWrapper(model);
+ }
+
+ instance.Model = model;
+ instance.IncludeCallback = (key, includeModel) => this.Run(this.compiledParts[key], includeModel);
+
+ templateReference = instance;
+ });
+
+ if (templateReference.Layout == null)
+ {
+ return result;
+ }
+
+ return this.compiledParts[templateReference.Layout].Run(instance =>
+ {
+ if (!(model is AnonymousTypeWrapper))
+ {
+ model = new AnonymousTypeWrapper(model);
+ }
+ instance.Sections = templateReference.Sections;
+ instance.Model = model;
+ instance.IncludeCallback = (key, includeModel) => this.Run(this.compiledParts[key], includeModel);
+ instance.RenderBodyCallback = () => result;
+ });
+ }
+
+ public void Save()
+ {
+ /*
+ TODO
+
+ this.compiledTemplate.SaveToFile();
+ this.compiledTemplate.SaveToStream();
+
+ foreach (var compiledPart in this.compiledParts)
+ {
+ compiledPart.Value.SaveToFile();
+ compiledPart.Value.SaveToStream();
+ }
+ */
+ }
+
+ public void Load()
+ {
+ // TODO
+ }
+ }
+
+}
diff --git a/ExampleAppCore/RazorEngineCoreExtensions.cs b/ExampleAppCore/RazorEngineCoreExtensions.cs
new file mode 100644
index 0000000..cf5a06c
--- /dev/null
+++ b/ExampleAppCore/RazorEngineCoreExtensions.cs
@@ -0,0 +1,23 @@
+using RazorEngineCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace ExampleApp
+{
+ public static class RazorEngineCoreExtensions
+ {
+ public static MyCompiledTemplate Compile(this RazorEngine razorEngine, string template, IDictionary parts,
+ Action builderAction = null)
+ {
+ return new MyCompiledTemplate(
+ razorEngine.Compile(template, builderAction),
+ parts.ToDictionary(
+ k => k.Key,
+ v => razorEngine.Compile(v.Value)));
+ }
+
+ }
+
+}
diff --git a/RazorEngineCore.Tests/RazorEngineCore.Tests.csproj b/RazorEngineCore.Tests/RazorEngineCore.Tests.csproj
index 636858a..1ca2f7b 100644
--- a/RazorEngineCore.Tests/RazorEngineCore.Tests.csproj
+++ b/RazorEngineCore.Tests/RazorEngineCore.Tests.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1;net472;net5.0;net6.0
+ net472;net6.0
false
diff --git a/RazorEngineCore/RazorEngine.cs b/RazorEngineCore/RazorEngine.cs
index ef31494..2a69456 100644
--- a/RazorEngineCore/RazorEngine.cs
+++ b/RazorEngineCore/RazorEngine.cs
@@ -60,6 +60,7 @@ protected virtual MemoryStream CreateAndCompileToStream(string templateSource, R
(builder) =>
{
builder.SetNamespace(options.TemplateNamespace);
+ options.ProjectEngineBuilderAction?.Invoke(builder);
});
string fileName = string.IsNullOrWhiteSpace(options.TemplateFilename) ? Path.GetRandomFileName() : options.TemplateFilename;
diff --git a/RazorEngineCore/RazorEngineCompilationOptions.cs b/RazorEngineCore/RazorEngineCompilationOptions.cs
index 3aab592..98e628a 100644
--- a/RazorEngineCore/RazorEngineCompilationOptions.cs
+++ b/RazorEngineCore/RazorEngineCompilationOptions.cs
@@ -3,6 +3,7 @@
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using System;
+using Microsoft.AspNetCore.Razor.Language;
namespace RazorEngineCore
{
@@ -22,6 +23,8 @@ public class RazorEngineCompilationOptions
"System.Collections.Generic"
};
+ public Action ProjectEngineBuilderAction { get; set; }
+
public RazorEngineCompilationOptions()
{
bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
diff --git a/RazorEngineCore/RazorEngineCore.csproj b/RazorEngineCore/RazorEngineCore.csproj
index b145bdc..a7722cd 100644
--- a/RazorEngineCore/RazorEngineCore.csproj
+++ b/RazorEngineCore/RazorEngineCore.csproj
@@ -1,6 +1,6 @@
- net6.0;net5.0;netstandard2.0
+ net7.0;netstandard2.0
true
2022.8.1
Alexander Selishchev, Simon Mourier, William David Cossey, Benjamin Smith, Dag H. Baardsen, krmr, jddj007-hydra
@@ -20,7 +20,7 @@
true
-
-
+
+
diff --git a/RazorEngineCore/RazorEngineTemplateBase.cs b/RazorEngineCore/RazorEngineTemplateBase.cs
index b54e570..dd432cb 100644
--- a/RazorEngineCore/RazorEngineTemplateBase.cs
+++ b/RazorEngineCore/RazorEngineTemplateBase.cs
@@ -1,11 +1,29 @@
-using System.Text;
+using System;
+using System.Text;
using System.Threading.Tasks;
namespace RazorEngineCore
{
public abstract class RazorEngineTemplateBase : IRazorEngineTemplate
{
- private readonly StringBuilder stringBuilder = new StringBuilder();
+ private readonly StringBuilder _stringBuilder = new StringBuilder();
+ private StringBuilder _captureStringBuilder;
+ private StringBuilder stringBuilder => _captureStringBuilder ?? _stringBuilder;
+
+ protected void BeginCapture()
+ {
+ if (_captureStringBuilder != null)
+ throw new InvalidOperationException("Already capturing");
+ _captureStringBuilder = new StringBuilder();
+ }
+ protected string EndCapture()
+ {
+ if (_captureStringBuilder == null)
+ throw new InvalidOperationException("Not capturing");
+ string result = _captureStringBuilder.ToString();
+ _captureStringBuilder = null;
+ return result;
+ }
private string attributeSuffix = null;