From ca5334c0164fe8eca0b1bac0852706a68905ba9c Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 12 Dec 2013 17:43:53 -0800 Subject: [PATCH] Fixing Razor templates invalidation --- .../Drivers/ShapePartDriver.cs | 13 +-- .../Handlers/ShapePartHandler.cs | 12 ++- .../Services/DefaultTemplateService.cs | 2 - .../Services/RazorTemplateProcessor.cs | 4 +- .../Services/TemplateBindingStrategy.cs | 34 +++---- src/Orchard/Compilation/ICompiler.cs | 2 +- ...mplateCache.cs => IRazorTemplateHolder.cs} | 2 +- .../Compilation/Razor/RazorCompiler.cs | 92 +++++++++---------- .../Compilation/Razor/RazorTemplateCache.cs | 4 +- src/Orchard/Environment/OrchardStarter.cs | 2 + src/Orchard/Orchard.Framework.csproj | 2 +- 11 files changed, 82 insertions(+), 87 deletions(-) rename src/Orchard/Compilation/Razor/{IRazorTemplateCache.cs => IRazorTemplateHolder.cs} (65%) diff --git a/src/Orchard.Web/Modules/Orchard.Templates/Drivers/ShapePartDriver.cs b/src/Orchard.Web/Modules/Orchard.Templates/Drivers/ShapePartDriver.cs index 864b106f3..20ff66426 100644 --- a/src/Orchard.Web/Modules/Orchard.Templates/Drivers/ShapePartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Templates/Drivers/ShapePartDriver.cs @@ -15,14 +15,15 @@ using Orchard.Utility.Extensions; namespace Orchard.Templates.Drivers { public class ShapePartDriver : ContentPartDriver { private readonly IEnumerable _processors; - private readonly IRazorTemplateCache _cache; - private readonly ITemplateService _service; + private readonly IRazorTemplateHolder _templateHolder; private readonly ITransactionManager _transactions; - public ShapePartDriver(IEnumerable processors, IRazorTemplateCache cache, ITemplateService service,ITransactionManager transactions) { + public ShapePartDriver( + IEnumerable processors, + IRazorTemplateHolder templateHolder, + ITransactionManager transactions) { _processors = processors; - _cache = cache; - _service = service; + _templateHolder = templateHolder; _transactions = transactions; T = NullLocalizer.Instance; } @@ -51,7 +52,7 @@ namespace Orchard.Templates.Drivers { try { var processor = _processors.FirstOrDefault(x => String.Equals(x.Type, part.Language, StringComparison.OrdinalIgnoreCase)); processor.Verify(part.Template); - _cache.Set(part.Name, part.Template); + _templateHolder.Set(part.Name, part.Template); } catch (Exception ex) { updater.AddModelError("", T("Template processing error: {0}", ex.Message)); diff --git a/src/Orchard.Web/Modules/Orchard.Templates/Handlers/ShapePartHandler.cs b/src/Orchard.Web/Modules/Orchard.Templates/Handlers/ShapePartHandler.cs index 9206989bc..af1653a15 100644 --- a/src/Orchard.Web/Modules/Orchard.Templates/Handlers/ShapePartHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Templates/Handlers/ShapePartHandler.cs @@ -1,16 +1,20 @@ -using Orchard.Caching; +using Orchard.Compilation.Razor; using Orchard.ContentManagement.Handlers; using Orchard.Data; using Orchard.Templates.Models; -using Orchard.Templates.Services; namespace Orchard.Templates.Handlers { public class ShapePartHandler : ContentHandler { - public ShapePartHandler(IRepository repository, ISignals signals) { + private readonly IRazorTemplateHolder _razorTemplateHolder; + + public ShapePartHandler( + IRepository repository, + IRazorTemplateHolder razorTemplateHolder) { + _razorTemplateHolder = razorTemplateHolder; Filters.Add(StorageFilter.For(repository)); OnGetContentItemMetadata((ctx, part) => ctx.Metadata.DisplayText = part.Name); - OnCreated((ctx, part) => signals.Trigger(DefaultTemplateService.TemplatesSignal)); + OnUpdated((ctx, part) => _razorTemplateHolder.Set(part.Name, part.Template)); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Templates/Services/DefaultTemplateService.cs b/src/Orchard.Web/Modules/Orchard.Templates/Services/DefaultTemplateService.cs index d0785ab91..f28399503 100644 --- a/src/Orchard.Web/Modules/Orchard.Templates/Services/DefaultTemplateService.cs +++ b/src/Orchard.Web/Modules/Orchard.Templates/Services/DefaultTemplateService.cs @@ -12,8 +12,6 @@ using Orchard.Templates.Models; namespace Orchard.Templates.Services { public class DefaultTemplateService : ITemplateService { - public const string TemplatesSignal = "Orchard.Templates"; - private readonly IShapeFactory _shapeFactory; private readonly IDisplayHelperFactory _displayHelperFactory; private readonly IWorkContextAccessor _workContextAccessor; diff --git a/src/Orchard.Web/Modules/Orchard.Templates/Services/RazorTemplateProcessor.cs b/src/Orchard.Web/Modules/Orchard.Templates/Services/RazorTemplateProcessor.cs index f17f3cf6a..d8d5d85fb 100644 --- a/src/Orchard.Web/Modules/Orchard.Templates/Services/RazorTemplateProcessor.cs +++ b/src/Orchard.Web/Modules/Orchard.Templates/Services/RazorTemplateProcessor.cs @@ -16,7 +16,6 @@ using Orchard.Themes.Models; namespace Orchard.Templates.Services { public class RazorTemplateProcessor : TemplateProcessorImpl { private readonly IRazorCompiler _compiler; - private readonly IRazorTemplateCache _cache; private readonly ISiteService _siteService; private readonly IHttpContextAccessor _hca; @@ -24,9 +23,8 @@ namespace Orchard.Templates.Services { get { return "Razor"; } } - public RazorTemplateProcessor(IRazorCompiler compiler, IRazorTemplateCache cache, ISiteService siteService, IHttpContextAccessor hca) { + public RazorTemplateProcessor(IRazorCompiler compiler, ISiteService siteService, IHttpContextAccessor hca) { _compiler = compiler; - _cache = cache; _siteService = siteService; _hca = hca; Logger = NullLogger.Instance; diff --git a/src/Orchard.Web/Modules/Orchard.Templates/Services/TemplateBindingStrategy.cs b/src/Orchard.Web/Modules/Orchard.Templates/Services/TemplateBindingStrategy.cs index 9cfc4b0b8..6b3cb2bc2 100644 --- a/src/Orchard.Web/Modules/Orchard.Templates/Services/TemplateBindingStrategy.cs +++ b/src/Orchard.Web/Modules/Orchard.Templates/Services/TemplateBindingStrategy.cs @@ -15,13 +15,13 @@ using Orchard.Themes.Models; namespace Orchard.Templates.Services { public class TemplateBindingStrategy : IShapeTableProvider { private readonly IWorkContextAccessor _wca; - private readonly ICacheManager _cache; - private readonly ISignals _signals; + private readonly IRazorTemplateHolder _templateProvider; - public TemplateBindingStrategy(IWorkContextAccessor wca, ICacheManager cache, ISignals signals) { + public TemplateBindingStrategy( + IWorkContextAccessor wca, + IRazorTemplateHolder templateProvider) { _wca = wca; - _cache = cache; - _signals = signals; + _templateProvider = templateProvider; } public void Discover(ShapeTableBuilder builder) { @@ -31,7 +31,6 @@ namespace Orchard.Templates.Services { private void BuildShapes(ShapeTableBuilder builder) { var templateService = _wca.GetContext().Resolve(); - var templateCache = _wca.GetContext().Resolve(); var siteService = _wca.GetContext().Resolve(); var extensionManager = _wca.GetContext().Resolve(); @@ -49,28 +48,23 @@ namespace Orchard.Templates.Services { Priority = int.MaxValue }; - var shapes = _cache.Get( - DefaultTemplateService.TemplatesSignal, - ctx => { - ctx.Monitor(_signals.When(DefaultTemplateService.TemplatesSignal)); - return templateService - .GetTemplates() - .Select(r => new { - r.Name, - r.Language, - r.Template }) - .ToList(); - }); + var shapes = templateService.GetTemplates().Select(r => + new { + r.Name, + r.Language, + r.Template + }) + .ToList(); foreach (var record in shapes) { - templateCache.Set(record.Name, record.Template); + _templateProvider.Set(record.Name, record.Template); var shapeType = AdjustName(record.Name); builder.Describe(shapeType) .From(new Feature { Descriptor = hackedDescriptor }) .BoundAs("Template::" + shapeType, descriptor => context => { - var template = templateCache.Get(record.Name); + var template = _templateProvider.Get(record.Name); return template != null ? PerformInvoke(context, record.Name, record.Language, template) : new HtmlString(""); }); } diff --git a/src/Orchard/Compilation/ICompiler.cs b/src/Orchard/Compilation/ICompiler.cs index d9161a5cd..c234f896d 100644 --- a/src/Orchard/Compilation/ICompiler.cs +++ b/src/Orchard/Compilation/ICompiler.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; namespace Orchard.Compilation { - public interface ICompiler{ + public interface ICompiler : IDependency { object Compile(string code, IDictionary parameters); T Compile(string code, IDictionary parameters); } diff --git a/src/Orchard/Compilation/Razor/IRazorTemplateCache.cs b/src/Orchard/Compilation/Razor/IRazorTemplateHolder.cs similarity index 65% rename from src/Orchard/Compilation/Razor/IRazorTemplateCache.cs rename to src/Orchard/Compilation/Razor/IRazorTemplateHolder.cs index 17b9cdd88..7a0ccb1ec 100644 --- a/src/Orchard/Compilation/Razor/IRazorTemplateCache.cs +++ b/src/Orchard/Compilation/Razor/IRazorTemplateHolder.cs @@ -1,5 +1,5 @@ namespace Orchard.Compilation.Razor { - public interface IRazorTemplateCache { + public interface IRazorTemplateHolder : ISingletonDependency { string Get(string name); void Set(string name, string template); } diff --git a/src/Orchard/Compilation/Razor/RazorCompiler.cs b/src/Orchard/Compilation/Razor/RazorCompiler.cs index 9ce2a10b9..76bb4d3ab 100644 --- a/src/Orchard/Compilation/Razor/RazorCompiler.cs +++ b/src/Orchard/Compilation/Razor/RazorCompiler.cs @@ -8,7 +8,6 @@ using System.Reflection; using System.Security.Cryptography; using System.Text; using System.Web.Razor; -using System.Web.Razor.Generator; using Microsoft.CSharp; using Orchard.Caching; using Orchard.Logging; @@ -17,25 +16,51 @@ using Orchard.Utility.Extensions; namespace Orchard.Compilation.Razor { public class RazorCompiler : IRazorCompiler { private readonly ICacheManager _cache; - private readonly IWorkContextAccessor _wca; - const string DynamicallyGeneratedClassName = "RazorTemplate"; - const string NamespaceForDynamicClasses = "Orchard.Compilation.Razor"; + private readonly ISignals _signals; + private const string DynamicallyGeneratedClassName = "RazorTemplate"; + private const string NamespaceForDynamicClasses = "Orchard.Compilation.Razor"; private const string ForceRecompile = "Razor.ForceRecompile"; + private static readonly string[] DefaultNamespaces = { + "System", + "System.IO", + "System.Linq", + "System.Collections", + "System.Collections.Generic", + "System.Dynamic", + "System.Text", + "System.Web", + "System.Web.Mvc", + "System.Web.Mvc.Html", + "System.Web.Mvc.Ajax", + "System.Web.UI", + "System.Web.Routing", + "Orchard.Compilation.Razor", + "Orchard.ContentManagement", + "Orchard.DisplayManagement", + "Orchard.DisplayManagement.Shapes", + "Orchard.Security.Permissions", + "Orchard.UI.Resources", + "Orchard.Security", + "Orchard.Mvc.Spooling", + "Orchard.Mvc.Html" + }; - public RazorCompiler(ICacheManager cache, IWorkContextAccessor wca) { + public RazorCompiler( + ICacheManager cache, + ISignals signals) { _cache = cache; - _wca = wca; + _signals = signals; Logger = NullLogger.Instance; } - ILogger Logger { get; set; } + private ILogger Logger { get; set; } public IRazorTemplateBase CompileRazor(string code, string name, IDictionary parameters) { - return (RazorTemplateBase)Compile(code, name, typeof(TModel), parameters); + return (RazorTemplateBase) Compile(code, name, typeof (TModel), parameters); } public IRazorTemplateBase CompileRazor(string code, string name, IDictionary parameters) { - return (IRazorTemplateBase)Compile(code, name, null, parameters); + return (IRazorTemplateBase) Compile(code, name, null, parameters); } public object Compile(string code, IDictionary parameters) { @@ -47,15 +72,12 @@ namespace Orchard.Compilation.Razor { } private object Compile(string code, string name, Type modelType, IDictionary parameters) { - ISignals signals = _wca.GetContext().TryResolve(out signals) ? signals : null; - - var cacheKey = name ?? GetHash(code); + + var cacheKey = (name ?? DynamicallyGeneratedClassName) + GetHash(code); var generatedClassName = name != null ? name.Strip(c => !c.IsLetter() && !Char.IsDigit(c)) : DynamicallyGeneratedClassName; var assembly = _cache.Get(cacheKey, ctx => { - if (signals != null) { - ctx.Monitor(signals.When(ForceRecompile)); - } + _signals.When(ForceRecompile); var modelTypeName = "dynamic"; var reader = new StringReader(code); @@ -81,32 +103,7 @@ namespace Orchard.Compilation.Razor { DefaultNamespace = NamespaceForDynamicClasses }; - var namespaces = new List { - "System", - "System.IO", - "System.Linq", - "System.Collections", - "System.Collections.Generic", - "System.Dynamic", - "System.Text", - "System.Web", - "System.Web.Mvc", - "System.Web.Mvc.Html", - "System.Web.Mvc.Ajax", - "System.Web.UI", - "System.Web.Routing", - "Orchard.Compilation.Razor", - "Orchard.ContentManagement", - "Orchard.DisplayManagement", - "Orchard.DisplayManagement.Shapes", - "Orchard.Security.Permissions", - "Orchard.UI.Resources", - "Orchard.Security", - "Orchard.Mvc.Spooling", - "Orchard.Mvc.Html" - }; - - foreach (var n in namespaces) { + foreach (var n in DefaultNamespaces) { host.NamespaceImports.Add(n); } @@ -119,16 +116,17 @@ namespace Orchard.Compilation.Razor { return assembly.CreateInstance(NamespaceForDynamicClasses + "." + generatedClassName); } - public static string GetHash(string value) - { + public static string GetHash(string value) { var data = Encoding.ASCII.GetBytes(value); - var hashData = new SHA1Managed().ComputeHash(data); + var hashData = new MD5CryptoServiceProvider().ComputeHash(data); - return hashData.Aggregate(string.Empty, (current, b) => current + b.ToString("X2")); + var strBuilder = new StringBuilder(); + hashData.Aggregate(strBuilder, (current, b) => strBuilder.Append(b)); + + return strBuilder.ToString(); } - private static Assembly CreateCompiledAssemblyFor(CodeCompileUnit unitToCompile, string templateName) - { + private static Assembly CreateCompiledAssemblyFor(CodeCompileUnit unitToCompile, string templateName) { var compilerParameters = new CompilerParameters(); compilerParameters.ReferencedAssemblies.AddRange(AppDomain.CurrentDomain .GetAssemblies() diff --git a/src/Orchard/Compilation/Razor/RazorTemplateCache.cs b/src/Orchard/Compilation/Razor/RazorTemplateCache.cs index be381e0c6..b946417c7 100644 --- a/src/Orchard/Compilation/Razor/RazorTemplateCache.cs +++ b/src/Orchard/Compilation/Razor/RazorTemplateCache.cs @@ -1,10 +1,10 @@ using System.Collections.Concurrent; namespace Orchard.Compilation.Razor { - public class RazorTemplateCache : IRazorTemplateCache { + public class RazorTemplateHolder : IRazorTemplateHolder { private readonly ConcurrentDictionary _cache; - public RazorTemplateCache() { + public RazorTemplateHolder() { _cache = new ConcurrentDictionary(); } public string Get(string name) { diff --git a/src/Orchard/Environment/OrchardStarter.cs b/src/Orchard/Environment/OrchardStarter.cs index d694d0a5d..5b6b52c1a 100644 --- a/src/Orchard/Environment/OrchardStarter.cs +++ b/src/Orchard/Environment/OrchardStarter.cs @@ -9,6 +9,7 @@ using System.Web.Mvc; using Autofac; using Autofac.Configuration; using Orchard.Caching; +using Orchard.Compilation.Razor; using Orchard.Data; using Orchard.Environment.AutofacUtil; using Orchard.Environment.Configuration; @@ -67,6 +68,7 @@ namespace Orchard.Environment { builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); + //builder.RegisterType().As().SingleInstance(); RegisterVolatileProvider(builder); RegisterVolatileProvider(builder); diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 35dcdda28..dae5a497e 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -153,7 +153,7 @@ - +