Puis-je spécifier un emplacement personnalisé pour & # 8220; rechercher des vues & # 8221; dans ASP.NET MVC?

StackOverflow https://stackoverflow.com/questions/632964

  •  08-07-2019
  •  | 
  •  

Question

J'ai la disposition suivante pour mon projet MVC:

  • / Contrôleurs
    • / démo
    • / Demo / DemoArea1Controller
    • / Demo / DemoArea2Controller
    • etc ...
  • / Vues
    • / démo
    • /Demo/DemoArea1/Index.aspx
    • /Demo/DemoArea2/Index.aspx

Cependant, lorsque j'ai ceci pour DemoArea1Controller :

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

Je reçois le " La vue 'index' ou son maître n'a pas pu être trouvé " erreur, avec les emplacements de recherche habituels.

Comment puis-je spécifier que les contrôleurs de la "Démo"? recherche dans l’espace de nommage dans la section "Démo". voir le sous-dossier?

Était-ce utile?

La solution

Vous pouvez facilement étendre WebFormViewEngine pour spécifier tous les emplacements sur lesquels vous souhaitez rechercher:

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;
    }
}

Assurez-vous de bien enregistrer le moteur de vue en modifiant la méthode Application_Start dans votre fichier Global.asax.cs

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

Autres conseils

Désormais, dans MVC 6, vous pouvez implémenter l'interface IViewLocationExpander sans déranger les moteurs de vue:

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
    }
}

{0} est le nom de la vue cible, {1} - nom du contrôleur et {2} - nom de la zone.

Vous pouvez retourner votre propre liste d'emplacements, la fusionner avec viewLocations ( .Union (viewLocations) par défaut) ou simplement les modifier ( viewLocations.Select ( path = > " AnotherPath " + chemin) ).

Pour enregistrer votre expandeur d'emplacement d'affichage personnalisé dans MVC, ajoutez les lignes suivantes à la méthode ConfigureServices dans le fichier Startup.cs :

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

Il existe en réalité une méthode beaucoup plus simple que de coder en dur les chemins dans votre constructeur. Vous trouverez ci-dessous un exemple d'extension du moteur Razor afin d'ajouter de nouveaux chemins. Une chose sur laquelle je ne suis pas tout à fait sûr est de savoir si les chemins que vous ajoutez ici seront mis en cache:

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();
    }
}

Et votre 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);
}

Une chose à noter: votre emplacement personnalisé aura besoin du fichier ViewStart.cshtml à la racine.

Si vous souhaitez simplement ajouter de nouveaux chemins, vous pouvez ajouter des moteurs de vue par défaut et économiser quelques lignes de 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);

Il en va de même pour WebFormEngine

Au lieu de sous-classer RazorViewEngine ou de le remplacer directement, vous pouvez simplement modifier la propriété existante de RazorViewEngine, PartialViewLocationFormats. Ce code va dans 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();
}

Dernière fois que j'ai vérifié, cela nécessite que vous construisiez votre propre ViewEngine. Je ne sais pas s'ils ont rendu les choses plus faciles avec RC1.

L’approche de base que j’avais utilisée avant la première télécommande a consisté, dans mon propre ViewEngine, à diviser l’espace de noms du contrôleur et à rechercher les dossiers qui correspondent aux pièces.

EDIT:

Nous sommes revenus et avons trouvé le code. Voici l’idée générale.

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;
}

Essayez quelque chose comme ça:

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);
}

Remarque: pour ASP.NET MVC 2, ils ont des chemins d’emplacement supplémentaires que vous devrez définir pour les vues dans "Zones".

 AreaViewLocationFormats
 AreaPartialViewLocationFormats
 AreaMasterLocationFormats

La création d'un moteur de vue pour une zone est décrite sur le site de Phil blog .

Remarque: il s’agit d’un aperçu de la version 1 et est donc sujet à modification.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top