Removing VirtualFileSystem from Razor

* Paths rooted by a leading slash (e.g. /foo) are correctly resolved by the
PhysicalFileSystem. This change is an experiment to determine if we can
get away with not having virtual paths in WebFx.

* Additionally removing types (MetadataVirtualPathViewFactory,
 VirtualPathAttribute) that are currently unused.
This commit is contained in:
Pranav K 2014-02-04 06:34:34 -08:00
parent 9365004248
commit 6c8485b1ef
13 changed files with 247 additions and 190 deletions

View File

@ -5,9 +5,9 @@ namespace MvcSample
{
public class HomeController : Controller
{
public string Index()
public IActionResult Index()
{
return "Hello World";
return View("MyView", User());
}
public IActionResult Something()

View File

@ -28,7 +28,6 @@ namespace MvcSample
string appRoot = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, "..", "..", ".."));
serviceProvider.AddInstance<IFileSystem>(new PhysicalFileSystem(appRoot));
serviceProvider.Add<IVirtualFileSystem, VirtualFileSystem>();
serviceProvider.AddInstance<IMvcRazorHost>(new MvcRazorHost("Microsoft.AspNet.Mvc.Razor.RazorView<dynamic>"));
serviceProvider.Add<ICompilationService, CscBasedCompilationService>();
serviceProvider.Add<IRazorCompilationService, RazorCompilationService>();

View File

@ -1,6 +1,6 @@

@{
Layout = "~/Views/Shared/_Layout.cshtml";
Layout = "/Views/Shared/_Layout.cshtml";
ViewBag.Title = "Home Page";
}

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.Routing
{
if (name.Equals("controller", StringComparison.OrdinalIgnoreCase))
{
return GetPartOrDefault(0, "HomeController");
return GetPartOrDefault(0, "Home");
}
else if (name.Equals("action", StringComparison.OrdinalIgnoreCase))
{

View File

@ -0,0 +1,83 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34003
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Microsoft.AspNet.Mvc.Razor
{
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if (object.ReferenceEquals(resourceMan, null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.Mvc.Razor.Resources", System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(Resources)).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Value cannot be null or empty..
/// </summary>
internal static string ArgumentCannotBeNullOrEmpty
{
get
{
return ResourceManager.GetString("ArgumentCannotBeNullOrEmpty", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
<value>Value cannot be null or empty.</value>
</data>
</root>

View File

@ -1,8 +0,0 @@
using Microsoft.Owin.FileSystems;
namespace Microsoft.AspNet.Mvc.Razor
{
public interface IVirtualFileSystem : IFileSystem
{
}
}

View File

@ -1,51 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.Razor
{
public class MetadataVirtualPathViewFactory : IVirtualPathViewFactory
{
private readonly Dictionary<string, Type> _viewMetadata;
public MetadataVirtualPathViewFactory(Assembly assembly)
{
var metadataType = assembly.GetType("ViewMetadata");
if (metadataType != null)
{
object metadata = metadataType.GetRuntimeProperties().First(prop => prop.Name == "Metadata")
.GetValue(obj: null);
_viewMetadata = new Dictionary<string, Type>((Dictionary<string, Type>)metadata, StringComparer.OrdinalIgnoreCase);
}
else
{
#if NET45
// Code to support precompiled views generated via RazorGenerator
_viewMetadata = assembly.GetExportedTypes()
.Where(type => typeof(RazorView).IsAssignableFrom(type))
.ToDictionary(type => GetVirtualPath(type), StringComparer.OrdinalIgnoreCase);
#endif
}
}
public Task<IView> CreateInstance(string virtualPath)
{
Type type;
IView view = null;
if (_viewMetadata.TryGetValue(virtualPath, out type))
{
view = (IView)Activator.CreateInstance(type);
}
return Task.FromResult(view);
}
private static string GetVirtualPath(Type type)
{
VirtualPathAttribute attribute = type.GetTypeInfo().GetCustomAttribute<VirtualPathAttribute>();
return attribute.VirtualPath;
}
}
}

View File

@ -6,10 +6,10 @@ namespace Microsoft.AspNet.Mvc.Razor
{
public class VirtualPathViewFactory : IVirtualPathViewFactory
{
private readonly IVirtualFileSystem _fileSystem;
private readonly IFileSystem _fileSystem;
private readonly IRazorCompilationService _compilationService;
public VirtualPathViewFactory(IVirtualFileSystem fileSystem, IRazorCompilationService compilationService)
public VirtualPathViewFactory(IFileSystem fileSystem, IRazorCompilationService compilationService)
{
_fileSystem = fileSystem;
_compilationService = compilationService;

View File

@ -9,8 +9,8 @@ namespace Microsoft.AspNet.Mvc.Razor
{
private static readonly string[] _viewLocationFormats = new[]
{
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
"/Views/{1}/{0}.cshtml",
"/Views/Shared/{0}.cshtml",
};
private readonly IActionDescriptorProvider _actionDescriptorProvider;
private readonly IVirtualPathViewFactory _virtualPathFactory;
@ -41,19 +41,41 @@ namespace Microsoft.AspNet.Mvc.Razor
viewName = actionDescriptor.ActionName;
}
string controllerName = actionDescriptor.ControllerName;
var searchedLocations = new List<string>(_viewLocationFormats.Length);
for (int i = 0; i < _viewLocationFormats.Length; i++)
if (String.IsNullOrEmpty(viewName))
{
string path = String.Format(CultureInfo.InvariantCulture, _viewLocationFormats[i], viewName, controllerName);
IView view = await _virtualPathFactory.CreateInstance(path);
if (view != null)
{
return ViewEngineResult.Found(view);
}
searchedLocations.Add(path);
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "viewName");
}
return ViewEngineResult.NotFound(searchedLocations);
bool nameRepresentsPath = IsSpecificPath(viewName);
if (nameRepresentsPath)
{
IView view = await _virtualPathFactory.CreateInstance(viewName);
return view != null ? ViewEngineResult.Found(view) :
ViewEngineResult.NotFound(new[] { viewName });
}
else
{
string controllerName = actionDescriptor.ControllerName;
var searchedLocations = new List<string>(_viewLocationFormats.Length);
for (int i = 0; i < _viewLocationFormats.Length; i++)
{
string path = String.Format(CultureInfo.InvariantCulture, _viewLocationFormats[i], viewName, controllerName);
IView view = await _virtualPathFactory.CreateInstance(path);
if (view != null)
{
return ViewEngineResult.Found(view);
}
searchedLocations.Add(path);
}
return ViewEngineResult.NotFound(searchedLocations);
}
}
private static bool IsSpecificPath(string name)
{
char c = name[0];
return (name[0] == '/');
}
}
}

View File

@ -1,48 +0,0 @@
using System;
using System.IO;
using Microsoft.Owin.FileSystems;
namespace Microsoft.AspNet.Mvc.Razor
{
public class VirtualFile : IFileInfo
{
private readonly string _virtualPath;
private readonly IFileInfo _underlyingFileInfo;
public VirtualFile(string virtualPath, IFileInfo underlyingFileInfo)
{
_virtualPath = virtualPath;
_underlyingFileInfo = underlyingFileInfo;
}
public Stream CreateReadStream()
{
return _underlyingFileInfo.CreateReadStream();
}
public bool IsDirectory
{
get { return _underlyingFileInfo.IsDirectory; }
}
public DateTime LastModified
{
get { return _underlyingFileInfo.LastModified; }
}
public long Length
{
get { return _underlyingFileInfo.Length; }
}
public string Name
{
get { return _underlyingFileInfo.Name; }
}
public string PhysicalPath
{
get { return _virtualPath; }
}
}
}

View File

@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Owin.FileSystems;
namespace Microsoft.AspNet.Mvc.Razor
{
public class VirtualFileSystem : IVirtualFileSystem
{
private readonly IFileSystem _fileSystem;
public VirtualFileSystem(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
public bool TryGetFileInfo(string subpath, out IFileInfo fileInfo)
{
string translated = TranslatePath(subpath);
if (_fileSystem.TryGetFileInfo(translated, out fileInfo))
{
fileInfo = new VirtualFile(subpath, fileInfo);
return true;
}
return false;
}
public bool TryGetDirectoryContents(string subpath, out IEnumerable<IFileInfo> contents)
{
string translated = TranslatePath(subpath);
if (_fileSystem.TryGetDirectoryContents(translated, out contents))
{
contents = contents.Select(c => new VirtualFile(subpath + '/' + c.Name, c));
return true;
}
return false;
}
private static string TranslatePath(string path)
{
if (path.StartsWith("~/", StringComparison.Ordinal))
{
path = path.Substring(2);
}
return path;
}
}
}

View File

@ -1,15 +0,0 @@
using System;
namespace Microsoft.AspNet.Mvc.Razor
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public class VirtualPathAttribute : Attribute
{
public VirtualPathAttribute(string virtualPath)
{
VirtualPath = virtualPath;
}
public string VirtualPath { get; private set; }
}
}