在动态SitemapNode提供程序上延迟加载

时间:2013-11-18 10:19:15

标签: c# asp.net-mvc asp.net-mvc-4 mvcsitemapprovider asp.net-mvc-sitemap

我使用MvcSiteMapProvider设置了面包屑。 我有一个具有一些静态节点的结构,然后在一些叶子上有使用asp.net属性添加的动态节点。

[MvcSiteMapNode(DynamicNodeProvider = "Web.DealerNet.CommonFramework.UserDynamicNodeProvider)]

这样做很好,但是当你深入了解时,性能真的非常糟糕,因为在启动时所有用户都被加载到内存中,并且每个用户都创建了一个节点。 这是official docu中建议的方法。有没有办法在这里使用延迟加载? (在真正调用时加载用户对象的属性,而不是为每个用户创建一个节点......我有20k个用户......)

public class UserDynamicNodeProvider : DynamicNodeProviderBase
    {
        public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
        {

            UserDao dao = DependencyResolver.Current.GetService<UserDao>();

            // Create a node for each element 
            foreach (User user in dao.GetAll())
            {   
                DynamicNode dynamicNode = new DynamicNode();
                dynamicNode.Title = user.UserName;
                dynamicNode.Description = user.UserName;
                dynamicNode.RouteValues.Add("id", user.IdUser);
                dynamicNode.ParentKey = "TheParentKey";
                dynamicNode.Clickable = false;
                dynamicNode.Area = "MyArea";

                yield return dynamicNode;
            }

        }

    }

1 个答案:

答案 0 :(得分:2)

目前不支持延迟加载(尽管为v5计划添加请求级节点)。

目前,用户列表听起来并不像您需要在搜索引擎中编制索引(除非您正在构建社交网站)。每个用户在/sitemap.xml端点中都不需要单独的条目。因此,为每个用户添加节点没什么价值。

如果是这种情况,更好的方法是使用savedRouteParameters强制所有请求匹配单个节点,然后使用visibility providersSiteMapTitleAttribute调整Menu和SiteMapPath HTML帮助程序的显示方式

[MvcSiteMapNode(Area="MyArea", PreservedRouteParameters="id", Clickable="False")]

在您的控制器操作中,将节点(或其父节点)的标题设置为正确的用户。

[SiteMapTitle("UserName")] 
public ViewResult Details(int id) { 
   UserDao dao = DependencyResolver.Current.GetService<UserDao>();

   // This example assumes your user model object has a public property "UserName"
   var user = dao.Find(id); 
   return View(user); 
}

这将复制每个请求上的id,使其与每个请求匹配,为breadcrumb路径中的每个用户提供一个节点的外观。通常,您需要使用FilteredSiteMapNodeVisibilityProvider从菜单中隐藏此“ghost”节点 - 使用此方法时,无法列出菜单中的所有用户。如果您需要所有用户的列表,我建议您创建一个直接从数据库加载的列表。

在帖子How to Make MvcSiteMapProvider Remember a User's Position上查看一些可下载的演示。

另一方面,如果 构建一个社交网站,我会建议extending the cache,以便它缓存到磁盘而不是将其全部保存在内存中。这不会改善初始启动时间,但会阻止您耗尽所有服务器的内存。在线提供了open source file cache和一些instructions on how you could build your implmentation。如果您对重新加载缓存的频率非常小心,这种方法可能对您有用。

可能导致性能问题的另一个问题是您使用DependencyResolver作为服务定位器(anti-pattern)。假设您正在使用外部DI,通过构造函数注入依赖项可确保您的DI容器(而不是应用程序)可以控制对象的生命周期。

public class UserDynamicNodeProvider : DynamicNodeProviderBase
{
    private readonly UserDao userDao;

    public UserDynamicNodeProvider(UserDao userDao)
    {
        if (userDao == null)
            throw new ArgumentNullException("userDao");

        this.userDao = userDao;
    }

    public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
    {
        // Create a node for each element 
        foreach (User user in this.userDao.GetAll())
        {   
            DynamicNode dynamicNode = new DynamicNode();
            dynamicNode.Title = user.UserName;
            dynamicNode.Description = user.UserName;
            dynamicNode.RouteValues.Add("id", user.IdUser);
            dynamicNode.ParentKey = "TheParentKey";
            dynamicNode.Clickable = false;
            dynamicNode.Area = "MyArea";

            yield return dynamicNode;
        }
    }
}

Microsoft不鼓励在您自己的应用程序代码中使用DependencyResolver.Current(请参阅Professional ASP.NET MVC 4,Wrox Press,第308页 - 由Jon Galloway,Phil Haack,Brad Wilson和K. Scott Allen撰写),以及如此紧密地将你的类绑定到MVC框架。