【问题标题】:Intermittent view path issue with custom MVC razor view engine自定义 MVC 剃刀视图引擎的间歇性视图路径问题
【发布时间】:2015-04-03 16:07:07
【问题描述】:

我们有一个 Sass 应用程序,我们刚刚实现了一个自定义 razor 视图引擎,以允许我们根据当前登录的用户提供不同的视图。在 Dev 中,这一切都很好。但是,在生产环境(共享网络主机)中,我们会遇到间歇性问题,即尝试使用不正确的视图路径提供不存在的视图。

发生的情况是,在我们部署之后,它工作正常。然后大约 20 - 30 分钟后,我们开始收到未找到视图的错误。如果我更改 web.conf 文件以强制重新启动应用程序池,那么一切都会再次正常运行......一段时间。

似乎不知何故,FileExists 方法在某些情况下以某种方式为这些路径返回 true。不确定这是由于缓存问题,还是在共享主机、网络农场、同一页面的多个请求同时从 FileExists 交叉获取结果等?我不知道。

错误:

System.Web.HttpException: The file '/Areas/OrderMgmt/Views/HH/ManageOrders/Pickup.cshtml' does not exist.

在上述情况下,该视图不存在,它位于 _Base 文件夹中:/Areas/OrderMgmt/Views/HH/ManageOrders/Pickup.cshtml

下面是自定义视图引擎代码:

{
//http://lonetechie.com/2012/09/25/multi-tenant-architecture-with-asp-net-mvc-4/
public class MulitTenantRazorViewEngine : RazorViewEngine
{
    public const string baseFolderPath = "_Base";

    public MulitTenantRazorViewEngine()
    {
        _logger = LogManager.GetLogger(GetType());

        AreaViewLocationFormats = new[] {
        "~/Areas/{2}/Views/%1/{1}/{0}.cshtml",
        "~/Areas/{2}/Views/%1/{1}/{0}.vbhtml",
        "~/Areas/{2}/Views/%1/Shared/{0}.cshtml",
        "~/Areas/{2}/Views/%1/Shared/{0}.vbhtml",
        "~/Areas/{2}/Views/_Base/{1}/{0}.cshtml",
        "~/Areas/{2}/Views/_Base/{1}/{0}.vbhtml",
        "~/Areas/{2}/Views/_Base/Shared/{0}.cshtml",
        "~/Areas/{2}/Views/_Base/Shared/{0}.vbhtml"
        };

        AreaMasterLocationFormats = new[] {
        "~/Areas/{2}/Views/%1/{1}/{0}.cshtml",
        "~/Areas/{2}/Views/%1/{1}/{0}.vbhtml",
        "~/Areas/{2}/Views/%1/Shared/{0}.cshtml",
        "~/Areas/{2}/Views/%1/Shared/{0}.vbhtml",
        "~/Areas/{2}/Views/_Base/{1}/{0}.cshtml",
        "~/Areas/{2}/Views/_Base/{1}/{0}.vbhtml",
        "~/Areas/{2}/Views/_Base/Shared/{0}.cshtml",
        "~/Areas/{2}/Views/_Base/Shared/{0}.vbhtml"
        };

        AreaPartialViewLocationFormats = new[] {
        "~/Areas/{2}/Views/%1/{1}/{0}.cshtml",
        "~/Areas/{2}/Views/%1/{1}/{0}.vbhtml",
        "~/Areas/{2}/Views/%1/Shared/{0}.cshtml",
        "~/Areas/{2}/Views/%1/Shared/{0}.vbhtml",
        "~/Areas/{2}/Views/_Base/{1}/{0}.cshtml",
        "~/Areas/{2}/Views/_Base/{1}/{0}.vbhtml",
        "~/Areas/{2}/Views/_Base/Shared/{0}.cshtml",
        "~/Areas/{2}/Views/_Base/Shared/{0}.vbhtml"
        };

        ViewLocationFormats = new[] {
        "~/Views/%1/{1}/{0}.cshtml",
        "~/Views/%1/{1}/{0}.vbhtml",
        "~/Views/%1/Shared/{0}.cshtml",
        "~/Views/%1/Shared/{0}.vbhtml",
        "~/Views/_Base/{1}/{0}.cshtml",
        "~/Views/_Base/{1}/{0}.vbhtml",
        "~/Views/_Base/Shared/{0}.cshtml",
        "~/Views/_Base/Shared/{0}.vbhtml"
        };

        MasterLocationFormats = new[] {
        "~/Views/%1/{1}/{0}.cshtml",
        "~/Views/%1/{1}/{0}.vbhtml",
        "~/Views/%1/Shared/{0}.cshtml",
        "~/Views/%1/Shared/{0}.vbhtml",
        "~/Views/_Base/{1}/{0}.cshtml",
        "~/Views/_Base/{1}/{0}.vbhtml",
        "~/Views/_Base/Shared/{0}.cshtml",
        "~/Views/_Base/Shared/{0}.vbhtml"
        };

        PartialViewLocationFormats = new[] {
        "~/Views/%1/{1}/{0}.cshtml",
        "~/Views/%1/{1}/{0}.vbhtml",
        "~/Views/%1/Shared/{0}.cshtml",
        "~/Views/%1/Shared/{0}.vbhtml",
        "~/Views/_Base/{1}/{0}.cshtml",
        "~/Views/_Base/{1}/{0}.vbhtml",
        "~/Views/_Base/Shared/{0}.cshtml",
        "~/Views/_Base/Shared/{0}.vbhtml"
        };
    }

    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
    {
        var PassedController = controllerContext.Controller as BaseController;
        Debug.Assert(PassedController != null, "PassedController != null");
        return base.CreatePartialView(controllerContext, GetTenantViewPath(partialPath, PassedController));
    }

    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
    {
        var PassedController = controllerContext.Controller as BaseController;
        Debug.Assert(PassedController != null, "PassedController != null");
        return base.CreateView(controllerContext, GetTenantViewPath(viewPath, PassedController), GetTenantViewPath(masterPath, PassedController));
    }

    protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
    {
        var PassedController = controllerContext.Controller as BaseController;
        Debug.Assert(PassedController != null, "PassedController != null");
        var tenantViewPath = GetTenantViewPath(virtualPath, PassedController);

        var isFound = base.FileExists(controllerContext, tenantViewPath);
        _logger.Debug(String.Format("Is Found: {0} Path: {1}.", isFound.ToString(), tenantViewPath));

        return isFound;
    }

    private string GetTenantViewPath(string virtualPath, BaseController PassedController)
    {
        string strReplacementString = "";

        if (PassedController == null)
        {
            strReplacementString = baseFolderPath;
        } 

        else if(PassedController.User == null) {
            strReplacementString = baseFolderPath;
        } 
        else 
        {
            strReplacementString = PassedController.User.CurrentAccountCode ?? baseFolderPath;
        }


        return virtualPath.Replace("%1", strReplacementString);
    }

    private readonly ILog _logger;
}

}

【问题讨论】:

  • 我建议你改写你的问题并选择更好的标签。自定义视图引擎与 .NET 路由无关。

标签: asp.net-mvc razor asp.net-mvc-5 viewengine


【解决方案1】:

事实证明,在发布模式下使用了缓存,这导致了我遇到的问题。您还需要重写 FindView 和 FindPartialView 方法并将 useCache 设置为 false:

//to not used Cached paths. see one of the comments here: http://lonetechie.com/2012/09/25/multi-tenant-architecture-with-asp-net-mvc-4/
        public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            return base.FindView(controllerContext, viewName, masterName, false);
        }

        //to not used Cached paths. see one of the comments here: http://lonetechie.com/2012/09/25/multi-tenant-architecture-with-asp-net-mvc-4/
        public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
        {
            return base.FindPartialView(controllerContext, partialViewName, false);
        }

【讨论】:

  • 非常感谢!我们遇到了完全相同的情况,我的同事偶然发现了您的答案,这似乎已经解决了问题。
  • @Anil 很高兴它帮助了你!
  • 哥们,你需要被女王封为爵士!非常感谢@ChadRichardson!
  • @ProxyTech 很高兴它帮助了你!
  • 您是否研究过保留缓存的方法?我有同样的问题,关闭缓存将解决我的直接问题,但我担心性能。最好能找到一种方法让缓存返回正确的路径。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-12
  • 2011-04-07
  • 1970-01-01
  • 2011-08-12
  • 1970-01-01
相关资源
最近更新 更多