Apakah tampilan ada di ASP.NET MVC?

95

Apakah mungkin untuk menentukan apakah nama tampilan tertentu ada dari dalam pengontrol sebelum merender tampilan?

Saya memiliki persyaratan untuk secara dinamis menentukan nama tampilan yang akan dirender. Jika tampilan ada dengan nama itu maka saya perlu membuat tampilan itu. Jika tidak ada tampilan dengan nama kustom, saya perlu membuat tampilan default.

Saya ingin melakukan sesuatu yang mirip dengan kode berikut dalam pengontrol saya:

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
sumber
14
Hanya membaca judul ini, sepertinya pertanyaan filosofis yang sangat dalam.

Jawaban:

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

Bagi mereka yang mencari metode ekstensi salin / tempel:

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
sumber
2
Ini mungkin lebih baik. Saya tidak menyadari ada metode FindView dari koleksi ViewEngines itu sendiri.
Lance Harper
1
Tetapi bagaimana cara memeriksa apakah tampilan ada untuk pengontrol lain?
SOReader
Sedikit tambahan: salah satu teknisi kami (sejak pindah) membangun mesin tampilan khusus (disebut MultiTenantViewEngine, sehingga Anda memahami tujuannya) yang mengimplementasikan FindView untuk menampilkan HttpException (404) jika tidak dapat menemukan yang diberikan melihat. Apakah ini praktik yang baik? Saya tidak punya ide. Tetapi tidak heran jika ada implementasi lain di luar sana seperti itu. Karena Anda tidak akan mengetahui cara kerja mesin tampilan saat kode ini dijalankan, Anda mungkin ingin melempar catch {return false; } di sekitar anak anjing ini, agar aman.
Brian Colavito
1
@SOReader, i hvnt diuji tetapi, IController controller = new HomeController (); dan kemudian controller.ControllerContext akan memberikan hal yang dapat Anda berikan ke metode findview.
Vishal Sharma
Terima kasih atas jawaban ini. Ini membantu saya dalam masalah lain. Saya perlu memeriksa apakah pandangan saya parsial atau tidak dan karena semua nama parsial saya dimulai dengan garis bawah sekarang saya dapat bekerja dengan solusi saya memeriksa apakah "result.View! = Null"
Deise Vicentin
19

Bagaimana jika mencoba sesuatu seperti berikut dengan asumsi Anda hanya menggunakan satu mesin tampilan:

bool viewExists = ViewEngines.Engines[0].FindView(ControllerContext, "ViewName", "MasterName", false) != null;
Lance Harper
sumber
Sepertinya yang ini diposting 3 menit sebelum jawaban diterima namun tidak ada cinta ?! +1 dari saya.
Trevor de Koekkoek
@TrevordeKoekkoek ... hmmm ... + 1
Vishal Sharma
8

Berikut cara lain [tidak selalu direkomendasikan] untuk melakukannya

 try
 {
     @Html.Partial("Category/SearchPanel/" + Model.CategoryKey)
 }
 catch (InvalidOperationException) { }
Simon_Weaver
sumber
ini untuk menguji keberadaan tampilan parsial dalam file .cshtml. ini sebenarnya bukan jawaban untuk pertanyaan ini, tetapi pertanyaan lain yang ditautkan di sini salah ditutup jadi saya meninggalkan jawaban saya di sini
Simon_Weaver
2
Ini sebenarnya tepat untuk saya gunakan, karena saya sedang mencari cara untuk menggunakan tampilan parsial khusus budaya. Jadi saya hanya memanggil ini dengan nama tampilan khusus budaya, dan kemudian memanggil tampilan default di dalam tangkapan. Dan aku melakukan ini dalam fungsi utilitas, jadi saya tidak punya akses ke ControllerContextsebagai FindViewmetode kebutuhan.
kagum
2

Jika Anda ingin menggunakannya kembali di beberapa tindakan pengontrol, berdasarkan solusi yang diberikan oleh Dave, Anda dapat menentukan hasil tampilan kustom sebagai berikut:

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

    ...
}

Kemudian dalam tindakan Anda, cukup kembalikan contoh tampilan kustom Anda:

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

2 sen saya.

tynar
sumber
1

Di asp.net core 2.x ViewEnginesproperti sudah tidak ada lagi sehingga kami harus menggunakan ICompositeViewEnginelayanan tersebut. Ini varian dari jawaban yang diterima menggunakan injeksi ketergantungan:

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

Bagi yang penasaran: Antarmuka dasar IViewEnginetidak terdaftar sebagai layanan jadi kita harus menyuntikkan ICompositeViewEnginesebagai gantinya. The FindView()Metode namun disediakan oleh IViewEnginesehingga variabel anggota dapat menggunakan antarmuka dasar.

idilov
sumber
0

Berikut cara melakukannya di Razor untuk Core 2.2 dll. Perhatikan bahwa panggilannya adalah "GetView", bukan "Find View)

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