如何实现自定义 SiteMapNodeProvider [英] How to implement custom SiteMapNodeProvider

查看:30
本文介绍了如何实现自定义 SiteMapNodeProvider的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to adapt the MvcSiteMapProvider to create the breadcrumb based on some Information stored in a database.

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

The answer in this post sounded promising so i implemented my own SiteMapNodeProvider. But then i didnt know how to wire things up so the newly implemented SiteMapNodeProvider is used instead of the static xml file ("Mvc.sitemap").

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

As i am using SimpleInjector in my project, i called the setup method in my already existent Injection-initialization code.

 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'

The MvcSiteMapProviderContainerInitializer class got created by the package: 'Mvcsitemapprovider.mvc4.di.simpleinjector/4.4.5'

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

Does anybody know what to do to make my project use the newly created SiteMapNodeProvider? I couldnt find any documentation about this in the official docu...

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

edit: i tried what you suggested (even removed the old DI stuff and only used the one from the nuget-package) but still i am getting errors... here is what i have in my 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);
        }
    }

但我仍然收到以下异常:

but still i get the following exception:

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

No registration for type DynamicSiteMapNodeBuilder could be found and an implicit registration could not be made. The constructor of the type DynamicSiteMapNodeBuilder contains the parameter of type ISiteMapNodeCreator with name 'siteMapNodeCreator' that is not registered. Please ensure ISiteMapNodeCreator is registered in the container, or change the constructor of DynamicSiteMapNodeBuilder.

推荐答案

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

First of all, to integrate with an existing DI setup, you should install MvcSiteMapProvider.MVC4.DI.SimpleInjector.Modules instead of MvcSiteMapProvider.MVC4.DI.SimpleInjector. You can downgrade by running this command from package manager console:

PM> Uninstall-Package -Id MvcSiteMapProvider.MVC4.DI.SimpleInjector

PM> Uninstall-Package -Id MvcSiteMapProvider.MVC4.DI.SimpleInjector

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

Be sure NOT to uninstall any dependencies. This will ensure that you don't have 2 sets of DI initialization code in your project - there should only be 1 for the entire application.

接下来,您需要连接 DI 以及 MvcSiteMapProvider 所需的其他一些初始化代码.自述文件包含说明如何去做这个.以下是您将如何使用现有配置进行操作.

Next, you need to wire up for DI as well as some other initialization code required by MvcSiteMapProvider. The readme file contains instructions how to do this. Here is how you would do it with your existing configuration.

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:

If the /sitemap.xml endpoint doesn't work, you may also need to add this line to register the XmlSiteMapController:

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

实现ISiteMapNodeProvider,这里有一个例子:MvcSiteMapProvider ISiteMapBuilder中结合 IDynamicNodeProvider.

To implement ISiteMapNodeProvider, there is an example here: MvcSiteMapProvider ISiteMapBuilder in conjunction with IDynamicNodeProvider.

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

To register your custom ISiteMapNodeProvider, you just need to ensure it gets added to the constructor of SiteMapBuilder. You can also exclude the existing SiteMapNodeProviders from the code below depending on your needs.

// 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 个主要区别:

Do note that IDynamicNodeProvider (which is documented) does almost exactly the same thing as ISiteMapNodeProvider, so you could use that option instead. There are 3 main differences:

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

关于 SimpleInjector.Verify

如果您想让 Verify() 工作,您需要将以下内容添加到 MvcSiteMapProviderContainerInitializer 中的 excludeTypes 数组中.

About SimpleInjector.Verify

If you want Verify() to work, you need to add the following to the excludeTypes array in the MvcSiteMapProviderContainerInitializer.

typeof(SiteMapNodeCreator),
typeof(DynamicSiteMapNodeBuilder)

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

I have added them to the module and will be in the next version of the Nuget package, but these modules do not update so you have to do it manually.

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

Note that the Verify() method tries to create an instance of everything that is registered with the container - including objects that never get created by the container in the real world. Therefore, if you use the Verify() method you have to be more diligent that something is not accidentally registered. This makes convention-based registration more difficult to do.

这篇关于如何实现自定义 SiteMapNodeProvider的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆