2013-02-22 2 views
5

У меня есть приложение MVC, которое использует динамические бизнес-объекты, которые наследуются от родительского типа объекта. Например, базовый класс Client может иметь два подкласса, называемых Vendor и ServiceProvider, и все они обрабатываются одним и тем же контроллером. У меня есть частичный вид, который я загружаю с правой стороны страницы при просмотре деталей клиента под названием _Aside.cshtml. Когда я загружаю клиента, я стараюсь сначала искать конкретную версию и не могу загрузить общий. Ниже приведен код.Отказ от альтернативного режима просмотра, когда частичный просмотр не найден?

@try 
{ 
    @Html.Partial("_" + Model.Type.TypeName + "Aside") 
} 
catch (InvalidOperationException ex) 
{ 
    @Html.Partial("_Aside") 
} 

Свойство TypeName будет содержать в нем слово «Поставщик» или «ServiceProvider».

Теперь это прекрасно работает, но проблема в том, что я только хочу, чтобы он потерпел неудачу, если вид не найден, он также терпит неудачу, когда есть фактический InvalidOperationException, брошенный частичным представлением (обычно это результат действия дочернего элемента может позвонить). Я думал о проверке против Exception.Message, но это кажется немного хакерским. Есть ли другой способ получить желаемый результат без необходимости проверки свойства Message или это мой единственный вариант на этом этапе?

ex.Message = "The partial view '_ServiceProviderAside' was not found or no view 
       engine supports the searched locations. The following locations were 
       searched: (... etc)" 

UPDATE: Это класс с методами расширения я в настоящее время в моем проекте основаны от ответа Джека, и предложения Чао, а также.

//For ASP.NET MVC 
public static class ViewExtensionMethods 
{ 
    public static bool PartialExists(this HtmlHelper helper, string viewName) 
    { 
     if (string.IsNullOrEmpty(viewName)) throw new ArgumentNullException(viewName, "View name cannot be empty"); 
     var view = ViewEngines.Engines.FindPartialView(helper.ViewContext, viewName); 
     return view.View != null; 
    } 
    public static bool PartialExists(this ControllerContext controllerContext, string viewName) 
    { 
     if (string.IsNullOrEmpty(viewName)) throw new ArgumentNullException(viewName, "View name cannot be empty"); 
     var view = ViewEngines.Engines.FindPartialView(controllerContext, viewName); 
     return view.View != null; 
    } 

    public static MvcHtmlString OptionalPartial(this HtmlHelper helper, string viewName) 
    { 
     return PartialExists(helper, viewName) ? helper.Partial(viewName) : HtmlString.Empty; 
    } 

    public static MvcHtmlString OptionalPartial(this HtmlHelper helper, string viewName, string fallbackViewName) 
    { 
     return OptionalPartial(helper, viewName, fallbackViewName, null); 
    } 

    public static MvcHtmlString OptionalPartial(this HtmlHelper helper, string viewName, object model) 
    { 
     return PartialExists(helper, viewName) ? helper.Partial(viewName, model) : MvcHtmlString.Empty; 
    } 

    public static MvcHtmlString OptionalPartial(this HtmlHelper helper, string viewName, string fallbackViewName, object model) 
    { 
     return helper.Partial(PartialExists(helper, viewName) ? viewName : fallbackViewName, model); 
    } 

    public static void RenderOptionalPartial(this HtmlHelper helper, string viewName) 
    { 
     if (PartialExists(helper, viewName)) 
     { 
      helper.RenderPartial(viewName); 
     } 
    } 

    public static void RenderOptionalPartial(this HtmlHelper helper, string viewName, string fallbackViewName) 
    { 
     helper.RenderPartial(PartialExists(helper, viewName) ? viewName : fallbackViewName); 
    } 
} 

UPDATE: Если вам случится быть с помощью ASP.NET MVC сердечника, поменять местами PartialExists() методы этих трех методов, и изменить все использований HtmlHelper для IHtmlHelper в других методах. Пропустить это, если вы не используете ASP.NET Сердечник

//For ASP.NET Core MVC 
public static class ViewExtensionMethods 
{ 
    public static bool PartialExists(this IHtmlHelper helper, string viewName) 
    { 
     var viewEngine = helper.ViewContext.HttpContext.RequestServices.GetService<ICompositeViewEngine>(); 
     if (string.IsNullOrEmpty(viewName)) throw new ArgumentNullException(viewName, "View name cannot be empty"); 
     var view = viewEngine.FindView(helper.ViewContext, viewName, false); 
     return view.View != null; 
    } 

    public static bool PartialExists(this ControllerContext controllerContext, string viewName) 
    { 
     var viewEngine = controllerContext.HttpContext.RequestServices.GetService<ICompositeViewEngine>(); 
     if (string.IsNullOrEmpty(viewName)) throw new ArgumentNullException(viewName, "View name cannot be empty"); 
     var view = viewEngine.FindView(controllerContext, viewName, false); 
     return view.View != null; 
    } 

    public static bool PartialExists(this ViewContext viewContext, string viewName) 
    { 
     var viewEngine = viewContext.HttpContext.RequestServices.GetService<ICompositeViewEngine>(); 
     if (string.IsNullOrEmpty(viewName)) throw new ArgumentNullException(viewName, "View name cannot be empty"); 
     var view = viewEngine.FindView(viewContext, viewName, false); 
     return view.View != null; 
    } 
} 

На мой взгляд ...

@Html.OptionalPartial("_" + Model.Type.TypeName + "Aside", "_Aside") 
//or 
@Html.OptionalPartial("_" + Model.Type.TypeName + "Aside", "_Aside", Model.AsideViewModel) 

ответ

1

Вы можете попробовать метод FindPartialView, чтобы проверить, если вид существует. Что-то вдоль этих линий может работать (непроверенные):

public bool DoesViewExist(string name) 
{ 
    string viewName = "_" + Model.Type.TypeName + "Aside"; 
    ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName , null); 
    return (viewResult.View != null); 
} 

Info on the FindPartialView method for ASP MVC 3

+1

Это должно сработать на самом деле, я был слишком сфокусирован на обработке ошибки, которую я не думал искать, используя открытые методы механизма просмотра. Я изменил вопрос, чтобы включить код, который я придумал. –

3

У меня было подобное требование. Я хотел сохранить очиститель разметки и также избегать генерации имени динамического представления дважды. Это то, что я придумал (изменен, чтобы соответствовать вашему примеру):

расширение Helper:

public static string FindPartial(this HtmlHelper html, string typeName) 
{ 
    // If you wanted to keep it in the view, you could move this concatenation out: 
    string viewName = "_" + typeName + "Aside"; 

    ViewEngineResult result = ViewEngines.Engines.FindPartialView(html.ViewContext, viewName); 
    if (result.View != null) 
     return viewName; 

    return "_Aside"; 
} 

Вид:

@Html.Partial(Html.FindPartial(Model.Type.TypeName)) 

или с доступом к модели в парциальное:

@Html.Partial(Html.FindPartial(Model.Type.TypeName), Model) 
5

Нашел этот ответ, пытаясь решить проблему вложенных разделов, поскольку я хотел включить стили и скрипты в промежуточном представлении. В итоге я решил, что самый простой подход - это соглашение templatename_scripts и templatename_styles.

Так что просто чтобы добавить к различным вариантам, вот что я использую на этом основании.

public static class OptionalPartialExtensions 
{ 
    public static MvcHtmlString OptionalPartial(this HtmlHelper helper, string viewName) 
    { 
     return PartialExists(helper, viewName) ? helper.Partial(viewName) : MvcHtmlString.Empty; 
    } 

    public static MvcHtmlString OptionalPartial(this HtmlHelper helper, string viewName, string fallbackViewName) 
    { 
     return helper.Partial(PartialExists(helper, viewName) ? viewName : fallbackViewName); 
    } 

    public static void RenderOptionalPartial(this HtmlHelper helper, string viewName) 
    { 
     if (PartialExists(helper, viewName)) 
     { 
      helper.RenderPartial(viewName); 
     } 
    } 

    public static void RenderOptionalPartial(this HtmlHelper helper, string viewName, string fallbackViewName) 
    { 
     helper.RenderPartial(PartialExists(helper, viewName) ? viewName : fallbackViewName); 
    } 

    public static bool PartialExists(this HtmlHelper helper, string viewName) 
    { 
     if (string.IsNullOrEmpty(viewName)) 
     { 
      throw new ArgumentNullException(viewName, "View name cannot be empty"); 
     } 
     var view = ViewEngines.Engines.FindPartialView(helper.ViewContext, viewName); 
     return view.View != null; 
    } 
} 

Это приносит свои наиболее распространенные случаи использования и методов расширения помогают поддерживать точку зрения, что немного чище, были добавлены RenderPartials для полноты картины.

+0

Ahh! Хороший звонок. Спасибо, что поделились этим. Это было бы лучшим способом сохранить логику того, как перейти к другому представлению из родительского представления. Я работаю над тем, чтобы получать все больше и больше от моей логики из представлений, а также в контроллеры или подобные методы. Я думаю, что я поменю свою реализацию, чтобы использовать этот стиль, спасибо :-) –

+1

Нет проблем, я делаю толки, чтобы делать то же самое, я содрогаюсь каждый раз, когда открываю некоторые из моих старых представлений, полных логики , – Chao

+0

Хахаха, да, я точно знаю, что вы имеете в виду. Напомню, что в моем случае мне пришлось реорганизовать методы, чтобы иметь перегруженную версию, позволяющую мне проходить в режиме просмотра, отличной от модели родительского представления (обычно это дочернее свойство родительской модели просмотра). Я не хотел, чтобы мои частичные представления зависели от одних и тех же viewmodels, что и родители, поскольку они, как правило, одноцелевые модели, мои модели с частичным представлением обычно являются типичными для нескольких видов. –

0

Исправлена ​​ошибка обрабатывать нулевой VIEWNAME или нулевую fallbackViewName (замените соответствующий код в OP):

public static MvcHtmlString OptionalPartial(this HtmlHelper helper, string viewName, string fallbackViewName, object model) 
{ 
    string partialToRender = null; 
    if (viewName != null && PartialExists(helper, viewName)) 
    { 
     partialToRender = viewName; 
    } 
    else if (fallbackViewName != null && PartialExists(helper, fallbackViewName)) 
    { 
     partialToRender = fallbackViewName; 
    } 
    if (partialToRender != null) 
    { 
     return helper.Partial(partialToRender, model); 
    } 
    else 
    { 
     return MvcHtmlString.Empty; 
    } 
} 

Я редактировал код Ора (который сочетает в себе код из нескольких ответов), но мое редактирование в очереди на рецензирование ,

+0

Это действительно не нужно. Идея заключалась в том, чтобы визуализировать представление только в том случае, если оно существует, а если нет, сделать резервное представление. Если запрошенный запрос имеет значение NULL, он, очевидно, не будет существовать, и он сделает резервную копию. Если резервное копирование является нулевым (что никогда не должно произойти), то вызов метода расширения «IHtmlContent.Partial» вызовет ошибку. То, что вы на самом деле делаете, - это проглатывание ошибки, когда это происходит, хотя это может быть то, что вы ищете, оно порождает ошибку по какой-то причине, и я бы предпочел не предлагать другим делать то же самое и позволять им делать это решение. –

+0

@NickAlbrecht Я запретил исключение, разрешив null для обозначения «ничего не делать». В моем случае мне не нужен резервный просмотр, но код не срабатывал при вызове параметра OptionPartial только с одним именем. (Был ли этот метод непроверен?) Я также добавил проверку имени основного представления, потому что некоторые люди могут динамически генерировать имя представления, а null - лучший способ указать отсутствие представления, чем поддельное имя. Мой прецедент: у меня есть шаблон приложения (установлен через NuGet), и каждое приложение может (или не может) реализовывать необязательные представления, которые я не хочу предустанавливать (поэтому не перезаписывать обновления). – YipYip

+0

Ах, ты прав, эта перегрузка была непроверенной. На самом деле у меня не было никаких применений этой перегрузки в моем проекте. Я обновил код в своем сообщении, чтобы отразить исправления. Спасибо за указание на это! То, что я пытался сделать выше, это то, что перегрузка, которая принимает как необязательное имя вида, так и имя резервного представления, никогда не должно быть сценария, в котором резервное представление равно null. –