Esiste una vista in ASP.NET MVC?


95

È possibile determinare se esiste un nome di vista specifico dall'interno di un controller prima di renderizzare la vista?

Ho la necessità di determinare dinamicamente il nome della vista di cui eseguire il rendering. Se esiste una vista con quel nome, devo renderizzare quella vista. Se non è presente una vista con il nome personalizzato, è necessario eseguire il rendering di una vista predefinita.

Vorrei fare qualcosa di simile al seguente codice all'interno del mio controller:

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

14
Solo leggendo il titolo di questo, sembra una questione filosofica molto profonda.

Risposte:


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

Per chi cerca un metodo di estensione copia / incolla:

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

2
Probabilmente è meglio. Non sapevo che ci fosse un metodo FindView fuori dalla raccolta ViewEngines stessa.
Lance Harper

1
Ma come verificare se la vista esiste per un altro controller?
SOReader

A parte: uno dei nostri ingegneri (da quando è andato avanti) ha costruito un motore di visualizzazione personalizzato (chiamato MultiTenantViewEngine, in modo da avere un'idea del suo scopo) che implementa FindView per lanciare una HttpException (404) se non riesce a trovare il dato Visualizza. È una buona pratica? Non ne ho idea. Ma non sarei sorpreso se ci fossero altre implementazioni là fuori come quella. Poiché non conoscerai il funzionamento interno del motore di visualizzazione durante l'esecuzione di questo codice, potresti voler lanciare un catch {return false; } attorno a questo cucciolo, solo per essere al sicuro.
Brian Colavito

1
@SOReader, ho provato hvnt ma, controller IController = new HomeController (); e quindi controller.ControllerContext darà la cosa che puoi passare ai metodi findview.
Vishal Sharma

Grazie per questa risposta. Mi ha aiutato in un altro problema. Avevo bisogno di controllare se la mia vista è parziale o meno e poiché il nome di tutti i miei parziali inizia con la sottolineatura ora posso lavorare con la mia soluzione controllando se "result.View! = Null"
Deise Vicentin

19

Che ne dici di provare qualcosa di simile al seguente assumendo che tu stia utilizzando un solo motore di visualizzazione:

bool viewExists = ViewEngines.Engines[0].FindView(ControllerContext, "ViewName", "MasterName", false) != null;

sembra che questo sia stato pubblicato 3 minuti prima della risposta accettata e tuttavia nessun amore ?! +1 da me.
Trevor de Koekkoek

@TrevordeKoekkoek ... hmmm ... + 1
Vishal Sharma

8

Ecco un altro modo [non necessariamente consigliato] per farlo

 try
 {
     @Html.Partial("Category/SearchPanel/" + Model.CategoryKey)
 }
 catch (InvalidOperationException) { }

questo serve per testare l'esistenza di una vista parziale all'interno di un file .cshtml. non è davvero una risposta a questa domanda, ma un'altra domanda che collega qui è stata chiusa in modo errato, quindi lascio la mia risposta qui
Simon_Weaver

2
Questo è stato effettivamente perfetto per il mio uso, dal momento che stavo cercando un modo per utilizzare una visione parziale specifica della cultura. Quindi l'ho chiamato semplicemente con il nome della vista specifico della cultura, quindi ho chiamato la vista predefinita all'interno del fermo. E lo stavo facendo in una funzione di utilità, quindi non avevo accesso a ControllerContextquanto richiesto dal FindViewmetodo.
timore reverenziale

2

Se si desidera riutilizzarlo per più azioni del controller, basandosi sulla soluzione fornita da Dave, è possibile definire un risultato di visualizzazione personalizzato come segue:

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

    ...
}

Quindi nella tua azione restituisci semplicemente un'istanza della tua visualizzazione personalizzata:

public ActionResult Index()
{ 
    return new CustomViewResult();
}

1
ViewEngines.Engines.FindView(ViewContext.Controller.ControllerContext, "View Name").View != null

I miei 2 centesimi.


1

In asp.net core 2.x la ViewEnginesproprietà non esiste più, quindi dobbiamo utilizzare il ICompositeViewEngineservizio. Questa è una variante della risposta accettata utilizzando l'inserimento delle dipendenze:

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

Per i curiosi: l'interfaccia di base IViewEnginenon è registrata come servizio quindi dobbiamo ICompositeViewEngineinvece iniettarla . Il FindView()metodo tuttavia è fornito da IViewEnginecosì la variabile membro può usare l'interfaccia di base.


0

Ecco come farlo in Razor for Core 2.2, ecc. Nota che la chiamata è "GetView", non "Find View)

@using Microsoft.AspNetCore.Mvc.ViewEngines
@inject ICompositeViewEngine Engine
...
@if (Engine.GetView(scriptName, scriptName, isMainPage: false).Success) 
{
    @await Html.PartialAsync(scriptName)
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.