Существует ли представление в ASP.NET MVC?

95

Можно ли определить, существует ли конкретное имя представления в контроллере до его рендеринга?

У меня есть требование динамически определять имя представления для рендеринга. Если существует представление с таким именем, мне нужно отобразить это представление. Если нет представления по настраиваемому имени, мне нужно отобразить представление по умолчанию.

Я хотел бы сделать что-то похожее на следующий код в моем контроллере:

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
Просто прочитав название, это кажется очень глубоким философским вопросом.

Ответы:

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

Для тех, кто ищет метод расширения копирования / вставки:

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
Это, наверное, лучше. Я не знал, что существует метод FindView из самой коллекции ViewEngines.
Lance Harper,
1
Но как проверить, существует ли представление для другого контроллера?
SOReader
Отчасти отступление: один из наших инженеров (с тех пор, как перешел) построил собственный механизм просмотра (названный MultiTenantViewEngine, чтобы вы понимали его цель), который реализует FindView для выдачи исключения HttpException (404), если он не может найти заданное Посмотреть. Это хорошая практика? Я понятия не имею. Но не удивлюсь, если есть другие подобные реализации. Поскольку вы не будете знать, как работает механизм представления во время выполнения этого кода, вы можете выбросить catch {return false; } вокруг этого щенка, на всякий случай.
Брайан Колавито
1
@SOReader, я тестировал hvnt, но контроллер IController = new HomeController (); а затем controller.ControllerContext предоставит то, что вы можете передать методам findview.
Vishal Sharma
Спасибо за этот ответ. Это помогло мне в другой проблеме. Мне нужно было проверить, является ли мое представление частичным или нет, и поскольку все мои частичные имена начинаются с подчеркивания, теперь я могу работать с моим решением, проверяя, является ли "result.View! =
Null
19

Как насчет того, чтобы попробовать что-то вроде следующего, если вы используете только один движок просмотра:

bool viewExists = ViewEngines.Engines[0].FindView(ControllerContext, "ViewName", "MasterName", false) != null;
Лэнс Харпер
источник
похоже, что этот был опубликован за 3 минуты до принятого ответа, а любви нет ?! +1 от меня.
Trevor de Koekkoek
@TrevordeKoekkoek ... хммм ... +1
Vishal Sharma
8

Вот еще один [не обязательно рекомендуемый] способ сделать это

 try
 {
     @Html.Partial("Category/SearchPanel/" + Model.CategoryKey)
 }
 catch (InvalidOperationException) { }
Simon_Weaver
источник
это для проверки существования частичного представления в файле .cshtml. на самом деле это не ответ на этот вопрос, а другой вопрос, что ссылки здесь были неправильно закрыты, поэтому я оставляю свой ответ здесь
Simon_Weaver
2
Это было действительно подходящее место для моего использования, поскольку я искал способ использовать частичное представление, специфичное для культуры. Поэтому я просто назвал это с именем представления, специфичным для культуры, а затем вызвал представление по умолчанию внутри улова. И я делал это в функции полезности, так что я не имел доступа к ControllerContextкак к FindViewпотребностям методы.
awe
2

Если вы хотите повторно использовать это в нескольких действиях контроллера, основываясь на решении, предложенном Дейвом, вы можете определить результат пользовательского представления следующим образом:

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

    ...
}

Затем в вашем действии просто верните экземпляр вашего настраиваемого представления:

public ActionResult Index()
{ 
    return new CustomViewResult();
}
DSO
источник
1
ViewEngines.Engines.FindView(ViewContext.Controller.ControllerContext, "View Name").View != null

Мои 2 цента.

тынар
источник
1

В asp.net core 2.x ViewEnginesсвойство больше не существует, поэтому мы должны использовать ICompositeViewEngineслужбу. Это вариант принятого ответа с использованием инъекции зависимостей:

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

Для любопытных: базовый интерфейс IViewEngineне зарегистрирован как сервис, поэтому ICompositeViewEngineвместо этого мы должны внедрить . Однако этот FindView()метод предоставляется, IViewEngineпоэтому переменная-член может использовать базовый интерфейс.

идилов
источник
0

Вот как это сделать в Razor для Core 2.2 и т. Д. Обратите внимание, что это вызов «GetView», а не «Find View»)

@using Microsoft.AspNetCore.Mvc.ViewEngines
@inject ICompositeViewEngine Engine
...
@if (Engine.GetView(scriptName, scriptName, isMainPage: false).Success) 
{
    @await Html.PartialAsync(scriptName)
}
филв
источник