Skip to content

Commit

Permalink
Scaffolding: Avoid recompiling T4 templates
Browse files Browse the repository at this point in the history
This dramatically improves the performance of the commands.

Part of dotnet#27598
  • Loading branch information
bricelam committed Aug 24, 2022
1 parent 510878d commit d11c2ee
Showing 1 changed file with 35 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
using System.Text;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Internal;
using Engine = Mono.TextTemplating.TemplatingEngine;
using Microsoft.VisualStudio.TextTemplating;
using Mono.TextTemplating;

namespace Microsoft.EntityFrameworkCore.Scaffolding.Internal;

Expand All @@ -23,7 +24,7 @@ public class TextTemplatingModelGenerator : TemplatedModelGenerator

private readonly IOperationReporter _reporter;
private readonly IServiceProvider _serviceProvider;
private Engine? _engine;
private TemplatingEngine? _engine;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -47,8 +48,8 @@ public TextTemplatingModelGenerator(
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
protected virtual Engine Engine
=> _engine ??= new Engine();
protected virtual TemplatingEngine Engine
=> _engine ??= new TemplatingEngine();

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down Expand Up @@ -151,6 +152,8 @@ public override ScaffoldedModel GenerateModel(IModel model, ModelCodeGenerationO
var entityTypeTemplate = Path.Combine(options.ProjectDir!, TemplatesDirectory, EntityTypeTemplate);
if (File.Exists(entityTypeTemplate))
{
var compiledEntityTypeTemplate = Engine.CompileTemplate(File.ReadAllText(entityTypeTemplate), host);

host.TemplateFile = entityTypeTemplate;

foreach (var entityType in model.GetEntityTypes())
Expand All @@ -161,7 +164,10 @@ public override ScaffoldedModel GenerateModel(IModel model, ModelCodeGenerationO
host.Session.Add("NamespaceHint", options.ModelNamespace);
host.Session.Add("ProjectDefaultNamespace", options.RootNamespace);

generatedCode = ProcessTemplate(entityTypeTemplate, host);
UpdateSession(compiledEntityTypeTemplate, host.Session);
generatedCode = compiledEntityTypeTemplate.Process();
HandleErrors(host);

if (string.IsNullOrWhiteSpace(generatedCode))
{
continue;
Expand All @@ -176,6 +182,8 @@ public override ScaffoldedModel GenerateModel(IModel model, ModelCodeGenerationO
var configurationTemplate = Path.Combine(options.ProjectDir!, TemplatesDirectory, EntityTypeConfigurationTemplate);
if (File.Exists(configurationTemplate))
{
var compiledConfigurationTemplate = Engine.CompileTemplate(File.ReadAllText(configurationTemplate), host);

host.TemplateFile = configurationTemplate;

foreach (var entityType in model.GetEntityTypes())
Expand All @@ -186,7 +194,10 @@ public override ScaffoldedModel GenerateModel(IModel model, ModelCodeGenerationO
host.Session.Add("NamespaceHint", options.ContextNamespace ?? options.ModelNamespace);
host.Session.Add("ProjectDefaultNamespace", options.RootNamespace);

generatedCode = ProcessTemplate(configurationTemplate, host);
UpdateSession(compiledConfigurationTemplate, host.Session);
generatedCode = compiledConfigurationTemplate.Process();
HandleErrors(host);

if (string.IsNullOrWhiteSpace(generatedCode))
{
continue;
Expand All @@ -205,12 +216,29 @@ public override ScaffoldedModel GenerateModel(IModel model, ModelCodeGenerationO
}

return resultingFiles;

// HACK: CompiledTemplate holds on to the previous session
static void UpdateSession(CompiledTemplate compiledTemplate, ITextTemplatingSession session)
{
var textTransformation = typeof(CompiledTemplate)
.GetField("textTransformation", BindingFlags.Instance | BindingFlags.NonPublic)!
.GetValue(compiledTemplate)!;
textTransformation.GetType()
.GetProperty("Session")!
.SetValue(textTransformation, session);
}
}

private string ProcessTemplate(string inputFile, TextTemplatingEngineHost host)
{
var output = Engine.ProcessTemplate(File.ReadAllText(inputFile), host);
HandleErrors(host);

return output;
}

private void HandleErrors(TextTemplatingEngineHost host)
{
foreach (CompilerError error in host.Errors)
{
_reporter.Write(error);
Expand All @@ -223,9 +251,7 @@ private string ProcessTemplate(string inputFile, TextTemplatingEngineHost host)

if (host.Errors.HasErrors)
{
throw new OperationException(DesignStrings.ErrorGeneratingOutput(inputFile));
throw new OperationException(DesignStrings.ErrorGeneratingOutput(host.TemplateFile));
}

return output;
}
}

0 comments on commit d11c2ee

Please sign in to comment.