【问题标题】:MvcSiteMapProvider Visibility directives in annotation doesn't work when implementing custom visibility provider实现自定义可见性提供程序时,注释中的 MvcSiteMapProvider 可见性指令不起作用
【发布时间】:2016-07-15 06:41:15
【问题描述】:

我正在使用 C# 注释配置 MvcSiteMapProvider,而不是通过 XML。我根据文档实现了一个自定义可见性提供程序。我从FilteredSiteMapNodeVisibilityProvider 派生我的课程:

public class CustomVisibilityProvider: FilteredSiteMapNodeVisibilityProvider
{
    public override bool IsVisible(ISiteMapNode node, IDictionary<string, object> sourceMetadata)
    {
        if (node.Attributes.Keys.Contains("customVisibility"))
        {
            string customVisibility = (string)node.Attributes["customVisibility"];
            if (!string.IsNullOrEmpty(customVisibility))
            {
                customVisibility = customVisibility.Trim();
                ...
                var criteria = ...
                return criteria && base.IsVisible(node, sourceMetadata);
            }
        }

        return base.IsVisible(node, sourceMetadata);
    }
}

我的控制器的视图:

[MvcSiteMapNode(Title = "My View", ParentKey = "ParentController", Key = "MyView", Order = 922, PreservedRouteParameters = "id", Attributes = @"{ ""Visibility"": ""SiteMapPathHelper,!*"" }")]
public ActionResult MyView(int? id)
{
    return ViewForEntity(id);
}

正如我们所见,我没有在此视图中使用自己的 customVisibility 属性,但我想使用标准的 Visibility 属性。此特定视图不应出现在菜单或其他地方,SiteMap 除外。

问题在于,当检查此视图的 SiteMapNode 在菜单中的可见性(又名(string)sourceMetadata["HtmlHelper"] == "MvcSiteMapProvider.Web.Html.SiteMapPathHelper")时,base.IsVisible(node, sourceMetadata) 返回true。我希望FilteredSiteMapNodeVisibilityProvider 将处理Visibility 属性并返回false,因为这个视图应该只出现在站点地图中。

作为一种解决方法,我目前实施了自己的检查:

    private bool checkDefaultVisibility(ISiteMapNode node, IDictionary<string, object> sourceMetadata)
    {
        bool defaultVisibility = sourceMetadata["HtmlHelper"] == null || !node.Attributes.Keys.Contains("Visibility");
        if (sourceMetadata["HtmlHelper"] != null && node.Attributes.Keys.Contains("Visibility"))
        {
            var htmlHelper = (string)sourceMetadata["HtmlHelper"];  // Example: "MvcSiteMapProvider.Web.Html.SiteMapPathHelper"
            var helpersRules = ((string)node.Attributes["Visibility"]).Split(',');
            foreach(var helperRule in helpersRules)
            {
                if (helperRule != "!*" && htmlHelper.EndsWith("." + helperRule))
                {
                    defaultVisibility = true;
                    break;
                }
            }
        }
        return defaultVisibility;
    }

这是我的自定义可见性提供程序的一种方法。我讨厌它,因为它不是通用的,只处理特定情况。同时我不想在这里重新发明轮子。我希望VisibilityMvcSiteMapProvider 内部处理。如何实现?

【问题讨论】:

标签: c# asp.net-mvc-5 mvcsitemapprovider


【解决方案1】:

字典键区分大小写FilteredSiteMapVisibilityProvider 没有返回 false 的原因是您设置了一个名为 Visibility 的属性,而不是预期的名称 visibility

[MvcSiteMapNode(Title = "My View", ParentKey = "ParentController", Key = "MyView", Order = 922, PreservedRouteParameters = "id", Attributes = @"{ ""visibility"": ""SiteMapPathHelper,!*"" }")]

就使可见性“普遍”而言,这很难做到,因为每个人都有不同的可见性要求。可见性提供者的目的是实现您自己的可见性要求。 FilteredSiteMapNodeVisibilityProvider 尽可能通用,如果不符合您的要求,您需要自定义。

请注意,您可以通过将VisibilityProvider 设置为自定义可见性提供程序类的类型字符串来为特定节点提供override the default visibility provider

[MvcSiteMapNode(Title = "My View", ParentKey = "ParentController", Key = "MyView", Order = 922, PreservedRouteParameters = "id", VisibilityProvider = "MyNamespace.CustomVisibilityProvider, MyAssembly", Attributes = @"{ ""customVisibility"": ""foo"" }")]

如果您需要在同一节点上使用多个可见性提供程序,可以使用外部 DI 和 CompositeSiteMapNodeVisibilityProvider 作为 shown here 来完成。

请注意,如果需要,您可以继承此类以与内部 DI 容器一起使用 - 但对于内部 DI,您需要一个默认构造函数,因此它在内部使用的类型必须硬编码到构造函数中。但是您可以根据需要为整个可见性配置创建任意数量的这些类。

using MvcSiteMapProvider;
using MvcSiteMapProvider.Reflection;

public class MyCompositeVisibilityProvider : CompositeSiteMapNodeVisibilityProvider
{
    public MyCompositeVisibilityProvider()
        : base(
            typeof(MyCompositeVisibilityProvider).ShortAssemblyQualifiedName(), 

            // Note that the visibility providers are executed in
            // the order specified here, but execution stops when
            // the first visibility provider returns false.
            new FilteredSiteMapNodeVisibilityProvider(),
            new TrimEmptyGroupingNodesVisibilityProvider(),
            new CustomVisibilityProvider()
        )
    { }
}

然后通过使用调用它:

[MvcSiteMapNode(Title = "My View", ParentKey = "ParentController", Key = "MyView", Order = 922, PreservedRouteParameters = "id", VisibilityProvider = "MyNamespace.MyCompositeVisibilityProvider, MyAssembly", Attributes = @"{ ""visibility"": ""SiteMapPathHelper,!*"", ""customVisibility"": ""foo"" }")]

另请注意,还有许多其他方法可以控制可见性,包括security trimming、在/Views/Shared/DisplayTemplates/ 文件夹中自定义模板(或创建新模板并在 HTML 帮助程序中明确指定 templateName),甚至创建custom HTML helpers

【讨论】:

  • 酷,CompositeSiteMapNodeVisibilityProvider 似乎提供了最好的责任分离。我稍后再试。
  • 我还在使用安全修整(基于角色)并自定义了模板。现在我的菜单最近变得如此广泛,我不得不考虑缓存以加快速度。
  • 它有效,我正在使用复合提供者和我的自定义提供者
  • 顺便说一句,最初我从小写可见性开始,但是当base.IsVisible(node, sourceMetadata) 调用FilteredSiteMapNodeVisibilityProvider 意外返回true 时,如果这样可以解决问题,我尝试切换到大写。我仍然不明白为什么这不起作用,但无论如何我更喜欢复合提供者。
猜你喜欢
  • 1970-01-01
  • 2015-05-11
  • 1970-01-01
  • 2018-11-05
  • 2015-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-09
相关资源
最近更新 更多