diff --git a/OliverBooth.Blog/Services/TemplateService.cs b/OliverBooth.Blog/Services/TemplateService.cs
new file mode 100644
index 0000000..394753e
--- /dev/null
+++ b/OliverBooth.Blog/Services/TemplateService.cs
@@ -0,0 +1,70 @@
+using System.Buffers.Binary;
+using Microsoft.EntityFrameworkCore;
+using OliverBooth.Blog.Data;
+using OliverBooth.Common.Formatting;
+using OliverBooth.Common.Markdown;
+using OliverBooth.Common.Services;
+using SmartFormat;
+using SmartFormat.Extensions;
+
+namespace OliverBooth.Blog.Services;
+
+///
+/// Represents a service that renders MediaWiki-style templates.
+///
+internal sealed class TemplateService : ITemplateService
+{
+ private static readonly Random Random = new();
+ private readonly IDbContextFactory _webContextFactory;
+ private readonly SmartFormatter _formatter;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ /// The factory.
+ public TemplateService(IServiceProvider serviceProvider, IDbContextFactory webContextFactory)
+ {
+ _formatter = Smart.CreateDefaultSmartFormat();
+ _formatter.AddExtensions(new DefaultSource());
+ _formatter.AddExtensions(new ReflectionSource());
+ _formatter.AddExtensions(new DateFormatter());
+ _formatter.AddExtensions(new MarkdownFormatter(serviceProvider));
+
+ _webContextFactory = webContextFactory;
+ }
+
+ ///
+ public string RenderTemplate(TemplateInline templateInline)
+ {
+ if (templateInline is null) throw new ArgumentNullException(nameof(templateInline));
+
+ using BlogContext webContext = _webContextFactory.CreateDbContext();
+ Template? template = webContext.Templates.Find(templateInline.Name);
+ if (template is null)
+ {
+ return $"{{{{{templateInline.Name}}}}}";
+ }
+
+ Span randomBytes = stackalloc byte[20];
+ Random.NextBytes(randomBytes);
+
+ var formatted = new
+ {
+ templateInline.ArgumentList,
+ templateInline.ArgumentString,
+ templateInline.Params,
+ RandomInt = BinaryPrimitives.ReadInt32LittleEndian(randomBytes[..4]),
+ RandomGuid = new Guid(randomBytes[4..]).ToString("N"),
+ };
+
+ try
+ {
+ return _formatter.Format(template.FormatString, formatted);
+ }
+ catch
+ {
+ return $"{{{{{templateInline.Name}|{templateInline.ArgumentString}}}}}";
+ }
+ }
+}