Existiert eine Ansicht in ASP.NET MVC?

95

Kann vor dem Rendern der Ansicht festgestellt werden, ob in einem Controller ein bestimmter Ansichtsname vorhanden ist?

Ich muss den Namen der zu rendernden Ansicht dynamisch bestimmen. Wenn eine Ansicht mit diesem Namen vorhanden ist, muss ich diese Ansicht rendern. Wenn es keine Ansicht mit dem benutzerdefinierten Namen gibt, muss ich eine Standardansicht rendern.

Ich möchte etwas Ähnliches wie den folgenden Code in meinem Controller tun:

public ActionResult Index()
{
    var name = SomeMethodToGetViewName();

    // The 'ViewExists' method is what I've been unable to find.
    if (ViewExists(name))
    {
        retun View(name);
    }
    else
    {
        return View();
    }
}
Andrew Hanson
quelle
14
Nur den Titel zu lesen, scheint eine sehr tiefe philosophische Frage zu sein.

Antworten:

154
 private bool ViewExists(string name)
 {
     ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, name, null);
     return (result.View != null);
 }

Für diejenigen, die eine Erweiterungsmethode zum Kopieren / Einfügen suchen:

public static class ControllerExtensions
{
    public static bool ViewExists(this Controller controller, string name)
    {
        ViewEngineResult result = ViewEngines.Engines.FindView(controller.ControllerContext, name, null);
        return (result.View != null);
    }
}
Dave Cluderay
quelle
2
Das ist wahrscheinlich besser. Mir war nicht bekannt, dass es eine FindView-Methode aus der ViewEngines-Auflistung selbst gibt.
Lance Harper
1
Aber wie kann man überprüfen, ob die Ansicht für einen anderen Controller vorhanden ist?
SOReader
Nebenbei bemerkt: Einer unserer Ingenieure (seitdem) hat eine benutzerdefinierte Ansichts-Engine (MultiTenantViewEngine) erstellt, die FindView implementiert, um eine HttpException (404) auszulösen, wenn die angegebene nicht gefunden werden kann Aussicht. Ist das eine gute Praxis? Ich habe keine Ahnung. Aber wäre nicht überrascht, wenn es andere Implementierungen wie diese gibt. Da Sie das Innenleben der View Engine bei der Ausführung dieses Codes nicht kennen, möchten Sie möglicherweise einen catch {return false; } um diesen Welpen herum, nur um sicher zu gehen.
Brian Colavito
1
@SOReader, ich habe aber nicht getestet, IController controller = new HomeController (); und dann wird controller.ControllerContext das Ding geben, das Sie an findview-Methoden übergeben können.
Vishal Sharma
Danke für diese Antwort. Es hat mir bei einem anderen Problem geholfen. Ich musste überprüfen, ob meine Ansicht partiell ist oder nicht, und da der Name aller Partials jetzt mit Unterstreichung beginnt, kann ich mit meiner Lösung prüfen, ob "result.View! = Null"
Deise Vicentin
19

Wie wäre es, wenn Sie Folgendes ausprobieren, vorausgesetzt, Sie verwenden nur eine Ansichts-Engine:

bool viewExists = ViewEngines.Engines[0].FindView(ControllerContext, "ViewName", "MasterName", false) != null;
Lance Harper
quelle
sieht so aus, als ob dieser 3 Minuten vor der akzeptierten Antwort gepostet wurde und doch keine Liebe?! +1 von mir.
Trevor de Koekkoek
@ TrevorvorKoekkoek ... hmmm ... + 1
Vishal Sharma
8

Hier ist eine andere [nicht unbedingt empfohlene] Möglichkeit, dies zu tun

 try
 {
     @Html.Partial("Category/SearchPanel/" + Model.CategoryKey)
 }
 catch (InvalidOperationException) { }
Simon_Weaver
quelle
Dies dient zum Testen der Existenz einer Teilansicht in einer CSHTML-Datei. Es ist nicht wirklich eine Antwort auf diese Frage, aber eine andere Frage, die hier verlinkt ist, wurde falsch geschlossen, so dass ich meine Antwort hier
lasse
2
Dies war genau das Richtige für mich, da ich nach einer Möglichkeit suchte, eine kulturspezifische Teilansicht zu verwenden. Also habe ich dies nur mit dem kulturspezifischen Ansichtsnamen aufgerufen und dann die Standardansicht innerhalb des Catch aufgerufen. Und ich tat dies in einer Utility-Funktion, so dass ich keinen Zugriff auf die hatte, ControllerContextwie es die FindViewMethode benötigt.
Ehrfurcht
2

Wenn Sie dies auf der Grundlage der von Dave angegebenen Lösung für mehrere Controller-Aktionen wiederverwenden möchten, können Sie ein benutzerdefiniertes Ansichtsergebnis wie folgt definieren:

public class CustomViewResult : ViewResult
{
    protected override ViewEngineResult FindView(ControllerContext context)
    {
        string name = SomeMethodToGetViewName();

        ViewEngineResult result = ViewEngines.Engines.FindView(context, name, null);

        if (result.View != null)
        {
            return result;
        }

        return base.FindView(context);
    }

    ...
}

Geben Sie dann in Ihrer Aktion einfach eine Instanz Ihrer benutzerdefinierten Ansicht zurück:

public ActionResult Index()
{ 
    return new CustomViewResult();
}
DSO
quelle
1
ViewEngines.Engines.FindView(ViewContext.Controller.ControllerContext, "View Name").View != null

Meine 2 Cent.

Tynar
quelle
1

In asp.net core 2.x ist die ViewEnginesEigenschaft nicht mehr vorhanden, daher müssen wir den ICompositeViewEngineDienst verwenden. Dies ist eine Variante der akzeptierten Antwort mit Abhängigkeitsinjektion:

public class DemoController : Controller
{
    private readonly IViewEngine _viewEngine;

    public DemoController(ICompositeViewEngine viewEngine)
    {
        _viewEngine = viewEngine;
    }

    private bool ViewExists(string name)
    {
        ViewEngineResult viewEngineResult = _viewEngine.FindView(ControllerContext, name, true);
        return viewEngineResult?.View != null;
    }

    public ActionResult Index() ...
}

Für Neugierige: Die Basisschnittstelle IViewEngineist nicht als Dienst registriert, daher müssen wir ICompositeViewEnginestattdessen injizieren . Die FindView()Methode wird jedoch bereitgestellt, IViewEnginedamit die Mitgliedsvariable die Basisschnittstelle verwenden kann.

idilov
quelle
0

Hier erfahren Sie, wie Sie dies in Razor für Core 2.2 usw. tun. Beachten Sie, dass der Aufruf "GetView" und nicht "Find View" lautet.

@using Microsoft.AspNetCore.Mvc.ViewEngines
@inject ICompositeViewEngine Engine
...
@if (Engine.GetView(scriptName, scriptName, isMainPage: false).Success) 
{
    @await Html.PartialAsync(scriptName)
}
philw
quelle