zoukankan      html  css  js  c++  java
  • 自定义SiteMapProvider实现动态加载SITEMAP dodo

    一、理论知识

    继承自SiteMapProvider

    要实现站点导航,在ASP.NET 2.0中最方便的就是SiteMap功能了。如果仅仅使用XmlSiteMapProvider则只能从静态的sitemap文件中影射出导航来,无法反映数据库中存储的导航结构。如果要实现根据数据库生成站点导航,就要开发自己的SiteMapProvider。

    SiteMapProvider有4个方法要重写,分别是FindSiteMapNode(根据URL获取节点)、GetChildNodes(获取所有子节点)、GetParentNode(获取父节点)、GetRootNodeCore(获取本SiteMapProvider管理范围内的根节点)。对于SiteMap的使用方来说,例如SiteMapDataSource,通过上述4个方法总能在有限步骤内完成SiteMap相关的查询,例如展开若干层SiteMap,以及获取根SiteMapNode到特定SiteMapNode的路径。所以你只要确保自己的SiteMapProvider类正确实现上述4个方法,就可以用于任何SiteMap查询。

    SiteMapProvider的灵活性是非常高的,特能构造严格的树,甚至是有向图,例如一个节点只能有一个父节点,但它却可以是多个节点的子节点。这看上去不那么容易理解,但你确实可以这样做,因为SiteMapProvider可以设计为在多个不同的节点输入到GetChildNodes方法时返回的列表都包含同一个特定的节点。如果你觉得你不需要这种灵活性,而需要使用严格的树,并且树是相对静态的(也就是一次构造树就可以用于多次查询),那就应该考虑继承自StaticSiteMapProvider而不是SiteMapProvider。

    继承自StaticSiteMapProvider

    实现StaticSiteMapProvider的方式与实现SiteMapProvider的方式是不同的。如果你选择了继承自StaticSiteMapProvider,你就相当于确定了导航模型是严格的树。你只需要负责从持久数据中将树结构描述出来,而StaticSiteMapProvider的基础功能会帮你维护树结构在内存中的副本,并由此而提供上述4个要重写的方法中的3个,只剩下GetRootNodeCore需要由你自己重写。

    继承自StaticSiteMapProvider的话,除了GetRootNodeCore需要重写以外,还需要重写BuildSiteMap方法,这个正是StaticSiteMapProvider构建内存中树结构的地方。构建操作所需要的方法也由StaticSiteMapProvider提供了,分别是AddNode和RemoveNode方法,另外还有一个Clear方法可以清空内存中的整个树结构。

    结合数据库

    一般通过数据库构造SiteMap,需要重写StaticSiteMapProvider的Initialize、GetRootNodeCore和BuildSiteMap方法。

    Initialize方法继承自ProviderBase,这是一个所有Provider类的基类。在Initialize方法中你能接收到此Provider的名称与配置信息。你可以将ConnectionString写在此Provider在web.config中的配置节,在Initialize时这些配置键值就会传入,你可以在此时将传入的ConnectionString保存到Provider的私有变量中,但不要在Initialize中构造树,因为它仅仅会被调用一次。

    GetRootNodeCore用于返回此SiteMapProvider责任范围内的根节点。因为整个SiteMap可以由多个SiteMapProvider提供的SiteMap构成,在跨越SiteMapProvider责任范围边界时,范围内的根节点就关键的标记。通常的做法是,在实现自己的StaticSiteMapProvider时使用一个私有变量保存代表根节点的那个SiteMapNode,而这个SiteMapNode由BuildSiteMap负责构建。

    BuildSiteMap是StaticSiteMapProvider派生类的核心,在这里你需要调用Clear方法清空原来的树,然后查询数据库并利用AddNode和RemoveNode方法构建新的树。需要注意的是,此方法必须是线程安全的,因为可能多个客户端访问页面而导致同时向你的StaticSiteMapProvider查询数据,于是多个线程同时调用BuildSiteMap方法。所以重写的BuildSiteMap方法中,通常一开头进行Clear之后就是lock(this),然后再开始构建树。

    注意事项

    SiteMapProvider和StaticSiteMapProvider的其他方法你喜欢的话也可以去重写,记得要确保线程安全就是了。StaticSiteMapProvider自带的方法都是线程安全的,如果直接调用的话则可依赖其内部的lock而无需自己lock。

    如果你的SiteMapProvider要作为XmlSiteMapProvider的下一级SiteMapProvider,则通过在静态sitemap文件中声明<siteMapNode provider="SomeSiteMapProvider" />来配置。如果你的SiteMapProvider中还要引用另外一个SiteMapProvider,那么父SiteMapProvider仅需要在自己提供的SiteMap中包含下一级SiteMap的根节点,这通常可以通过下一级的SiteMapProvider.GetRootNodeCore方法获取,同时设置子SiteMapProvider的ParentProvider属性为父SiteMapProvider,这就行了。

    二、DEMO演示

    /// <summary>
    /// SqlSiteMapProvider
    /// </summary>
    public class SqlSiteMapProvider : StaticSiteMapProvider
    {
        private string _strCon;
        private int _indexID, _indexTitle, _indexUrl, _indexDesc, _indexParent;

        // 节点
        private SiteMapNode _node;

        // 节点字典表
        private Dictionary<int, SiteMapNode> _nodes = new Dictionary<int, SiteMapNode>();

        // 用于单例模式
        private readonly object _lock = new object();

       


       /// <summary>
       /// 初始化
       /// </summary>
       /// <param name="name"></param>
       /// <param name="config"></param>
        public override void Initialize(string name, NameValueCollection config)
        {
            // 验证是否有config
            if (config == null)
                throw new ArgumentNullException("config不能是null");

            // 没有provider则设置为默认的
            if (String.IsNullOrEmpty(name))
                name = "SqlSiteMapProvider";

            // 没有描述就增加一个描述
            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "SqlSiteMapProvider");
            }

            // 调用基类的初始化方法
            base.Initialize(name, config);

            // 初始化连接字符串
            string conStringName = config["connectionStringName"];

            if (String.IsNullOrEmpty(conStringName))
                throw new ProviderException("没找到connectionStringName");

            config.Remove("connectionStringName");

            if (WebConfigurationManager.ConnectionStrings[conStringName] == null)
                throw new ProviderException("根据connectionStringName没找到连接字符串");

            // 获得连接字符串
            _strCon = WebConfigurationManager.ConnectionStrings[conStringName].ConnectionString;

            if (String.IsNullOrEmpty(_strCon))
                throw new ProviderException("连接字符串是空的");

             Clear();

             sitMapBll.ProductTrcipSitMap();


        }

        /// <summary>
        /// 从持久性存储区加载站点地图信息,并在内存中构建它
        /// </summary>
        /// <returns></returns>
        public override SiteMapNode BuildSiteMap()
        {

            lock (_lock)
            {

                // 单例模式的实现
                if (_node != null)
                    return _node;

               
                   
                SqlConnection connection = new SqlConnection(_strCon);

                try
                {
                    SqlCommand command = new SqlCommand("sp_GetSiteMap", connection);
                    command.CommandType = CommandType.StoredProcedure;

                    connection.Open();
                    SqlDataReader reader = command.ExecuteReader();

                    // 获得各个字段的索引
                    _indexID = reader.GetOrdinal("ID");
                    _indexUrl = reader.GetOrdinal("Url");
                    _indexTitle = reader.GetOrdinal("Title");
                    _indexDesc = reader.GetOrdinal("Description");
                    _indexParent = reader.GetOrdinal("Parent");

                    if (reader.Read())
                    {
                        // 把第一条记录作为根节点添加
                        _node = CreateSiteMapNodeFromDataReader(reader);
                        AddNode(_node, null);

                        // 构造节点树
                        while (reader.Read())
                        {
                            // 在站点地图中增加一个节点
                            SiteMapNode node = CreateSiteMapNodeFromDataReader(reader);
                            AddNode(node, GetParentNodeFromDataReader(reader));
                        }

                    }

                    reader.Close();
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.ToString());
                }
                finally
                {
                    connection.Close();
                }

                // 返回SiteMapNode
                return _node;
            }
        }

        /// <summary>
        /// 将检索目前由当前提供程序管理的所有节点的根节点
        /// </summary>
        /// <returns></returns>
        protected override SiteMapNode GetRootNodeCore()
        {
            //Clear();
           
            lock (_lock)
            {
                return BuildSiteMap();
            }
        }

        /// <summary>
        /// 清除站点地图中的节点。
        /// </summary>
        protected override void Clear()
        {
            lock (_lock)
            {
                _nodes = new Dictionary<int,SiteMapNode>();
                base.Clear();
            }
        }

        /// <summary>
        /// 根据DataReader读出来的数据返回SiteMapNode
        /// </summary>
        /// <param name="reader">DbDataReader</param>
        /// <returns></returns>
        private SiteMapNode CreateSiteMapNodeFromDataReader(DbDataReader reader)
        {
            if (reader.IsDBNull(_indexID))
                throw new ProviderException("没找到ID");

            int id = reader.GetInt32(_indexID);

            if (_nodes.ContainsKey(id))
                throw new ProviderException("不能有重复ID");

            // 根据字段索引获得相应字段的值
            string title = reader.IsDBNull(_indexTitle) ? null : reader.GetString(_indexTitle).Trim();
            string url = reader.IsDBNull(_indexUrl) ? null : reader.GetString(_indexUrl).Trim();
            string description = reader.IsDBNull(_indexDesc) ? null : reader.GetString(_indexDesc).Trim();

            // 新建一个SiteMapNode
            SiteMapNode node = new SiteMapNode(this, id.ToString(), url, title, description);

            // 把这个SiteMapNode添加进节点字典表里
            _nodes.Add(id, node);

            // 返回这个SiteMapNode
            return node;
        }

        /// <summary>
        /// 得到父节点的SiteMapNode
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        private SiteMapNode GetParentNodeFromDataReader(DbDataReader reader)
        {
            if (reader.IsDBNull(_indexParent))
                throw new ProviderException("父节点不能是空");

            int pid = reader.GetInt32(_indexParent);

            if (!_nodes.ContainsKey(pid))
                throw new ProviderException("有重复节点ID");

            // 返回父节点的SiteMapNode
            return _nodes[pid];
        }


    }


     

  • 相关阅读:
    使用Apworks开发基于CQRS架构的应用程序(六):创建.NET WCF服务
    Microsoft NLayerApp案例理论与实践 项目简介与环境搭建
    使用Apworks开发基于CQRS架构的应用程序(七):配置数据库
    测试一下亚马逊联盟
    Revit参数族之DMD系列静电水处理器
    Revit参数族之ZP系列消声器
    第一个Ruby程序:Hello world!
    百度文库下载器冰点下载
    sketchup ruby编程之绘制梯段
    加西亚马尔克斯枯枝败叶
  • 原文地址:https://www.cnblogs.com/zgqys1980/p/1701667.html
Copyright © 2011-2022 走看看