5

我有一个 MVC 应用程序,它使用从父对象类型继承的动态业务对象。例如,基类Client可能有两个名为Vendorand的子类ServiceProvider,它们都由同一个控制器处理。我有一个局部视图,当查看名为_Aside.cshtml. 当我加载客户端时,我尝试先查找特定的 Aside,然后加载通用的 Aside 失败。下面是代码的样子。

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

TypeName 属性将包含单词“Vendor”或“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)"

更新:根据 Jack 的回答和 Chao 的建议,这是我目前在项目中使用的具有扩展方法的类。

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

更新:如果您碰巧使用的是 ASP.NET Core MVC,请交换PartialExists()这三种方法的方法,并更改其他方法中HtmlHelperforIHtmlHelper的所有用法。如果您不使用 ASP.NET Core,请跳过此步骤

//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)
4

4 回答 4

5

在尝试解决嵌套部分的问题时遇到了这个答案,因为我想在中间视图中包含样式和脚本。我最终决定最简单的方法是templatename_scriptsand的约定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 以确保完整性。

于 2015-10-08T11:42:53.507 回答
3

我有类似的要求。我想保持视图标记更干净,同时也避免生成两次动态视图名称。这就是我想出的(修改以匹配您的示例):

辅助扩展:

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)
于 2014-10-01T08:28:55.143 回答
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);
 }

有关 ASP MVC 3 的 FindPartialView 方法的信息

于 2013-02-22T19:06:49.567 回答
0

处理 null viewName 或 null 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;
    }
}

我已经编辑了 OP 的代码(它结合了来自多个答案的代码),但我的编辑正在等待同行评审。

于 2017-10-12T00:12:35.680 回答