如何实现自定义RazorViewEngine以在非标准位置查找视图?

时间:2022-09-02 13:32:47

I'm looking at implementing a custom RazorViewEngine. Basically I have two sites with effectively the same code base. The differences being that they look different. I want to override the standard view engine to make MVC look in two separate locations for it's views, layouts, etc. one for company A and another for Company B. Company A will contain the master views and company B's view will override these masters. So I want the View Engine to look in location B for a view, layout, master or partial if it finds it then return it, if it doesn't find it I want it to default to company A's views as the default. Obviously company A will only look in it's own folder.

我正在寻找实现自定义RazorViewEngine。基本上我有两个有效的相同代码库的网站。不同之处在于它们看起来不同。我想覆盖标准视图引擎,使MVC在两个不同的位置查看它的视图,布局等,一个用于公司A,另一个用于公司B.公司A将包含主视图,公司B的视图将覆盖这些主视图。因此,我希望View Engine在位置B中查找视图,布局,主控或部分,如果找到它然后返回它,如果找不到它我希望它默认为公司A的视图作为默认值。显然,公司A只会查看它自己的文件夹。

Ok to the crux of the question: I've found this site: http://www.aspnetwiki.com/mvc-3-razor:extending-the-view-engine

好的问题的关键:我找到了这个网站:http://www.aspnetwiki.com/mvc-3-razor:extending-the-view-engine

First question, is this the best way to achieve this?

第一个问题,这是实现这一目标的最佳方法吗?

Second do I need to override the CreatePartial, CreateView, FindPartial and FindView methods?

我需要覆盖CreatePartial,CreateView,FindPartial和FindView方法吗?

Update

更新

Ok I've figured out the second question myself, the Methods I want to override are CreateView and CreatePartialView as at this point it's built the view string and I can fiddle with it.

好吧我自己想出了第二个问题,我想要覆盖的方法是CreateView和CreatePartialView,因为此时它构建了视图字符串,我可以摆弄它。

3 个解决方案

#1


15  

Ok in the end I opted for an approach detailed here: http://weblogs.asp.net/imranbaloch/archive/2011/06/27/view-engine-with-dynamic-view-location.aspx

好的,最后我选择了一个详细的方法:http://weblogs.asp.net/imranbaloch/archive/2011/06/27/view-engine-with-dynamic-view-location.aspx

thanks to @Adriano for the answers and pointers but in the end I think this approach fits my needs better. The approach below allows me to keep the standard functionality but to create a new higher priority view location to be searched.

感谢@Adriano的答案和指示,但最后我认为这种方法更符合我的需求。下面的方法允许我保留标准功能,但是要创建一个新的更高优先级的视图位置进行搜索。

public class Travel2ViewEngine : RazorViewEngine
{
    protected BrandNameEnum BrandName;
    private string[] _newAreaViewLocations = new string[] {
        "~/Areas/{2}/%1Views/{1}/{0}.cshtml",
        "~/Areas/{2}/%1Views/{1}/{0}.vbhtml",
        "~/Areas/{2}/%1Views//Shared/{0}.cshtml",
        "~/Areas/{2}/%1Views//Shared/{0}.vbhtml"
    };

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

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

    private string[] _newViewLocations = new string[] {
        "~/%1Views/{1}/{0}.cshtml",
        "~/%1Views/{1}/{0}.vbhtml",
        "~/%1Views/Shared/{0}.cshtml",
        "~/%1Views/Shared/{0}.vbhtml"
    };

    private string[] _newMasterLocations = new string[] {
        "~/%1Views/{1}/{0}.cshtml",
        "~/%1Views/{1}/{0}.vbhtml",
        "~/%1Views/Shared/{0}.cshtml",
        "~/%1Views/Shared/{0}.vbhtml"
    };

    private string[] _newPartialViewLocations = new string[] {
        "~/%1Views/{1}/{0}.cshtml",
        "~/%1Views/{1}/{0}.vbhtml",
        "~/%1Views/Shared/{0}.cshtml",
        "~/%1Views/Shared/{0}.vbhtml"
    };

    public Travel2ViewEngine()
        : base()
    {
        Enum.TryParse<BrandNameEnum>(Travel2.WebUI.Properties.Settings.Default.BrandName, out BrandName);

        AreaViewLocationFormats = AppendLocationFormats(_newAreaViewLocations, AreaViewLocationFormats);

        AreaMasterLocationFormats = AppendLocationFormats(_newAreaMasterLocations, AreaMasterLocationFormats);

        AreaPartialViewLocationFormats = AppendLocationFormats(_newAreaPartialViewLocations, AreaPartialViewLocationFormats);

        ViewLocationFormats = AppendLocationFormats(_newViewLocations, ViewLocationFormats);

        MasterLocationFormats = AppendLocationFormats(_newMasterLocations, MasterLocationFormats);

        PartialViewLocationFormats = AppendLocationFormats(_newPartialViewLocations, PartialViewLocationFormats);
    }

    private string[] AppendLocationFormats(string[] newLocations, string[] defaultLocations)
    {
        List<string> viewLocations = new List<string>();
        viewLocations.AddRange(newLocations);
        viewLocations.AddRange(defaultLocations);
        return viewLocations.ToArray();
    }

    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
    {
        return base.CreateView(controllerContext, viewPath.Replace("%1", BrandName.ToString()), masterPath);
    }

    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
    {
        return base.CreatePartialView(controllerContext, partialPath.Replace("%1", BrandName.ToString()));
    }

    protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
    {
        return base.FileExists(controllerContext, virtualPath.Replace("%1", BrandName.ToString()));
    }
}

then register in Gloabal.asax

然后在Gloabal.asax注册

protected void Application_Start(object sender, EventArgs e)
{
    RegisterRoutes(RouteTable.Routes);


    //Register our customer view engine to control T2 and TBag views and over ridding
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new Travel2ViewEngine());
}

#2


7  

Yes that's the way but you do not need to override that methods. RazorViewEngine inherits from VirtualPathProviderViewEngine so you can use its properties to set the location of your views.

是的,这是方式,但您不需要覆盖这些方法。 RazorViewEngine继承自VirtualPathProviderViewEngine,因此您可以使用其属性来设置视图的位置。

For an example read Creating your first MVC ViewEngine and How to set a Default Route (To an Area) in MVC.

有关示例,请参阅创建第一个MVC ViewEngine以及如何在MVC中设置默认路由(到某个区域)。

#3


1  

Here's my answer: Add this to your global.ascx

这是我的答案:将其添加到您的global.ascx

        ViewEngines.Engines.Clear();
        var customEngine = new RazorViewEngine();
        customEngine.PartialViewLocationFormats = new string[]
        {
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml",
            "~/Views/Partial/{0}.cshtml",
            "~/Views/Partial/{1}/{0}.cshtml"
        };



        customEngine.ViewLocationFormats = new string[]
        {
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml",
            "~/Views/Controller/{1}/{0}.cshtml"
        };

        customEngine.MasterLocationFormats = new string[]
        {
            "~/Views/Shared/{0}.cshtml",
            "~/Views/Layout/{0}.cshtml"
        };

        ViewEngines.Engines.Add(customEngine);

those are the folders where razor checks your views.

这些是剃刀检查你的观点的文件夹。

Let me know if this works.

让我知道这个是否奏效。

#1


15  

Ok in the end I opted for an approach detailed here: http://weblogs.asp.net/imranbaloch/archive/2011/06/27/view-engine-with-dynamic-view-location.aspx

好的,最后我选择了一个详细的方法:http://weblogs.asp.net/imranbaloch/archive/2011/06/27/view-engine-with-dynamic-view-location.aspx

thanks to @Adriano for the answers and pointers but in the end I think this approach fits my needs better. The approach below allows me to keep the standard functionality but to create a new higher priority view location to be searched.

感谢@Adriano的答案和指示,但最后我认为这种方法更符合我的需求。下面的方法允许我保留标准功能,但是要创建一个新的更高优先级的视图位置进行搜索。

public class Travel2ViewEngine : RazorViewEngine
{
    protected BrandNameEnum BrandName;
    private string[] _newAreaViewLocations = new string[] {
        "~/Areas/{2}/%1Views/{1}/{0}.cshtml",
        "~/Areas/{2}/%1Views/{1}/{0}.vbhtml",
        "~/Areas/{2}/%1Views//Shared/{0}.cshtml",
        "~/Areas/{2}/%1Views//Shared/{0}.vbhtml"
    };

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

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

    private string[] _newViewLocations = new string[] {
        "~/%1Views/{1}/{0}.cshtml",
        "~/%1Views/{1}/{0}.vbhtml",
        "~/%1Views/Shared/{0}.cshtml",
        "~/%1Views/Shared/{0}.vbhtml"
    };

    private string[] _newMasterLocations = new string[] {
        "~/%1Views/{1}/{0}.cshtml",
        "~/%1Views/{1}/{0}.vbhtml",
        "~/%1Views/Shared/{0}.cshtml",
        "~/%1Views/Shared/{0}.vbhtml"
    };

    private string[] _newPartialViewLocations = new string[] {
        "~/%1Views/{1}/{0}.cshtml",
        "~/%1Views/{1}/{0}.vbhtml",
        "~/%1Views/Shared/{0}.cshtml",
        "~/%1Views/Shared/{0}.vbhtml"
    };

    public Travel2ViewEngine()
        : base()
    {
        Enum.TryParse<BrandNameEnum>(Travel2.WebUI.Properties.Settings.Default.BrandName, out BrandName);

        AreaViewLocationFormats = AppendLocationFormats(_newAreaViewLocations, AreaViewLocationFormats);

        AreaMasterLocationFormats = AppendLocationFormats(_newAreaMasterLocations, AreaMasterLocationFormats);

        AreaPartialViewLocationFormats = AppendLocationFormats(_newAreaPartialViewLocations, AreaPartialViewLocationFormats);

        ViewLocationFormats = AppendLocationFormats(_newViewLocations, ViewLocationFormats);

        MasterLocationFormats = AppendLocationFormats(_newMasterLocations, MasterLocationFormats);

        PartialViewLocationFormats = AppendLocationFormats(_newPartialViewLocations, PartialViewLocationFormats);
    }

    private string[] AppendLocationFormats(string[] newLocations, string[] defaultLocations)
    {
        List<string> viewLocations = new List<string>();
        viewLocations.AddRange(newLocations);
        viewLocations.AddRange(defaultLocations);
        return viewLocations.ToArray();
    }

    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
    {
        return base.CreateView(controllerContext, viewPath.Replace("%1", BrandName.ToString()), masterPath);
    }

    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
    {
        return base.CreatePartialView(controllerContext, partialPath.Replace("%1", BrandName.ToString()));
    }

    protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
    {
        return base.FileExists(controllerContext, virtualPath.Replace("%1", BrandName.ToString()));
    }
}

then register in Gloabal.asax

然后在Gloabal.asax注册

protected void Application_Start(object sender, EventArgs e)
{
    RegisterRoutes(RouteTable.Routes);


    //Register our customer view engine to control T2 and TBag views and over ridding
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new Travel2ViewEngine());
}

#2


7  

Yes that's the way but you do not need to override that methods. RazorViewEngine inherits from VirtualPathProviderViewEngine so you can use its properties to set the location of your views.

是的,这是方式,但您不需要覆盖这些方法。 RazorViewEngine继承自VirtualPathProviderViewEngine,因此您可以使用其属性来设置视图的位置。

For an example read Creating your first MVC ViewEngine and How to set a Default Route (To an Area) in MVC.

有关示例,请参阅创建第一个MVC ViewEngine以及如何在MVC中设置默认路由(到某个区域)。

#3


1  

Here's my answer: Add this to your global.ascx

这是我的答案:将其添加到您的global.ascx

        ViewEngines.Engines.Clear();
        var customEngine = new RazorViewEngine();
        customEngine.PartialViewLocationFormats = new string[]
        {
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml",
            "~/Views/Partial/{0}.cshtml",
            "~/Views/Partial/{1}/{0}.cshtml"
        };



        customEngine.ViewLocationFormats = new string[]
        {
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml",
            "~/Views/Controller/{1}/{0}.cshtml"
        };

        customEngine.MasterLocationFormats = new string[]
        {
            "~/Views/Shared/{0}.cshtml",
            "~/Views/Layout/{0}.cshtml"
        };

        ViewEngines.Engines.Add(customEngine);

those are the folders where razor checks your views.

这些是剃刀检查你的观点的文件夹。

Let me know if this works.

让我知道这个是否奏效。