自定义MVC剃刀视图引擎的间歇视图路径问题

时间:2015-04-03 16:07:08

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

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

在我们部署之后,它会正常工作。然后大约20-30分钟后,我们开始查看未找到错误的错误。如果我更改web.conf文件以强制重新启动应用程序池,那么一切都可以正常工作......暂时。

似乎某种情况下,FileExists方法在某些情况下以某种方式返回true。不确定是由于缓存问题,还是在共享主机,webfarm,同一页面的多个请求同时从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;
}

}

1 个答案:

答案 0 :(得分:4)

事实证明,在发布模式下使用缓存会导致我遇到的问题。您还需要覆盖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);
        }