如何启用RazorViewEngine在多个项目(多个启动项目使用的基础项目)中查找视图

时间:2019-04-03 18:20:54

标签: c# razor razorengine viewengine

我正在研究一个包含多个带有控制器和视图的项目的解决方案。使用VirtualPathProvider加载视图的时间过长。我现在正在尝试使用RazorViewEngine的不同方法。但是我仍然得到:找不到“索引”视图或其主视图,或者没有视图引擎支持搜索到的位置。

已经存在的是: EmbeddedVirtualPathProvider

    // source: http://www.ianmariano.com/2013/06/11/embedded-razor-views-in-mvc-4/
internal sealed class EmbeddedVirtualPathProvider : VirtualPathProvider
{
    private readonly Type _type;
    private readonly ConcurrentDictionary<string, string> _embededResources = new ConcurrentDictionary<string, string>();
    private readonly DateTime _modified;

    public EmbeddedVirtualPathProvider()
    {
        _type = GetType();
        _modified = File.GetCreationTime(_type.Assembly.Location);
    }

    public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
    {
        string embedded = GetCachedEmbeddedPath(virtualPath);

        // not embedded? fall back
        if (string.IsNullOrEmpty(embedded)) return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);

        // there is no cache dependency for embedded resources
        return null;
    }

    public override bool FileExists(string virtualPath)
    {
        string embedded = GetCachedEmbeddedPath(virtualPath);

        // You can override the embed by placing a real file
        // at the virtual path...
        return base.FileExists(virtualPath) || !string.IsNullOrEmpty(embedded);
    }

    public override VirtualFile GetFile(string virtualPath)
    {
        // You can override the embed by placing a real file
        // at the virtual path...
        if (base.FileExists(virtualPath)) 
            return base.GetFile(virtualPath);

        string embedded = GetCachedEmbeddedPath(virtualPath);

        // sanity...
        return !string.IsNullOrEmpty(embedded) 
            ? new EmbeddedVirtualFile(virtualPath, _type.Assembly.GetManifestResourceStream(embedded), _modified)
            : null;
    }

    private string GetCachedEmbeddedPath(string path)
    {
        return _embededResources.GetOrAdd(path, GetEmbeddedPath);
    }

    private string GetEmbeddedPath(string path)
    {
        if (path.StartsWith("~/")) path = path.Substring(1);

        string directory = VirtualPathUtility.GetDirectory(path);
        string file = VirtualPathUtility.GetFileName(path);

        string[] resourcePath = directory.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
        for (int i = 0; i < resourcePath.Length; i++)
        {
            resourcePath[i] = StronglyTypedResourceBuilder.VerifyResourceName(resourcePath[i], new CSharpCodeProvider());
        }

        path = string.Concat(_type.Namespace, ".", string.Join(".", resourcePath), ".", file);

        // this makes sure the "virtual path" exists as an embedded resource;
        return _type.Assembly.GetManifestResourceNames().FirstOrDefault(s => s.Equals(path, StringComparison.OrdinalIgnoreCase));
    }
}

EmbeddedVirtualFile:

internal sealed class EmbeddedVirtualFile : VirtualFile
{
    private readonly Stream _stream;
    private readonly DateTime _modified;

    public EmbeddedVirtualFile(string virtualPath, Stream stream, DateTime modified)
        : base(virtualPath)
    {
        if (null == stream) throw new ArgumentNullException("stream");
        _stream = stream;
        _modified = modified;
    }

    public override Stream Open()
    {
        // hack (http://stackoverflow.com/questions/8224075/maintaining-cache-control-property-on-a-file-when-it-is-returned-as-stream-from)
        if (VirtualPath.Equals(HttpContext.Current.Request.RawUrl, StringComparison.OrdinalIgnoreCase))
        {
            HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public);
            HttpContext.Current.Response.Cache.SetMaxAge(TimeSpan.FromMinutes(30));
            HttpContext.Current.Response.Cache.SetLastModified(_modified);
        }

        return _stream;
    }
}

从PreApplicationStartCode类初始化虚拟路径提供程序。

PreApplicationStartCode:

public class PreApplicationStartCode
{
    private static bool isStarting;

    public static void Start()
    {
        if (isStarting) return;
        isStarting = true;

        // Fix for precompiled website: http://stackoverflow.com/questions/7527571/virtualpathprovider-not-working-on-hosting-environment
        if (!BuildManager.IsPrecompiledApp)
        {
            HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedVirtualPathProvider());
            return;
        }

        // We get the current instance of HostingEnvironment class. We can't create a new because 
        // it is not allowed to do so. An AppDomain can only have one HostingEnvironment instance.
        EmbeddedVirtualPathProvider providerInstance = new EmbeddedVirtualPathProvider();
        HostingEnvironment hostingEnvironmentInstance = (HostingEnvironment)typeof(HostingEnvironment).InvokeMember("_theHostingEnvironment", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField, null, null, null);
        if (hostingEnvironmentInstance == null)
            return;

        // We get the MethodInfo for RegisterVirtualPathProviderInternal method which is internal and also static.
        MethodInfo mi = typeof(HostingEnvironment).GetMethod("RegisterVirtualPathProviderInternal", BindingFlags.NonPublic | BindingFlags.Static);
        if (mi == null)
            return;

        // finally we invoke RegisterVirtualPathProviderInternal method with one argument which
        // is the instance of our own VirtualPathProvider.
        mi.Invoke(hostingEnvironmentInstance, new object[] { providerInstance });
    }
}

这有效,但是再次启动/调试花费的时间太长。在Controller已经完成运行之后,需要花费一分钟多的时间来加载视图。

我现在要开始工作的是:

public class MultiProjectRazorViewEngine : RazorViewEngine
{
    private readonly string[] _areaMasterLocationFormats = new[]
    {
        "~/Areas/{2}/Views/{0}.cshtml",
        "~/Areas/{2}/Views/Shared/{0}.cshtml",
        "~/Views/{0}.cshtml",
        "~/Views/Shared/{0}.cshtml"
    };

    private readonly string[] _areaViewLocationFormats = new[]
    {
        "~/Areas/{2}/Views/{1}/{0}.cshtml",
        "~/Areas/{2}/Views/Shared/{1}/{0}.cshtml"
    };

    private readonly string[] _areaPartialViewLocationFormats = new[]
    {
        "~/Areas/{2}/Views/{1}/{0}.cshtml",
        "~/Areas/{2}/Views/Shared/{1}/{0}.cshtml"
    };

    private readonly string[] _masterLocationFormats = new[]
    {
        "~/Views/{0}.cshtml",
        "~/Views/Shared/{0}.cshtml"
    };

    private readonly string[] _viewLocationFormats = new[]
    {
        "~/Views/{1}/{0}.cshtml",
        "~/Views/Shared/{1}/{0}.cshtml"
    };

    private readonly string[] _partialViewLocationFormats = new[]
    {
        "~/Views/{1}/{0}.cshtml",
        "~/Views/Shared/{1}/{0}.cshtml"
    };

    public MultiProjectRazorViewEngine()
    {
        AreaMasterLocationFormats = _areaMasterLocationFormats;
        AreaViewLocationFormats = _areaViewLocationFormats;
        AreaPartialViewLocationFormats = _areaPartialViewLocationFormats;
        MasterLocationFormats = _masterLocationFormats;
        ViewLocationFormats = _viewLocationFormats;
        PartialViewLocationFormats = _partialViewLocationFormats;
    }
}

仅在正确的位置检查.cshtml文件(HomeController.Index被查找为:“〜/ Views / Home / Index.cshtml”)是正确的。但是,它没有从启动项目中找到Base项目中的控制器和/或视图。我不知道为什么会这样。似乎在我的启动项目中找不到引用的基础项目视图/控制器。有人可以告诉我为什么吗?

所以我要实现的是一个快速启动的应用程序,在控制器完成运行之后,它不需要1分钟的时间即可加载所有视图。我还不完全了解视图的位置,因此我不确定从哪里开始。 Google搜索尚无定论,而且似乎已经过时。

(但是我可能会经常看错东西……)

0 个答案:

没有答案