使用共享库在项目之间共享剃刀视图

时间:2015-10-12 02:46:04

标签: asp.net-mvc asp.net-mvc-4 razor

我正在尝试从一个项目到另一个项目共享剃刀视图。起初,我尝试使用RazorGenerator扩展程序(Precompile your MVC Razor views using RazorGenerator

但是,我只使用VS 2013 Web Express,因此无法正常使用扩展程序(我只能使用Web Express)。

现在,我已经尝试了这个:Sharing MVC Views,但我无法让它找到视图。我所做的是创建了一个类库" ClassLibrary2",添加了一个文件夹" Views"在它下面"共享"。在"共享"文件夹,我添加了一个视图" MyView.cshtml" (此文件为空)然后使用" MyView.cshtml"编译项目。设置为嵌入式资源。

在此之后,我创建了一个新的MVC项目(MVC模板),引用了" ClassLibrary2"并添加了页面中提到的代码(" EmbeddedResourceViewPathProvider"以及" EmbeddedResourceFile"类)。还在Application_Start()和#34; EmbeddedResourceFile"的代码中添加了代码,我已更改为:

var resourcename = 
        virtualPath
        .Substring(virtualPath.IndexOf("Views/"))
        // NB: Your assembly name here
        .Replace("Views/", "ClassLibrary2.")
        .Replace("/", ".");

但是,仍然找不到视图。

2 个答案:

答案 0 :(得分:0)

我在尝试自己做类似的事情时发现了这个问题,并且我能够使用链接文章(共享MVC视图)来完成从另一个程序集共享视图作为资源。如果您仍在尝试解决此问题,那么发布更多代码可能会有所帮助。我发现这种方法并不适合我开箱即用;根本问题是确保资源名称请求"命中"有点棘手。您的EmbeddedResourceViewPathProvider。

具体来说,EmbeddedResourceFile类上的GetResourceName函数将采用虚拟路径(如"〜/ Views / _foo.cshtml")并尝试将其转换为资源名称(如MyApplication.SomeNamespace) ._foo.cshtml);这一行:

   var resourcename = 
        virtualPath
        .Substring(virtualPath.IndexOf("Views/"))
        // NB: Your assembly name here
        .Replace("Views/", "LeapingGorilla.Common.MVC.Views.")
        .Replace("/", ".");

可能会产生误导,因为它说'#34;你的汇编名称在这里",当你真正想要的时候有一个命名空间(不一定是相同的,在我的情况下确实不一样)。我发现实现建议的解决方案很有帮助,然后通过ResourceFileExists函数调试以查看解决方案正在寻找什么。让我们举一个例子:

假设我有一个名为ViewLibrary的项目,其文件夹/文件结构如下:

ViewLibrary
|-->Web
|---->Views
|------>_example.cshtml

在一个引用ViewLibrary的Web项目中,我有一个Razor文件要求〜/ Views / _example.cshtml。 MVC将尝试在其所有VirtualPathProviders中查找资源,因此最终将调用GetResourceName函数,以将〜/ Views / _example.cshtml解析为资源名称。在我们的例子中。该资源名为ViewLibrary.Web.Views._example.cshtml,因此替换将需要如下所示:

.Replace("Views/", "ViewLibrary.Web.Views.")

我遇到的另一个问题是MVC会小写路径名,因此您可能需要调整实际获取资源的功能,以便对此进行说明。对于我的解决方案,我实际上让我的路径提供程序通过资源文件对象传递资源名称数组,以不区分大小写的方式解析它。以下是我如何实现这一目标的完整示例:

ViewLibrary / EmbeddedResourceViewPathProvider.cs:

using System;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Web.Hosting;

namespace ViewLibrary
{
    public class EmbeddedResourceViewPathProvider : VirtualPathProvider
    {
        private readonly Lazy<string[]> _resourceNames =
            new Lazy<string[]>(() => Assembly.GetExecutingAssembly().GetManifestResourceNames(),
                LazyThreadSafetyMode.ExecutionAndPublication);

        private bool ResourceFileExists(string virtualPath)
        {
            var resourcename = EmbeddedResourceFile
                                .GetResourceName(virtualPath);
            var result = resourcename != null
                && _resourceNames.Value.Select(v => v.ToLower()).Contains(resourcename.ToLower());

            return result;
        }

        public override bool FileExists(string virtualPath)
        {
            return base.FileExists(virtualPath)
                || ResourceFileExists(virtualPath);
        }

        public override VirtualFile GetFile(string virtualPath)
        {
            if (!base.FileExists(virtualPath))
            {
                return new EmbeddedResourceFile(virtualPath, _resourceNames);
            }

            return base.GetFile(virtualPath);
        }
    }
}

ViewLibrary / EmbeddedResourceFile.cs:

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web.Hosting;

namespace ViewLibrary
{
    public class EmbeddedResourceFile : VirtualFile
    {
        private readonly Lazy<string[]> resourceNames;

        public EmbeddedResourceFile(string virtualPath) :
            base(virtualPath)
        {
            resourceNames = null;
        }

        public EmbeddedResourceFile(string virtualPath, Lazy<string[]> resourceNames) :
            base(virtualPath)
        {
            this.resourceNames = resourceNames;
        }

        public static string GetResourceName(string virtualPath)
        {
            if (!virtualPath.Contains("/Views/"))
            {
                return null;
            }

            var resourceName =
                virtualPath
                .Substring(virtualPath.IndexOf("Views/"))
                .Replace("Views/", "ViewLibrary.Web.Views.")
                .Replace("/", ".");

            return resourceName;
        }

        public override Stream Open()
        {
            var assembly = Assembly.GetExecutingAssembly();
            var resourceName = GetResourceName(VirtualPath);
            // Make resource search case-insensitive
            if (resourceNames != null)
            {
                string foundResource = resourceNames.Value.FirstOrDefault(v => v.ToLower() == resourceName.ToLower());
                if (foundResource != null)
                {
                    resourceName = foundResource;
                }
            }
            return assembly.GetManifestResourceStream(resourceName);
        }
    }
}

希望有所帮助,感谢您指点我帮助我解决问题的文章!

答案 1 :(得分:0)

这是Mira Javora使用Visual Studio Extension RazorGenerator的一篇非常好的文章......

http://blog.mirajavora.com/re-use-mvc-views-across-projects-with-razor-generator/

实际上,这篇文章使用RazorGenerator工具将.cshtml文件编译成.cs文件。生成的.cs文件包含虚拟路径 - 例如......

[System.Web.WebPages.PageVirtualPathAttribute("~/Views/Shared/Error.cshtml")]

我遵循了写作的说明,并且它第一次工作。好文章。