2014-10-24 3 views
2

Я установил v4 MvcSiteMapProvider, и теперь я хочу динамически загружать файл Sitemap. Мои потребности просты - загрузите XML-карту сайта для каждого запроса на страницу, на основе текущей роли пользователя, например. AdminSiteMap.xml и UserSiteMap.xmlЗагрузите XML-карту сайта в MvcSiteMapProvider на основе роли пользователя

Оказывается, что это может быть сделано:

Поэтому в основном вы должны использовать DI для достижения этого (Overkill IMHO). Любой шанс это можно сделать без a DI?

Так как я использую ASP Boilerplate (http://www.aspnetboilerplate.com/) У меня есть замок Виндзор как мой DI.

Таким образом, я установил «MvcSiteMapProvider MVC5 Windsor Dependency Injection Configuration» через NuGet. Однако теперь, когда я запускаю приложение, я получаю следующую ошибку:

SiteMapLoader не был инициализирован.

Check the 'MvcSiteMapProvider_UseExternalDIContainer' setting in the AppSettings section of web.config. 

If the setting is set to 'false', you will need to call the MvcSiteMapProvider.DI.Composer.Compose() method at the end of Application_Start in the Global.asax file. Alternatively, if you are using .NET 4.0 or higher you can install the MvcSiteMapProvider.MVCx NuGet package corresponding to your MVC version. 

If the setting is set to 'true', you must set the SiteMaps.Loader property during Application_Start in Global.asax to an instance of the built-in SiteMapLoader type or a custom ISiteMapLoader instance. This can be achieved most easily by using your external DI container. 

Я не изменил конфигурацию по умолчанию, и подтвердили, что Install() метод вызывается в класса MvcSiteMapProviderInstaller общественности: IWindsorInstaller, как она попадает в точку останова там.

Итак, что мне здесь не хватает, чтобы сделать эту работу. Помните, что все, что я пытаюсь сделать, это загрузить SiteMap на основе зарегистрированного пользователя по каждому запросу.

**** **** UPDATE

Хотя это не может быть элегантным это не требует огромного количества кода, как это было предложено путем внедрения DI контейнера. См ответ viggity (около 4-го один вниз) на @Using Multiple MvcSiteMaps

ответ

5

Прежде всего, 1 SiteMap для каждого пользователя можно, но не очень хорошо масштабируется - на самом деле это в значительной степени поражения цели создания сайта карту в , Я бы не рекомендовал этот подход, если вы не уверены, что на вашем сайте не будет более нескольких десятков одновременных пользователей, у вас будет менее нескольких сотен страниц на сайте, и у вас есть дополнительные ресурсы на сервере.

Есть более масштабируемые варианты, чтобы сделать узлы видимыми/невидимыми в соответствии с которым пользователь вошел в систему.

  • Использование Security Trimming. Когда включено, это автоматически срабатывает, только правильно настроив защиту MVC с помощью AuthorizeAttribute. AuthorizeAttribute имеет полную поддержку ролей. Вы также можете наследовать AuthorizeAttribute, чтобы добавить пользовательскую логику безопасности, если вам нужно.
  • Используйте custom visibility providers, чтобы контролировать, является ли каждый узел видимым или невидимым в соответствии с пользовательскими критериями.
  • Настроить встроенные HTML-хелперы (путем изменения шаблонов в папке /Views/Shared/DisplayTemplates/) или build custom HTML helpers для динамической загрузки ссылок на запрос для каждого пользователя в дополнение к ссылкам из экземпляра SiteMap.

Ни один из этих подходов не требует внешнего DI.

Рекомендованный подход заключается в загрузке всех узлов в SiteMap, к которым каждый пользователь может получить доступ, а затем использовать обрезку безопасности, чтобы сделать узлы для текущего пользователя невидимыми в пользовательском интерфейсе. Вы можете принудительно перезагрузить кеш при изменении данных с помощью SiteMapCacheReleaseAttribute, чтобы сделать dynamic nodes видимым в пользовательском интерфейсе сразу после добавления их в источник данных.


С этим знанием, если вы все-таки хотел бы продолжить вниз путь вы в данный момент, вы установили неправильный пакет NuGet. Способ загрузки зависимостей заключается в том, что вам нужен ровно 1 корневой состав в вашем проекте (то есть один экземпляр WindsorContainer). Поскольку у вас уже есть корень композиции в вашем проекте, вы должны установить только модули MvcSiteMapProvider для Windsor, а затем вручную добавить модуль в конфигурацию Windsor, добавив несколько строк кода. Вы можете вернуться к модулям только пакет, выполнив эту команду в Package Manager Console:

PM> Uninstall-Package MvcSiteMapProvider.MVC5.DI.Windsor 

Тогда поиск где new WindsorContainer() объявлен в проекте и добавить модуль MvcSiteMapProvider к конфигурации DI.

// Create the DI container (typically part of your DI setup already) 
var container = new WindsorContainer(); 


// Your existing DI configuration should typically be here... 

// Setup configuration of DI 
container.Install(new MvcSiteMapProviderInstaller()); // Required 
container.Install(new MvcInstaller()); // Required by MVC. Typically already part of your setup (double check the contents of the module). 

// Setup global sitemap loader (required) 
MvcSiteMapProvider.SiteMaps.Loader = container.Resolve<ISiteMapLoader>(); 

// Check all configured .sitemap files to ensure they follow the XSD for MvcSiteMapProvider (optional) 
var validator = container.Resolve<ISiteMapXmlValidator>(); 
validator.ValidateXml(HostingEnvironment.MapPath("~/Mvc.sitemap")); 

// Register the Sitemaps routes for search engines (optional) 
XmlSiteMapController.RegisterRoutes(RouteTable.Routes); 

Если вы обеспечиваете есть только один экземпляр WindsorConntainer проекта-широкий и добавить код, указанный выше в зависимости от обстоятельств, вы должны иметь конфигурацию рабочего DI.

Чтобы загрузить 1 SiteMap для каждого пользователя, вам необходимо создать собственный ISiteMapCacheKeyGenerator, который возвращает другую строку для каждого пользователя.

public class UserSiteMapCacheKeyGenerator 
    : ISiteMapCacheKeyGenerator 
{ 
    public virtual string GenerateKey() 
    { 
     var context = HttpContext.Current; 
     if (context.User.Identity.IsAuthenticated) 
     { 
      // Note: the way you retrieve the user name depends on whether you are using 
      // Windows or Forms authentication 
      return context.User.Identity.Name; 
     } 
     else 
     { 
      return "default"; 
     } 
    } 
} 

И введите его, отредактировав модуль Виндзора по адресу /DI/Windsor/Installers/MvcSiteMapProviderInstaller.cs.

var excludeTypes = new Type[] { 
    // Use this array to add types you wish to explicitly exclude from convention-based 
    // auto-registration. By default all types that either match I[TypeName] = [TypeName] or 
    // I[TypeName] = [TypeName]Adapter will be automatically wired up as long as they don't 
    // have the [ExcludeFromAutoRegistrationAttribute]. 
    // 
    // If you want to override a type that follows the convention, you should add the name 
    // of either the implementation name or the interface that it inherits to this list and 
    // add your manual registration code below. This will prevent duplicate registrations 
    // of the types from occurring. 

    // Example: 
    // typeof(SiteMap), 
    // typeof(SiteMapNodeVisibilityProviderStrategy) 
    typeof(SiteMapNodeUrlResolver), 
    typeof(ISiteMapCacheKeyGenerator) // <-- add this line 
}; 

// Code omitted here... 


// Add this to the bottom of the module 
container.Register(Component.For<ISiteMapCacheKeyGenerator>().ImplementedBy<UserSiteMapCacheKeyGenerator>(); 

Оставшаяся только вещь использовать dynamic node providers или реализации ISiteMapNodeProvider динамически поставлять узлы для каждого пользователя. Если вы настроили его выше, вы можете получить имя пользователя через свойство SiteMap.CacheKey.

public class SomeDynamicNodeProvider : DynamicNodeProviderBase 
{ 
    public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node) 
    { 
     // Get the user name 
     var user = node.SiteMap.CacheKey; 

     // Entities would be your entity framework context class 
     // or repository. 
     using (var entities = new Entities()) 
     { 
      // Add the nodes for the current user only 
      foreach (var story in entities.Stories.Where(x => x.User == user) 
      { 
       DynamicNode dynamicNode = new DynamicNode(); 
       dynamicNode.Title = story.Title; 

       // The key of the node that this node will be the child of. 
       // This works best if you explicitly set the key property/attribute 
       // of the parent node. 
       dynamicNode.ParentKey = "Home"; 
       dynamicNode.Key = "Story_" + story.Id; 
       dynamicNode.Controller = "Story"; 
       dynamicNode.Action = "Details"; 

       // Add the "id" (or any other custom route values) 
       dynamicNode.RouteValues.Add("id", story.Id); 

       yield return dynamicNode; 

       // If you have child nodes to the current node, you can 
       // nest them here by setting their ParentKey property to 
       // the same value as the dynamicNode.Key and returning them 
       // using yield return. 
      } 
     } 
    } 
} 

И, наконец, добавьте свои «шаблонные» узлы в свою конфигурацию для загрузки динамических узлов.

// Set a key explicitly to attach the dynamic nodes to. 
// The key property here corresponds to the ParentKey property of the dynamic node. 
<mvcSiteMapNode title="Home" controller="Home" action="Index" key="Home"> 
    // Use a "dummy" node for each dynamic node provider. This node won't be in the SiteMap. 
    <mvcSiteMapNode dynamicNodeProvider="NamespaceName.SomeDynamicNodeProivder, AssemblyName"/> 
</mvcSiteMapNode>