Update WebFormVirtualPathProvider GetFileHash method

When a WebForm view file is served through the WebFormVirtualPathProvider,
we override GetFileHash to include the virtual path of all the dependencies
used to compile the form. For example, if we had a "Assembly Name=..."
directive, we want to include the file hash of the assembly file, so that if the
assembly file is updated with a newer version, the file hash of the WebForm view
file will be different, which will tell ASP.NET the view file needs to be
recompiled.

--HG--
branch : dev
This commit is contained in:
Renaud Paquay
2010-06-18 10:23:41 -07:00
parent e9dedb765d
commit a9593f69ff
14 changed files with 172 additions and 44 deletions

View File

@@ -94,7 +94,7 @@ namespace Orchard.Tests.Stubs {
}
public void CreateDirectory(string path) {
var entry = _fileSystem.CreateDirectoryEntry(path);
_fileSystem.CreateDirectoryEntry(path);
}
public IVolatileToken WhenPathChanges(string path) {
@@ -105,6 +105,10 @@ namespace Orchard.Tests.Stubs {
throw new NotImplementedException();
}
public string GetVirtualPath(string path) {
throw new NotImplementedException();
}
public DateTime GetLastWriteTimeUtc(string path) {
var entry = _fileSystem.GetFileEntry(path);
if (entry == null)

View File

@@ -4,6 +4,7 @@ using System.Reflection;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
using Orchard.Logging;
namespace Orchard.Environment.Extensions.Loaders {
public class AreaExtensionLoader : ExtensionLoaderBase {
@@ -11,8 +12,11 @@ namespace Orchard.Environment.Extensions.Loaders {
public AreaExtensionLoader(IDependenciesFolder dependenciesFolder) {
_dependenciesFolder = dependenciesFolder;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public override int Order { get { return 50; } }
public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
@@ -30,6 +34,7 @@ namespace Orchard.Environment.Extensions.Loaders {
public override ExtensionEntry Load(ExtensionDescriptor descriptor) {
var dependency = _dependenciesFolder.GetDescriptor(descriptor.Name);
if (dependency != null && dependency.LoaderName == this.Name) {
Logger.Information("Loading extension \"{0}\"", dependency.Name);
var assembly = Assembly.Load("Orchard.Web");

View File

@@ -5,6 +5,7 @@ using Orchard.Caching;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
using Orchard.Logging;
namespace Orchard.Environment.Extensions.Loaders {
/// <summary>
@@ -15,8 +16,11 @@ namespace Orchard.Environment.Extensions.Loaders {
public CoreExtensionLoader(IDependenciesFolder dependenciesFolder) {
_dependenciesFolder = dependenciesFolder;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public override int Order { get { return 10; } }
public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
@@ -34,6 +38,7 @@ namespace Orchard.Environment.Extensions.Loaders {
public override ExtensionEntry Load(ExtensionDescriptor descriptor) {
var dependency = _dependenciesFolder.GetDescriptor(descriptor.Name);
if (dependency != null && dependency.LoaderName == this.Name) {
Logger.Information("Loading extension \"{0}\"", dependency.Name);
var assembly = Assembly.Load("Orchard.Core");

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using Orchard.Caching;
using Orchard.Environment.Extensions.Models;
@@ -30,10 +31,14 @@ namespace Orchard.Environment.Extensions.Loaders {
public override int Order { get { return 100; } }
public override string GetAssemblyDirective(DependencyDescriptor dependency) {
public override string GetWebFormAssemblyDirective(DependencyDescriptor dependency) {
return string.Format("<%@ Assembly Src=\"{0}\"%>", dependency.VirtualPath);
}
public override IEnumerable<string> GetWebFormVirtualDependencies(DependencyDescriptor dependency) {
yield return dependency.VirtualPath;
}
public override void Monitor(ExtensionDescriptor descriptor, Action<IVolatileToken> monitor) {
// We need to monitor the path to the ".csproj" file.
string projectPath = GetProjectPath(descriptor);
@@ -75,6 +80,7 @@ namespace Orchard.Environment.Extensions.Loaders {
if (dependency != null && dependency.LoaderName == this.Name) {
var assembly = _buildManager.GetCompiledAssembly(dependency.VirtualPath);
Logger.Information("Loading extension \"{0}\": assembly name=\"{1}\"", dependency.Name, assembly.GetName().Name);
return new ExtensionEntry {
Descriptor = descriptor,

View File

@@ -1,5 +1,5 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using Orchard.Caching;
using Orchard.Environment.Extensions.Models;
@@ -17,16 +17,16 @@ namespace Orchard.Environment.Extensions.Loaders {
public virtual void ExtensionDeactivated(ExtensionLoadingContext ctx, bool isNewExtension, ExtensionDescriptor extension){}
public virtual void ExtensionRemoved(ExtensionLoadingContext ctx, DependencyDescriptor dependency) { }
public virtual void Monitor(ExtensionDescriptor extension, Action<IVolatileToken> monitor) { }
public virtual string GetAssemblyDirective(DependencyDescriptor dependency) { return null; }
public static bool FileIsNewer(string sourceFileName, string destinationFileName) {
if (!File.Exists(destinationFileName))
return true;
return File.GetLastWriteTimeUtc(sourceFileName) > File.GetLastWriteTimeUtc(destinationFileName);
public virtual string GetWebFormAssemblyDirective(DependencyDescriptor dependency) {
return null;
}
public static bool IsAssemblyLoaded(string moduleName) {
public virtual IEnumerable<string> GetWebFormVirtualDependencies(DependencyDescriptor dependency) {
return Enumerable.Empty<string>();
}
protected static bool IsAssemblyLoaded(string moduleName) {
return AppDomain.CurrentDomain.GetAssemblies().Any(a => a.GetName().Name == moduleName);
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Orchard.Caching;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
@@ -24,6 +25,7 @@ namespace Orchard.Environment.Extensions.Loaders {
void Monitor(ExtensionDescriptor extension, Action<IVolatileToken> monitor);
string GetAssemblyDirective(DependencyDescriptor dependency);
string GetWebFormAssemblyDirective(DependencyDescriptor dependency);
IEnumerable<string> GetWebFormVirtualDependencies(DependencyDescriptor dependency);
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using Orchard.Caching;
using Orchard.Environment.Extensions.Models;
@@ -29,10 +30,14 @@ namespace Orchard.Environment.Extensions.Loaders {
public override int Order { get { return 30; } }
public override string GetAssemblyDirective(DependencyDescriptor dependency) {
public override string GetWebFormAssemblyDirective(DependencyDescriptor dependency) {
return string.Format("<%@ Assembly Name=\"{0}\"%>", dependency.Name);
}
public override IEnumerable<string> GetWebFormVirtualDependencies(DependencyDescriptor dependency) {
yield return _assemblyProbingFolder.GetAssemblyVirtualPath(dependency.Name);
}
public override void ExtensionRemoved(ExtensionLoadingContext ctx, DependencyDescriptor dependency) {
if (_assemblyProbingFolder.AssemblyExists(dependency.Name)) {
ctx.DeleteActions.Add(() => _assemblyProbingFolder.DeleteAssembly(dependency.Name));
@@ -52,7 +57,7 @@ namespace Orchard.Environment.Extensions.Loaders {
bool copyAssembly =
!_assemblyProbingFolder.AssemblyExists(extension.Name) ||
File.GetLastWriteTimeUtc(sourceFileName) > _assemblyProbingFolder.GetAssemblyDateTimeUtc(extension.Name);
if (copyAssembly) {
ctx.CopyActions.Add(() => _assemblyProbingFolder.StoreAssembly(extension.Name, sourceFileName));
// We need to restart the appDomain if the assembly is loaded
@@ -104,6 +109,8 @@ namespace Orchard.Environment.Extensions.Loaders {
if (assembly == null)
return null;
Logger.Information("Loading extension \"{0}\"", dependency.Name);
return new ExtensionEntry {
Descriptor = descriptor,
Assembly = assembly,

View File

@@ -1,4 +1,5 @@
using System.IO;
using System.Collections.Generic;
using System.IO;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
using Orchard.Logging;
@@ -22,10 +23,14 @@ namespace Orchard.Environment.Extensions.Loaders {
public override int Order { get { return 40; } }
public override string GetAssemblyDirective(DependencyDescriptor dependency) {
public override string GetWebFormAssemblyDirective(DependencyDescriptor dependency) {
return string.Format("<%@ Assembly Name=\"{0}\"%>", dependency.Name);
}
public override IEnumerable<string> GetWebFormVirtualDependencies(DependencyDescriptor dependency) {
yield return _assemblyProbingFolder.GetAssemblyVirtualPath(dependency.Name);
}
public override void ExtensionRemoved(ExtensionLoadingContext ctx, DependencyDescriptor dependency) {
if (_assemblyProbingFolder.AssemblyExists(dependency.Name)) {
ctx.DeleteActions.Add(() => _assemblyProbingFolder.DeleteAssembly(dependency.Name));
@@ -74,6 +79,8 @@ namespace Orchard.Environment.Extensions.Loaders {
if (assembly == null)
return null;
Logger.Information("Loading extension \"{0}\"", dependency.Name);
return new ExtensionEntry {
Descriptor = descriptor,
Assembly = assembly,

View File

@@ -6,6 +6,7 @@ using System.Web.Hosting;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
using Orchard.Logging;
namespace Orchard.Environment.Extensions.Loaders {
/// <summary>
@@ -18,8 +19,11 @@ namespace Orchard.Environment.Extensions.Loaders {
public ReferencedExtensionLoader(IDependenciesFolder dependenciesFolder, IVirtualPathProvider virtualPathProvider) {
_dependenciesFolder = dependenciesFolder;
_virtualPathProvider = virtualPathProvider;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public override int Order { get { return 20; } }
public override void ExtensionDeactivated(ExtensionLoadingContext ctx, bool isNewExtension, ExtensionDescriptor extension) {
@@ -59,6 +63,11 @@ namespace Orchard.Environment.Extensions.Loaders {
.OfType<Assembly>()
.FirstOrDefault(x => x.GetName().Name == descriptor.Name);
if (assembly == null)
return null;
Logger.Information("Loading extension \"{0}\"", dependency.Name);
return new ExtensionEntry {
Descriptor = descriptor,
Assembly = assembly,

View File

@@ -85,8 +85,11 @@ namespace Orchard.FileSystems.AppData {
}
public IVolatileToken WhenPathChanges(string path) {
var replace = Combine(AppDataPath, path);
return _virtualPathMonitor.WhenPathChanges(replace);
return _virtualPathMonitor.WhenPathChanges(GetVirtualPath(path));
}
public string GetVirtualPath(string path) {
return Combine(AppDataPath, path);
}
public void CreateFile(string path, string content) {

View File

@@ -30,5 +30,6 @@ namespace Orchard.FileSystems.AppData {
IVolatileToken WhenPathChanges(string path);
string MapPath(string path);
string GetVirtualPath(string path);
}
}

View File

@@ -21,6 +21,14 @@ namespace Orchard.FileSystems.Dependencies {
return _appDataFolder.GetFileLastWriteTimeUtc(path);
}
public string GetAssemblyVirtualPath(string moduleName) {
var path = PrecompiledAssemblyPath(moduleName);
if (!_appDataFolder.FileExists(path))
return null;
return _appDataFolder.GetVirtualPath(path);
}
public Assembly LoadAssembly(string moduleName) {
var path = PrecompiledAssemblyPath(moduleName);
if (!_appDataFolder.FileExists(path))

View File

@@ -22,6 +22,11 @@ namespace Orchard.FileSystems.Dependencies {
/// </summary>
DateTime GetAssemblyDateTimeUtc(string moduleName);
/// <summary>
/// Return the virtual path of the assembly (optional)
/// </summary>
string GetAssemblyVirtualPath(string moduleName);
/// <summary>
/// Load the assembly corresponding to "moduleName" if the assembly file
/// is present in the folder.

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web.Hosting;
@@ -30,16 +31,73 @@ namespace Orchard.FileSystems.Dependencies {
return Previous.FileExists(virtualPath);
}
public override string GetFileHash(string virtualPath, System.Collections.IEnumerable virtualPathDependencies) {
var result = GetFileHashWorker(virtualPath, virtualPathDependencies);
Logger.Information("GetFileHash(\"{0}\"): {1}", virtualPath, result);
return result;
}
private string GetFileHashWorker(string virtualPath, IEnumerable virtualPathDependencies) {
// We override the "GetFileHash()" behavior to take into account the dependencies folder
// state. This ensures that if any dependency changes, ASP.NET will recompile views that
// have been customized to include custom assembly directives.
var file = GetModuleVirtualOverride(virtualPath) ?? GetThemeVirtualOverride(virtualPath);
if (file == null) {
return base.GetFileHash(virtualPath, virtualPathDependencies);
}
var dependencies =
virtualPathDependencies
.OfType<string>()
.Concat(file.Loaders.SelectMany(dl => dl.Loader.GetWebFormVirtualDependencies(dl.Descriptor)));
if (Logger.IsEnabled(LogLevel.Debug)) {
Logger.Debug("GetFilHash(\"{0}\") - virtual path dependencies:", virtualPath);
foreach(var dependency in dependencies) {
Logger.Debug(" Dependency: \"{0}\"", dependency);
}
}
return base.GetFileHash(virtualPath, dependencies);
}
public override VirtualFile GetFile(string virtualPath) {
Logger.Debug("GetFile(\"{0}\")", virtualPath);
var actualFile = Previous.GetFile(virtualPath);
return
GetModuleCustomVirtualFile(virtualPath, actualFile) ??
GetThemeCustomVirtualFile(virtualPath, actualFile) ??
actualFile;
return GetModuleCustomVirtualFile(virtualPath, actualFile) ??
GetThemeCustomVirtualFile(virtualPath, actualFile) ??
actualFile;
}
private VirtualFile GetModuleCustomVirtualFile(string virtualPath, VirtualFile actualFile) {
var file = GetModuleVirtualOverride(virtualPath);
if (file == null)
return null;
if (Logger.IsEnabled(LogLevel.Debug)) {
Logger.Debug("Virtual file from module \"{0}\" served with specific assembly directive:", file.ModuleName);
Logger.Debug(" Virtual path: {0}", virtualPath);
Logger.Debug(" Assembly directive: {0}", file.Directive);
}
return new WebFormsExtensionsVirtualFile(virtualPath, actualFile, file.Directive);
}
private VirtualFile GetThemeCustomVirtualFile(string virtualPath, VirtualFile actualFile) {
var file = GetThemeVirtualOverride(virtualPath);
if (file == null)
return null;
if (Logger.IsEnabled(LogLevel.Debug)) {
Logger.Debug("Virtual file from theme served with specific assembly directive:");
Logger.Debug(" Virtual path: {0}", virtualPath);
Logger.Debug(" Assembly directive: {0}", file.Directive);
}
return new WebFormsExtensionsVirtualFile(virtualPath, actualFile, file.Directive);
}
private VirtualFileOverride GetModuleVirtualOverride(string virtualPath) {
var prefix = PrefixMatch(virtualPath, _modulesPrefixes);
if (prefix == null)
return null;
@@ -60,20 +118,18 @@ namespace Orchard.FileSystems.Dependencies {
if (loader == null)
return null;
var directive = loader.GetAssemblyDirective(dependencyDescriptor);
var directive = loader.GetWebFormAssemblyDirective(dependencyDescriptor);
if (string.IsNullOrEmpty(directive))
return null;
if (Logger.IsEnabled(LogLevel.Debug)) {
Logger.Debug("Virtual file from module \"{0}\" served with specific assembly directive:", moduleName);
Logger.Debug(" Virtual path: {0}", virtualPath);
Logger.Debug(" Assembly directive: {0}", directive);
}
return new WebFormsExtensionsVirtualFile(virtualPath, actualFile, directive);
return new VirtualFileOverride {
ModuleName = moduleName,
Directive = directive,
Loaders = new[] { new DependencyLoader { Loader = loader, Descriptor = dependencyDescriptor } }
};
}
private VirtualFile GetThemeCustomVirtualFile(string virtualPath, VirtualFile actualFile) {
private VirtualFileOverride GetThemeVirtualOverride(string virtualPath) {
var prefix = PrefixMatch(virtualPath, _themesPrefixes);
if (prefix == null)
return null;
@@ -82,24 +138,23 @@ namespace Orchard.FileSystems.Dependencies {
if (extension == null)
return null;
string directive = _dependenciesFolder.LoadDescriptors().Aggregate("", (s, desc) => {
var loader = _loaders.Where(l => l.Name == desc.LoaderName).FirstOrDefault();
if (loader == null)
return s;
else {
return s + loader.GetAssemblyDirective(desc);
}});
var loaders = _loaders
.SelectMany(loader => _dependenciesFolder
.LoadDescriptors()
.Where(d => d.LoaderName == loader.Name),
(loader, desr) => new DependencyLoader { Loader = loader, Descriptor = desr });
var directive = loaders
.Aggregate("", (s, dl) => s + dl.Loader.GetWebFormAssemblyDirective(dl.Descriptor));
if (string.IsNullOrEmpty(directive))
return null;
if (Logger.IsEnabled(LogLevel.Debug)) {
Logger.Debug("Virtual file from theme served with specific assembly directive:");
Logger.Debug(" Virtual path: {0}", virtualPath);
Logger.Debug(" Assembly directive: {0}", directive);
}
return new WebFormsExtensionsVirtualFile(virtualPath, actualFile, directive);
return new VirtualFileOverride {
ModuleName = "",
Directive = directive,
Loaders = loaders
};
}
private string ModuleMatch(string virtualPath, string prefix) {
@@ -124,5 +179,16 @@ namespace Orchard.FileSystems.Dependencies {
VirtualPathProvider ICustomVirtualPathProvider.Instance {
get { return this; }
}
private class VirtualFileOverride {
public string ModuleName { get; set; }
public string Directive { get; set; }
public IEnumerable<DependencyLoader> Loaders { get; set; }
}
private class DependencyLoader {
public IExtensionLoader Loader { get; set; }
public DependencyDescriptor Descriptor { get; set; }
}
}
}