title: Failover to an alternate View when Partial View is not found?
body: <p>I have an MVC app that uses dynamic business objects that are inherited from a parent object type. For example the base class <code>Client</code> might have two sub classes called <code>Vendor</code> and <code>ServiceProvider</code>, and these are all handled by the same controller. I have a partial view that I load on the right side of the page when viewing the client's details called <code>_Aside.cshtml</code>. When I load the client I try to look for a specific Aside first and failing that I load a generic one. Below is what the code looks like.</p>
  
  <pre><code>@try
  {
      @Html.Partial("_" + Model.Type.TypeName + "Aside")
  }
  catch (InvalidOperationException ex)
  {
      @Html.Partial("_Aside")
  }
  </code></pre>
  
  <p>The TypeName property would have the word "Vendor" or "ServiceProvider" in it.</p>
  
  <p>Now this works fine but the problem is I only want it to fail over if the view is not found, It's also failing over when there is an actual <code>InvalidOperationException</code> thrown by the partial view (usually the result of a child action it might call). I've thought about checking against <code>Exception.Message</code> but that seems a bit hackish. Is there some other way I can get the desired result without having to check the <code>Message</code> property or is that my only option at this point?</p>
  
  <pre><code>ex.Message = "The partial view '_ServiceProviderAside' was not found or no view
                engine supports the searched locations. The following locations were
                searched: (... etc)"
  </code></pre>
  
  <p><strong>UPDATE</strong>: This is the class with extension methods I have currently in my project based off of Jack's answer, and Chao's suggestions as well.</p>
  
  <pre class="lang-c# prettyprint-override"><code>//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);
      }
  }
  </code></pre>
  
  <p><strong>UPDATE</strong>: If you happen to be using ASP.NET Core MVC, swap the <code>PartialExists()</code> methods for these three methods, and change all of the usages of <code>HtmlHelper</code> for <code>IHtmlHelper</code> in the other methods. Skip this if you're not using ASP.NET Core</p>
  
  <pre class="lang-c# prettyprint-override"><code>//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&lt;ICompositeViewEngine&gt;();
          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&lt;ICompositeViewEngine&gt;();
          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&lt;ICompositeViewEngine&gt;();
          if (string.IsNullOrEmpty(viewName)) throw new ArgumentNullException(viewName, "View name cannot be empty");
          var view = viewEngine.FindView(viewContext, viewName, false);
          return view.View != null;
      }
  }
  </code></pre>
  
  <p>In my view...</p>
  
  <pre><code>@Html.OptionalPartial("_" + Model.Type.TypeName + "Aside", "_Aside")
  //or
  @Html.OptionalPartial("_" + Model.Type.TypeName + "Aside", "_Aside", Model.AsideViewModel)
  </code></pre>