【问题标题】:How to implement custom SiteMapNodeProvider如何实现自定义 SiteMapNodeProvider
【发布时间】:2013-11-07 14:12:32
【问题描述】:

我正在尝试调整 MvcSiteMapProvider 以根据存储在数据库中的一些信息创建面包屑。

this 帖子中的答案听起来很有希望,所以我实现了自己的 SiteMapNodeProvider。但是后来我不知道如何连接,所以使用新实现的 SiteMapNodeProvider 而不是静态 xml 文件(“Mvc.sitemap”)。

当我在我的项目中使用 SimpleInjector 时,我在我已经存在的注入初始化代码中调用了 setup 方法。

 public static void Initialize()
    {
        Injection.Global = new Container();
        InitializeContainer(Injection.Global);
        Injection.Global.RegisterMvcControllers(Assembly.GetExecutingAssembly());
        Injection.Global.RegisterMvcAttributeFilterProvider();
        Injection.Global.Verify();
        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(Injection.Global));
    }

    private static void InitializeContainer(Container container)
    {
        // Setup configuration of DI
        MvcSiteMapProviderContainerInitializer.SetUp(container);

        //... register some other stuff for my project here ...
    }

MvcSiteMapProviderContainerInitializer 类由包创建:'Mvcsitemapprovider.mvc4.di.simpleinjector/4.4.5'

有人知道如何让我的项目使用新创建的 SiteMapNodeProvider 吗? 我在官方文档中找不到任何关于此的文档...

编辑: 我尝试了你的建议(甚至删除了旧的 DI 东西,只使用了 nuget-package 中的那个),但我仍然收到错误... 这是我的 MvcSiteMapProviderContainerInitializer 中的内容

    public static void SetUp(Container container)
        {
            bool securityTrimmingEnabled = false;
            bool enableLocalization = true;
            string absoluteFileName = HostingEnvironment.MapPath("~/Mvc.sitemap");
            TimeSpan absoluteCacheExpiration = TimeSpan.FromMinutes(5);
            string[] includeAssembliesForScan = new string[] { "testsitemap" };

// Extension to allow resolution of arrays by GetAllInstances (natively based on IEnumerable).
// source from: https://simpleinjector.codeplex.com/wikipage?title=CollectionRegistrationExtensions
            AllowToResolveArraysAndLists(container);

            var currentAssembly = typeof(MvcSiteMapProviderContainerInitializer).Assembly;
            var siteMapProviderAssembly = typeof(SiteMaps).Assembly;
            var allAssemblies = new Assembly[] { currentAssembly, siteMapProviderAssembly };
            var excludeTypes = new Type[]
                {
                    typeof (SiteMapNodeVisibilityProviderStrategy),
                    typeof (SiteMapXmlReservedAttributeNameProvider),
                    typeof (SiteMapBuilderSetStrategy),
                    typeof (ControllerTypeResolverFactory),

// Added 2013-06-28 by eric-b to avoid default singleton registration:
                    typeof(XmlSiteMapController),

// Added 2013-06-28 by eric-b for SimpleInjector.Verify method:
                    typeof(PreservedRouteParameterCollection),
                    typeof(MvcResolver),
                    typeof(MvcSiteMapProvider.SiteMap),
                    typeof(MetaRobotsValueCollection),
                    typeof(RoleCollection),
                    typeof(SiteMapPluginProvider),
                    typeof(ControllerTypeResolver),
                    typeof(RouteValueDictionary),
                    typeof(AttributeDictionary)

                    ,typeof(SiteMapNodeCreator)
                };
            var multipleImplementationTypes = new Type[]
                {
                    typeof (ISiteMapNodeUrlResolver),
                    typeof (ISiteMapNodeVisibilityProvider),
                    typeof (IDynamicNodeProvider)
                };

// Single implementations of interface with matching name (minus the "I").
            CommonConventions.RegisterDefaultConventions(
                (interfaceType, implementationType) => container.RegisterSingle(interfaceType, implementationType),
                new Assembly[] { siteMapProviderAssembly },
                allAssemblies,
                excludeTypes,
                string.Empty);

// Multiple implementations of strategy based extension points
            CommonConventions.RegisterAllImplementationsOfInterfaceSingle(
                (interfaceType, implementationTypes) => container.RegisterAll(interfaceType, implementationTypes),
                multipleImplementationTypes,
                allAssemblies,
                new Type[0],
                "^Composite");

            container.Register<XmlSiteMapController>();

// Visibility Providers
            container.RegisterSingle<ISiteMapNodeVisibilityProviderStrategy>(() =>
                                                                       new SiteMapNodeVisibilityProviderStrategy(
                                                                           container.GetAllInstances
                                                                               <ISiteMapNodeVisibilityProvider>().
                                                                               ToArray(), string.Empty));

// Pass in the global controllerBuilder reference
            container.RegisterSingle<ControllerBuilder>(() => ControllerBuilder.Current);

            container.RegisterSingle<IControllerBuilder, ControllerBuilderAdaptor>();

            container.RegisterSingle<IBuildManager, BuildManagerAdaptor>();

            container.RegisterSingle<IControllerTypeResolverFactory>(() =>
                                                               new ControllerTypeResolverFactory(new string[0],
                                                                                                 container.GetInstance
                                                                                                     <IControllerBuilder
                                                                                                     >(),
                                                                                                 container.GetInstance
                                                                                                     <IBuildManager>()));

// Configure Security
            container.RegisterAll<IAclModule>(typeof(AuthorizeAttributeAclModule), typeof(XmlRolesAclModule));
            container.RegisterSingle<IAclModule>(() => new CompositeAclModule(container.GetAllInstances<IAclModule>().ToArray()));

// Setup cache




            container.RegisterSingle<System.Runtime.Caching.ObjectCache>(() => System.Runtime.Caching.MemoryCache.Default);
            container.RegisterSingleOpenGeneric(typeof(ICacheProvider<>), typeof(RuntimeCacheProvider<>));
            container.RegisterSingle<ICacheDependency>(() => new RuntimeFileCacheDependency(absoluteFileName));

            container.RegisterSingle<ICacheDetails>(() => new CacheDetails(absoluteCacheExpiration, TimeSpan.MinValue, container.GetInstance<ICacheDependency>()));

// Configure the visitors
            container.RegisterSingle<ISiteMapNodeVisitor, UrlResolvingSiteMapNodeVisitor>();


// Prepare for the sitemap node providers
            container.RegisterSingle<ISiteMapXmlReservedAttributeNameProvider>(
                () => new SiteMapXmlReservedAttributeNameProvider(new string[0]));

            container.RegisterSingle<IXmlSource>(() => new FileXmlSource(absoluteFileName));


            // Register the sitemap node providers
            container.RegisterSingle<XmlSiteMapNodeProvider>(() => container.GetInstance<XmlSiteMapNodeProviderFactory>()
                .Create(container.GetInstance<IXmlSource>()));
            container.RegisterSingle<ReflectionSiteMapNodeProvider>(() => container.GetInstance<ReflectionSiteMapNodeProviderFactory>()
                .Create(includeAssembliesForScan));

            // Register your custom sitemap node provider
            container.RegisterSingle<ISiteMapNodeProvider, CustomSiteMapNodeProvider>();

            // Register the collection of sitemap node providers (including the custom one)
            container.RegisterSingle<ISiteMapBuilder>(() => container.GetInstance<SiteMapBuilderFactory>()
                .Create(new CompositeSiteMapNodeProvider(
                    container.GetInstance<XmlSiteMapNodeProvider>(),
                    container.GetInstance<ReflectionSiteMapNodeProvider>(),
                    container.GetInstance<CustomSiteMapNodeProvider>())));


            container.RegisterAll<ISiteMapBuilderSet>(ResolveISiteMapBuilderSets(container, securityTrimmingEnabled, enableLocalization));
            container.RegisterSingle<ISiteMapBuilderSetStrategy>(() => new SiteMapBuilderSetStrategy(container.GetAllInstances<ISiteMapBuilderSet>().ToArray()));
        }

        private static IEnumerable<ISiteMapBuilderSet> ResolveISiteMapBuilderSets(Container container, bool securityTrimmingEnabled, bool enableLocalization)
        {
            yield return new SiteMapBuilderSet(
                "default",
                securityTrimmingEnabled,
                enableLocalization,
                container.GetInstance<ISiteMapBuilder>(),
                container.GetInstance<ICacheDetails>());
        }

        private static void AllowToResolveArraysAndLists(Container container)
        {
            container.ResolveUnregisteredType += (sender, e) =>
            {
                var serviceType = e.UnregisteredServiceType;

                if (serviceType.IsArray)
                {
                    RegisterArrayResolver(e, container,
                        serviceType.GetElementType());
                }
                else if (serviceType.IsGenericType &&
                    serviceType.GetGenericTypeDefinition() == typeof(IList<>))
                {
                    RegisterArrayResolver(e, container,
                        serviceType.GetGenericArguments()[0]);
                }
            };
        }

        private static void RegisterArrayResolver(UnregisteredTypeEventArgs e, Container container, Type elementType)
        {
            var producer = container.GetRegistration(typeof(IEnumerable<>)
                .MakeGenericType(elementType));
            var enumerableExpression = producer.BuildExpression();
            var arrayMethod = typeof(Enumerable).GetMethod("ToArray")
                .MakeGenericMethod(elementType);
            var arrayExpression = Expression.Call(arrayMethod, enumerableExpression);
            e.Register(arrayExpression);
        }
    }

但我仍然得到以下异常:

找不到类型 DynamicSiteMapNodeBuilder 的注册,并且 无法进行隐式注册。的构造函数 type DynamicSiteMapNodeBuilder 包含类型的参数 名称为“siteMapNodeCreator”的 ISiteMapNodeCreator 不是 挂号的。请确保 ISiteMapNodeCreator 已在 容器,或者更改 DynamicSiteMapNodeBuilder 的构造函数。

【问题讨论】:

    标签: asp.net-mvc-4 mvcsitemapprovider


    【解决方案1】:

    首先,要与现有的 DI 设置集成,您应该安装 MvcSiteMapProvider.MVC4.DI.SimpleInjector.Modules 而不是 MvcSiteMapProvider.MVC4.DI.SimpleInjector。您可以通过从包管理器控制台运行以下命令来降级:

    PM>卸载包-Id MvcSiteMapProvider.MVC4.DI.SimpleInjector

    确保不要卸载任何依赖项。这将确保您的项目中没有 2 组 DI 初始化代码 - 整个应用程序应该只有 1 组。

    接下来,您需要连接 DI 以及 MvcSiteMapProvider 所需的一些其他初始化代码。 readme file 包含如何执行此操作的说明。以下是使用现有配置的方法。

    public static void Initialize()
    {
        Injection.Global = new Container();
        InitializeContainer(Injection.Global);
        Injection.Global.RegisterMvcControllers(Assembly.GetExecutingAssembly());
        Injection.Global.RegisterMvcAttributeFilterProvider();
        Injection.Global.Verify();
        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(Injection.Global));
    }
    
    private static void InitializeContainer(Container container)
    {
        // Setup configuration of DI (required)
        MvcSiteMapProviderContainerInitializer.SetUp(container);
    
        // Setup global sitemap loader (required)
        MvcSiteMapProvider.SiteMaps.Loader = container.GetInstance<ISiteMapLoader>();
    
        // Check all configured .sitemap files to ensure they follow the XSD for MvcSiteMapProvider (optional)
        var validator = container.GetInstance<ISiteMapXmlValidator>();
        validator.ValidateXml(HostingEnvironment.MapPath("~/Mvc.sitemap"));
    
        // Register the Sitemaps routes for search engines (optional)
        XmlSiteMapController.RegisterRoutes(RouteTable.Routes); // NOTE: You can put this in your RouteConfig.cs file if desired.
    
        //... register some other stuff for your project here ...
    }
    

    如果/sitemap.xml端点不起作用,您可能还需要添加此行来注册XmlSiteMapController:

    Injection.Global.RegisterMvcControllers(typeof(MvcSiteMapProvider.SiteMaps).Assembly);
    

    要实现ISiteMapNodeProvider,这里有一个例子:MvcSiteMapProvider ISiteMapBuilder in conjunction with IDynamicNodeProvider

    要注册您的自定义 ISiteMapNodeProvider,您只需确保将其添加到 SiteMapBuilder 的构造函数中。您还可以根据需要从下面的代码中排除现有的 SiteMapNodeProviders。

    // Register the sitemap node providers
    container.RegisterSingle<XmlSiteMapNodeProvider>(() => container.GetInstance<XmlSiteMapNodeProviderFactory>()
        .Create(container.GetInstance<IXmlSource>()));
    container.RegisterSingle<ReflectionSiteMapNodeProvider>(() => container.GetInstance<ReflectionSiteMapNodeProviderFactory>()
        .Create(includeAssembliesForScan));
    
    // Register your custom sitemap node provider
    container.RegisterSingle<ISiteMapNodeProvider, CustomSiteMapNodeProvider>();
    
    // Register the collection of sitemap node providers (including the custom one)
    container.RegisterSingle<ISiteMapBuilder>(() => container.GetInstance<SiteMapBuilderFactory>()
        .Create(new CompositeSiteMapNodeProvider(
            container.GetInstance<XmlSiteMapNodeProvider>(), 
            container.GetInstance<ReflectionSiteMapNodeProvider>(), 
            container.GetInstance<CustomSiteMapNodeProvider>())));
    

    请注意,IDynamicNodeProvider(已记录在案)与 ISiteMapNodeProvider 几乎完全相同,因此您可以改用该选项。有 3 个主要区别:

    1. 使用 IDynamicNodeProvider,您必须创建一个定义 dynamicNodeProvider 属性的“模板”节点,并且模板节点本身不会包含在 SiteMap 中,因此它必须与处理动态节点的 ISiteMapNodeProvider 实现结合使用(内置的 ISiteMapNodeProviders 会自动执行此操作)。
    2. IDynamicNodeProvider 不需要成为 DI 设置的一部分,因为它已由 XmlSiteMapNodeProvider 和 ReflectionSiteMapNodeProvider 处理。
    3. 使用 ISiteMapNodeProvider,您可以直接使用 ISiteMapNode 对象,使用 IDynamicNodeProvider,您可以使用抽象 (DynamicNodeProvider),并且会自动进行转换。

    关于 SimpleInjector.Verify

    如果你想让Verify()工作,你需要在MvcSiteMapProviderContainerInitializer的excludeTypes数组中添加以下内容。

    typeof(SiteMapNodeCreator),
    typeof(DynamicSiteMapNodeBuilder)
    

    我已将它们添加到模块中,并将在下一版本的 Nuget 包中,但这些模块不会更新,因此您必须手动进行。

    请注意,Verify() 方法尝试创建在容器中注册的所有内容的实例 - 包括在现实世界中从未由容器创建的对象。因此,如果您使用Verify() 方法,您必须更加谨慎,以免意外注册某些内容。这使得基于约定的注册更加困难。

    【讨论】:

    • 感谢您的帮助,但在调用 verify() 时出现以下异常:配置无效。为类型 ISiteMapNodeCreator 创建实例失败。 ISiteMapNodeCreator 类型的注册委托引发了异常。找不到类型 SiteMapNodeCreator 的注册,并且无法进行隐式注册。 SiteMapNodeCreator 类型的构造函数包含名称为“siteMap”的 ISiteMap 类型的参数,该参数未注册。请确保ISiteMap已在容器中注册,或更改SiteMapNodeCreator的构造函数。
    • ISiteMapNodeCreator 是由 SiteMapNodeCreatorFactory 创建的,所以只需要注册工厂。您可能必须在 excludeTypes 数组中为 typeof(SiteMapNodeCreator) 添加排除项(并且可能还需要添加其他类型)。顺便说一句 - 测试配置不被认为是最佳实践,因为它并不总是能找到配置中的问题:blog.ploeh.dk/2011/12/21/TestingContainerConfigurations
    • 我编辑了我的问题。我怎么知道哪些类型要添加到忽略列表中? nuget-package 是否配置为开箱即用? (很抱歉打扰你,但我只是不明白如何进行......)
    猜你喜欢
    • 1970-01-01
    • 2010-12-02
    • 2016-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-18
    • 2010-11-17
    相关资源
    最近更新 更多