asp.net-mvc 我可以在 ASP.NET MVC 中指定一个自定义位置来“搜索视图”吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/632964/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-07 23:24:38  来源:igfitidea点击:

Can I specify a custom location to "search for views" in ASP.NET MVC?

asp.net-mvcviews

提问by Daniel Schaffer

I have the following layout for my mvc project:

我的 mvc 项目具有以下布局:

  • /Controllers
    • /Demo
    • /Demo/DemoArea1Controller
    • /Demo/DemoArea2Controller
    • etc...
  • /Views
    • /Demo
    • /Demo/DemoArea1/Index.aspx
    • /Demo/DemoArea2/Index.aspx
  • /控制器
    • /演示
    • /Demo/DemoArea1Controller
    • /Demo/DemoArea2Controller
    • 等等...
  • /浏览次数
    • /演示
    • /Demo/DemoArea1/Index.aspx
    • /Demo/DemoArea2/Index.aspx

However, when I have this for DemoArea1Controller:

但是,当我有这个时DemoArea1Controller

public class DemoArea1Controller : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

I get the "The view 'index' or its master could not be found" error, with the usual search locations.

我收到“无法找到视图‘索引’或其主文件”错误,以及通常的搜索位置。

How can I specify that controllers in the "Demo" namespace search in the "Demo" view subfolder?

如何在“Demo”视图子文件夹中的“Demo”命名空间搜索中指定控制器?

回答by Sam Wessel

You can easily extend the WebFormViewEngine to specify all the locations you want to look in:

您可以轻松扩展 WebFormViewEngine 以指定要查看的所有位置:

public class CustomViewEngine : WebFormViewEngine
{
    public CustomViewEngine()
    {
        var viewLocations =  new[] {  
            "~/Views/{1}/{0}.aspx",  
            "~/Views/{1}/{0}.ascx",  
            "~/Views/Shared/{0}.aspx",  
            "~/Views/Shared/{0}.ascx",  
            "~/AnotherPath/Views/{0}.ascx"
            // etc
        };

        this.PartialViewLocationFormats = viewLocations;
        this.ViewLocationFormats = viewLocations;
    }
}

Make sure you remember to register the view engine by modifying the Application_Start method in your Global.asax.cs

确保您记得通过修改 Global.asax.cs 中的 Application_Start 方法来注册视图引擎

protected void Application_Start()
{
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new CustomViewEngine());
}

回答by whyleee

Now in MVC 6 you can implement IViewLocationExpanderinterface without messing around with view engines:

现在在 MVC 6 中,您可以实现IViewLocationExpander接口而无需处理视图引擎:

public class MyViewLocationExpander : IViewLocationExpander
{
    public void PopulateValues(ViewLocationExpanderContext context) {}

    public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
    {
        return new[]
        {
            "/AnotherPath/Views/{1}/{0}.cshtml",
            "/AnotherPath/Views/Shared/{0}.cshtml"
        }; // add `.Union(viewLocations)` to add default locations
    }
}

where {0}is target view name, {1}- controller name and {2}- area name.

其中{0}是目标视图名称、{1}-控制器名称和{2}-区域名称。

You can return your own list of locations, merge it with default viewLocations(.Union(viewLocations)) or just change them (viewLocations.Select(path => "/AnotherPath" + path)).

您可以返回自己的位置列表,将其与默认viewLocations( .Union(viewLocations))合并,或者只是更改它们 ( viewLocations.Select(path => "/AnotherPath" + path))。

To register your custom view location expander in MVC, add next lines to ConfigureServicesmethod in Startup.csfile:

要在 MVC 中注册您的自定义视图位置扩展器,请将下一行添加到文件中的ConfigureServices方法Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.ViewLocationExpanders.Add(new MyViewLocationExpander());
    });
}

回答by Chris S

There's actually a lot easier method than hardcoding the paths into your constructor. Below is an example of extending the Razor engine to add new paths. One thing I'm not entirely sure about is whether the paths you add here will be cached:

实际上有比将路径硬编码到构造函数中更简单的方法。下面是扩展 Razor 引擎以添加新路径的示例。我不完全确定的一件事是您在此处添加的路径是否会被缓存:

public class ExtendedRazorViewEngine : RazorViewEngine
{
    public void AddViewLocationFormat(string paths)
    {
        List<string> existingPaths = new List<string>(ViewLocationFormats);
        existingPaths.Add(paths);

        ViewLocationFormats = existingPaths.ToArray();
    }

    public void AddPartialViewLocationFormat(string paths)
    {
        List<string> existingPaths = new List<string>(PartialViewLocationFormats);
        existingPaths.Add(paths);

        PartialViewLocationFormats = existingPaths.ToArray();
    }
}

And your Global.asax.cs

还有你的 Global.asax.cs

protected void Application_Start()
{
    ViewEngines.Engines.Clear();

    ExtendedRazorViewEngine engine = new ExtendedRazorViewEngine();
    engine.AddViewLocationFormat("~/MyThemes/{1}/{0}.cshtml");
    engine.AddViewLocationFormat("~/MyThemes/{1}/{0}.vbhtml");

    // Add a shared location too, as the lines above are controller specific
    engine.AddPartialViewLocationFormat("~/MyThemes/{0}.cshtml");
    engine.AddPartialViewLocationFormat("~/MyThemes/{0}.vbhtml");

    ViewEngines.Engines.Add(engine);

    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
}

One thing to note: your custom location will need the ViewStart.cshtml file in its root.

需要注意的一件事:您的自定义位置将需要其根目录中的 ViewStart.cshtml 文件。

回答by Marcelo De Zen

If you want just add new paths, you can add to the default view engines and spare some lines of code:

如果您只想添加新路径,您可以添加到默认视图引擎并节省一些代码行:

ViewEngines.Engines.Clear();
var razorEngine = new RazorViewEngine();
razorEngine.MasterLocationFormats = razorEngine.MasterLocationFormats
      .Concat(new[] { 
          "~/custom/path/{0}.cshtml" 
      }).ToArray();

razorEngine.PartialViewLocationFormats = razorEngine.PartialViewLocationFormats
      .Concat(new[] { 
          "~/custom/path/{1}/{0}.cshtml",   // {1} = controller name
          "~/custom/path/Shared/{0}.cshtml" 
      }).ToArray();

ViewEngines.Engines.Add(razorEngine);

The same applies to WebFormEngine

这同样适用于 WebFormEngine

回答by Simon Giles

Instead of subclassing the RazorViewEngine, or replacing it outright, you can just alter existing RazorViewEngine's PartialViewLocationFormats property. This code goes in Application_Start:

您可以更改现有 RazorViewEngine 的 PartialViewLocationFormats 属性,而不是继承 RazorViewEngine 或直接替换它。此代码位于 Application_Start 中:

System.Web.Mvc.RazorViewEngine rve = (RazorViewEngine)ViewEngines.Engines
  .Where(e=>e.GetType()==typeof(RazorViewEngine))
  .FirstOrDefault();

string[] additionalPartialViewLocations = new[] { 
  "~/Views/[YourCustomPathHere]"
};

if(rve!=null)
{
  rve.PartialViewLocationFormats = rve.PartialViewLocationFormats
    .Union( additionalPartialViewLocations )
    .ToArray();
}

回答by Joel

Last I checked, this requires you to build your own ViewEngine. I don't know if they made it easier in RC1 though.

最后我检查过,这需要您构建自己的 ViewEngine。我不知道他们是否在 RC1 中让它变得更容易。

The basic approach I used before the first RC was, in my own ViewEngine, to split the namespace of the controller and look for folders which matched the parts.

我在第一个 RC 之前使用的基本方法是,在我自己的 ViewEngine 中,拆分控制器的命名空间并查找与部件匹配的文件夹。

EDIT:

编辑:

Went back and found the code. Here's the general idea.

回去发现了密码。这是一般的想法。

public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName)
{
    string ns = controllerContext.Controller.GetType().Namespace;
    string controller = controllerContext.Controller.GetType().Name.Replace("Controller", "");

    //try to find the view
    string rel = "~/Views/" +
        (
            ns == baseControllerNamespace ? "" :
            ns.Substring(baseControllerNamespace.Length + 1).Replace(".", "/") + "/"
        )
        + controller;
    string[] pathsToSearch = new string[]{
        rel+"/"+viewName+".aspx",
        rel+"/"+viewName+".ascx"
    };

    string viewPath = null;
    foreach (var path in pathsToSearch)
    {
        if (this.VirtualPathProvider.FileExists(path))
        {
            viewPath = path;
            break;
        }
    }

    if (viewPath != null)
    {
        string masterPath = null;

        //try find the master
        if (!string.IsNullOrEmpty(masterName))
        {

            string[] masterPathsToSearch = new string[]{
                rel+"/"+masterName+".master",
                "~/Views/"+ controller +"/"+ masterName+".master",
                "~/Views/Shared/"+ masterName+".master"
            };


            foreach (var path in masterPathsToSearch)
            {
                if (this.VirtualPathProvider.FileExists(path))
                {
                    masterPath = path;
                    break;
                }
            }
        }

        if (string.IsNullOrEmpty(masterName) || masterPath != null)
        {
            return new ViewEngineResult(
                this.CreateView(controllerContext, viewPath, masterPath), this);
        }
    }

    //try default implementation
    var result = base.FindView(controllerContext, viewName, masterName);
    if (result.View == null)
    {
        //add the location searched
        return new ViewEngineResult(pathsToSearch);
    }
    return result;
}

回答by Vitaliy Ulantikov

Try something like this:

尝试这样的事情:

private static void RegisterViewEngines(ICollection<IViewEngine> engines)
{
    engines.Add(new WebFormViewEngine
    {
        MasterLocationFormats = new[] {"~/App/Views/Admin/{0}.master"},
        PartialViewLocationFormats = new[] {"~/App/Views/Admin//{1}/{0}.ascx"},
        ViewLocationFormats = new[] {"~/App/Views/Admin//{1}/{0}.aspx"}
    });
}

protected void Application_Start()
{
    RegisterViewEngines(ViewEngines.Engines);
}

回答by Simon_Weaver

Note: for ASP.NET MVC 2 they have additional location paths you will need to set for views in 'Areas'.

注意:对于 ASP.NET MVC 2,您需要为“区域”中的视图设置额外的位置路径。

 AreaViewLocationFormats
 AreaPartialViewLocationFormats
 AreaMasterLocationFormats

Creating a view engine for an Area is described on Phil's blog.

Phil 的博客中描述了为区域创建视图引擎。

Note: This is for preview release 1 so is subject to change.

注意:这是预览版 1,因此可能会发生变化。

回答by Hooman Bahreini

Most of the answers here, clear the existing locationsby calling ViewEngines.Engines.Clear()and then add them back in again... there is no need to do this.

这里的大多数答案,通过调用清除现有位置ViewEngines.Engines.Clear(),然后再次添加它们......没有必要这样做。

We can simply add the new locations to the existing ones, as shown below:

我们可以简单地将新位置添加到现有位置,如下所示:

// note that the base class is RazorViewEngine, NOT WebFormViewEngine
public class ExpandedViewEngine : RazorViewEngine
{
    public ExpandedViewEngine()
    {
        var customViewSubfolders = new[] 
        {
            // {1} is conroller name, {0} is action name
            "~/Areas/AreaName/Views/Subfolder1/{1}/{0}.cshtml",
            "~/Areas/AreaName/Views/Subfolder1/Shared/{0}.cshtml"
        };

        var customPartialViewSubfolders = new[] 
        {
            "~/Areas/MyAreaName/Views/Subfolder1/{1}/Partials/{0}.cshtml",
            "~/Areas/MyAreaName/Views/Subfolder1/Shared/Partials/{0}.cshtml"
        };

        ViewLocationFormats = ViewLocationFormats.Union(customViewSubfolders).ToArray();
        PartialViewLocationFormats = PartialViewLocationFormats.Union(customPartialViewSubfolders).ToArray();

        // use the following if you want to extend the master locations
        // MasterLocationFormats = MasterLocationFormats.Union(new[] { "new master location" }).ToArray();   
    }
}

Now you can configure your project to use the above RazorViewEnginein Global.asax:

现在您可以配置您的项目以RazorViewEngine在 Global.asax 中使用上述内容:

protected void Application_Start()
{
    ViewEngines.Engines.Add(new ExpandedViewEngine());
    // more configurations
}

See this tutoralfor more info.

有关更多信息,请参阅本教程