Использование Web.SiteMap с динамическими URL-адресами (URL Routing) - PullRequest
9 голосов
/ 15 ноября 2008

Я бы хотел сопоставить "приблизительные" совпадения в Web.SiteMap

Хорошо работает поставщик статического файла Sitemap для Web.Sitemap, за исключением одной вещи. ЭТО СТАТИКА!

Итак, если бы мне нужно было иметь sitemapnode для каждой из 10000 статей на моей странице, вот так:

  • site.com / статьи / 1 / статьи титульный
  • site.com / статьи / 2 / другая-статья-заголовок
  • site.com / статьи / 3 / другое-статья опять-таки
  • ...
  • site.com / статьи / 9999 / заместитель последней статьи

Есть ли какое-либо сопоставление с подстановочными знаками, которое я могу сделать с SiteMap, чтобы сопоставить что-либо в соответствии со статьями?

Или, возможно, на моей странице Webforms есть способ вручную установить текущий узел?

Я нашел "немного" справки на этой странице , когда делал это с ASP.Net MVC Framework, но все еще ищу хорошее решение для веб-форм.

Я думаю, что мне нужно будет создать собственный SiteMap Provider

Ответы [ 3 ]

7 голосов
/ 20 ноября 2008

Это в ответ на комментарий выше. Я не могу опубликовать полный код, но в основном так работает мой провайдер.

Предположим, у вас есть страница article.aspx, и она использует параметр строки запроса "id" для извлечения и отображения заголовка и тела статьи. Тогда это в Web.sitemap:

<siteMapNode url="/article.aspx" title="(this will be replaced)" param="id" />

Затем вы создаете этот класс:

public class DynamicSiteMapPath : SiteMapPath
{
  protected override void InitializeItem(SiteMapNodeItem item)
  {
    if (item.ItemType != SiteMapNodeItemType.PathSeparator)
    {
      string url = item.SiteMapNode.Url;
      string param = item.SiteMapNode["param"];

      // get parameter value
      int id = System.Web.HttpContext.Current.Request.QueryString[param];

      // retrieve article from database using id
      <write your own code>

      // override node link
      HyperLink link = new HyperLink();
      link.NavigateUrl = url + "?" + param + "=" + id.ToString();
      link.Text = <the article title from the database>;
      link.ToolTip = <the article title from the database>;
      item.Controls.Add(link);
    }
    else
    {
      // if current node is a separator, initialize as usual
      base.InitializeItem(item);
    }
  }
}

Наконец, вы используете этот провайдер в своем коде, как если бы вы использовали статический провайдер.

<mycontrols:DynamicSiteMapPath ID="dsmpMain" runat="server" />

Мой класс сложнее, чем этот, но это основы. Вместо того, чтобы использовать параметр строки запроса, вы можете просто проанализировать дружественный URL-адрес, который вы используете, и использовать его вместо этого, чтобы получить правильный контент. Чтобы свести к минимуму дополнительные операции поиска в базе данных при каждом запросе, вы можете добавить механизм кэширования в провайдер (заголовок статьи обычно меняется не часто).

Надеюсь, это поможет.

2 голосов
/ 18 декабря 2010

Я столкнулся с этой проблемой и, честно говоря, не нашел решений, которые бы делали меня счастливым ... поэтому я заимствую идеи здесь и там. Мое решение состоит из нескольких частей: а) пусть SiteMapProvider находит фактическую страницу, обрабатывающую запрос, и использует ее узел, и б) использует стандартные методы для обновления оттуда sitemap.

A) Проблема, с которой я продолжал сталкиваться, заключается в том, что, если бы у меня не было правильного виртуального пути, SiteMap.CurrentNode был бы нулевым, а функция SiteMapResolve вызывала. Чтобы решить эту проблему, я создал подкласс XmlSiteMapProvider и переопределил CurrentNode:

 namespace WebFormTools
{
    class RouteBaseSitemapProvider : XmlSiteMapProvider
    {
        public override SiteMapNode CurrentNode
        {
            get
            {
                var node = base.CurrentNode;


                if (node == null) 
                {
                    // we don't have a node, see if this is from a route
                    var page = HttpContext.Current.CurrentHandler as System.Web.UI.Page;

                    if (page != null && page.RouteData != null)
                    {
                        // try and get the Virtual path associated with this route
                        var handler = page.RouteData.RouteHandler as PageRouteHandler;

                        if (handler != null) {
                            // try and find that path instead.
                            node = FindSiteMapNode(handler.VirtualPath);
                        }
                    }

                }

                return node;
            }
        }
    }
}

Обычно, если реализация по умолчанию ничего не находит, найдите маршрут (если есть) и попытайтесь найти узел, используя виртуальный путь обработчика.

Для справки - это часть моих файлов Web.Config, Global.asax и SiteMap:

Добавление провайдера

    <siteMap defaultProvider="RouteBaseSitemapProvider">
  <providers>
    <add name="RouteBaseSitemapProvider" type="WebFormTools.RouteBaseSitemapProvider" siteMapFile="Web.sitemap" />
  </providers>
</siteMap>

Маршрут:

            routes.MapPageRoute("EvalRoutes",
            "Evals/{type}/New.aspx",
            "~/Evals/New.aspx");

И Карта сайта:

        <siteMapNode url="~/Evals/New.aspx" title="New Eval - {type}"  description="" />

B) Я подкласс System.Web.UI.Page, метко названный BaseClass, который добавляет метод для регистрации обработчиков для события SiteMapResolve:

public System.Web.SiteMapNode Process(System.Web.SiteMapNode currentNode)
    {
        if (currentNode == null) return currentNode;

        var page = HttpContext.Current.CurrentHandler as System.Web.UI.Page;

        if (page != null && page.RouteData != null)
        {

            Dictionary<Regex, string> replacements = new Dictionary<Regex, string>();

            // build a list of RegEx to aid in converstion, using RegEx so I can ignore class.  Technically I could also
            foreach (var key in page.RouteData.Values.Keys)
            {
                replacements.Add(new Regex(string.Format("\\{{{0}\\}}", key), RegexOptions.IgnoreCase), page.RouteData.Values[key].ToString());              
            }


            // navigate up the nodes
            var activeNode = currentNode;
            while (activeNode != null)
            {
                // to the replacements
                foreach(var replacement in replacements)
                {
                    activeNode.Title =  replacement.Key.Replace(activeNode.Title, replacement.Value);
                }

                activeNode = activeNode.ParentNode;
            }

        }

        return currentNode;
    }

Мне все еще нужно правильно сопоставить URL-адреса (чтобы использовать URL-адрес страницы, получающей маршрут), то есть без информации о маршрутизации. Вероятно, я буду использовать пользовательский атрибут в карте сайта, чтобы сообщить узлу, как отображать URL.

2 голосов
/ 19 ноября 2008

Я думаю, что это не совсем ответ на ваш вопрос, но, возможно, это дает вам представление. Однажды я написал класс DynamicSiteMapPath, унаследовавший SiteMapPath. Я использую пользовательский атрибут в каждом теге <siteMapNode> в Web.sitemap, например:

<siteMapNode url="dynamicpage.aspx" title="blah" params="id" />

Затем класс DynamicSiteMapPath получает значение параметра «id», извлекает содержимое из базы данных и переопределяет отображаемый в данный момент узел элемента карты сайта с правильным заголовком и ссылкой. Это займет немного работы, но когда все сделано правильно, это очень аккуратный способ обеспечения динамической поддержки страниц.

...