SqlSiteMapProvider: Child nodes are not allowed.   zodpovězená otázka

C#, ASP.NET WebForms

Dobrý večer.

S pomocí ukázkového kódu jsem si napsal SqlSiteMapProvider pro hierarchické ukládání kategorií produktů. Všechno funguje fajn, až do té doby, než se pokusím do tohoto providera vložit další vnořenou hierarchii dat. V tom okamžiku dostanu chybovou hlášku "Exception Details: System.Configuration.ConfigurationErrorsException: Child nodes are not allowed."

Hledal jsem na internetu, ale nic jsem nenašel. Mohl by mi někdo poradit co je potřeba ještě udělat, aby se můj Provider uměl chovat tak, že se do něj dají vkládat další hierarchické struktury ??

Předem moc děkuji

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
    <siteMapNode url="~/" title="Root"  description="">
        <siteMapNode provider="ShopMenu">
            <siteMapNode url="~/Pokus.aspx" title="Pokus"  description=""/>
        </siteMapNode>
        <siteMapNode url="" title=""  description="" />
        <siteMapNode url="" title=""  description="" />
    </siteMapNode>
</siteMap>

public class SqlSiteMapProvider : StaticSiteMapProvider
{
    #region "Properties and configuration"
    
    private string _title;
    /// <summary>
    /// Název kořenové položky
    /// </summary>
    public string Title
    {
        get { return _title; }
        set { _title = value; }
    }

    private string _url;
    /// <summary>
    /// URL kořenové položky
    /// </summary>
    public string Url
    {
        get { return _url; }
        set { _url = value; }
    }

    private string _connectionStringName;
    /// <summary> 
    /// Název connectionStringu, který použijeme k připojení do databáze 
    /// </summary> 
    public string ConnectionStringName
    {
        get { return _connectionStringName; }
        set { _connectionStringName = value; }
    }

    private string _selectCommand;
    /// <summary> 
    /// SQL příkaz, který vybere seznam položek 
    /// </summary> 
    public string SelectCommand
    {
        get { return _selectCommand; }
        set { _selectCommand = value; }
    }

    private string _titleColumn;
    /// <summary> 
    /// Název sloupce s názvy stránek 
    /// </summary> 
    public string TitleColumn
    {
        get { return _titleColumn; }
        set { _titleColumn = value; }
    }

    private string _urlColumn;
    /// <summary> 
    /// Název sloupce s hodnotami, které se budou dosazovat do URL 
    /// </summary> 
    public string UrlColumn
    {
        get { return _urlColumn; }
        set { _urlColumn = value; }
    }

    private string _urlFormatString;
    /// <summary> 
    /// URL adresa, do které se budou dosazovat hodnoty z datbaáze 
    /// </summary> 
    public string UrlFormatString
    {
        get { return _urlFormatString; }
        set { _urlFormatString = value; }
    }

    public override void Initialize(string name, System.Collections.Specialized.NameValueCollection attributes)
    {
        base.Initialize(name, attributes);

        //načíst hodnoty parametrů z konfigurace 
        if (attributes["title"] != null)
        {
            this.Title = attributes["title"];
        }
        else throw new ArgumentException("title");

        if (attributes["url"] != null)
        {
            this.Url = attributes["url"];
        }
        else throw new ArgumentException("url");

        if (attributes["connectionStringName"] != null)
        {
            this.ConnectionStringName = attributes["connectionStringName"];
        }
        else throw new ArgumentException("connectionStringName");

        if (attributes["selectCommand"] != null)
        {
            this.SelectCommand = attributes["selectCommand"];
        }
        else throw new ArgumentException("selectCommand");

        if (attributes["titleColumn"] != null)
        {
            this.TitleColumn = attributes["titleColumn"];
        }
        else throw new ArgumentException("titleColumn");

        if (attributes["urlColumn"] != null)
        {
            this.UrlColumn = attributes["urlColumn"];
        }
        else throw new ArgumentException("urlColumn");

        if (attributes["urlFormatString"] != null)
        {
            this.UrlFormatString = attributes["urlFormatString"];
        }
        else throw new ArgumentException("urlFormatString");
    }

    #endregion

    private SiteMapNode root;

    public override SiteMapNode BuildSiteMap()
    {
        SiteMapNode rootNode;
        lock (this)
        {
            
            
            if (root == null)       //pokud je třeba generovat, generujeme 
            {
                //vytvořit kořenovou položku 
                root = new SiteMapNode(this, Guid.NewGuid().ToString(), this.Url, this.Title);

                //podpoložky natáhnout z databáze 
                SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings[this.ConnectionStringName].ConnectionString);
                SqlDataAdapter da = new SqlDataAdapter(SelectCommand, con);
                DataSet ds = new DataSet();
                da.Fill(ds, "SiteMap");
                DataTable dtSiteMap = ds.Tables["SiteMap"];
                DataRow rowRoot = dtSiteMap.Select("ParentID IS NULL")[0];

                    //projít vrácené záznamy 
                rootNode = new SiteMapNode(this, Guid.NewGuid().ToString(), string.Format(this.UrlFormatString, rowRoot[this.UrlColumn]), rowRoot[this.TitleColumn].ToString());
                //string rootID = rowRoot["ID"].ToString();
                string rootID = "1";

                AddNode(rootNode, root);
                
                AddChildren(rootNode, rootID, dtSiteMap);
            }

            return root;         //vrátíme kořenovou položku 
        }
    }

    private void AddChildren(SiteMapNode rootNode, string rootID, DataTable dtSiteMap)
    {
        DataRow[] childRows = dtSiteMap.Select("ParentID = " + rootID);
        foreach (DataRow row in childRows)
        {
            SiteMapNode childNode = new SiteMapNode(this, Guid.NewGuid().ToString(), string.Format(this.UrlFormatString, row[this.UrlColumn]), row[this.TitleColumn].ToString());
            string rowID = row["ID"].ToString();
            // Přidá SiteMapNode do kolekce ChildNodes
            // Metodou SiteMapNode AddNode
            AddNode(childNode, rootNode);

            // Kontroluje dceřiné uzly tohoto uzlu
            AddChildren(childNode, rowID, dtSiteMap);
        }
    }

    protected override System.Web.SiteMapNode GetRootNodeCore()
    {
        return BuildSiteMap();
    } 
}
nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pokud dostáváte ConfigurationErrorException, tak není chyba v providerovi, ale v konfiguraci, tedy typicky v souboru web.config. Pošlete sem sekci siteMap.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Dobrý poledne.

Přikládám siteMap defin ze souboru web.config

<siteMap enabled="true" defaultProvider="MySitemapProvider">
			<providers>
				<clear/>
				<add name="MySitemapProvider" type="System.Web.XmlSiteMapProvider" siteMapFile="Web.sitemap" securityTrimmingEnabled="true"/>
				<add name="ShopMenu" type="SqlSiteMapProvider" title="Produkty" url="~/Default.aspx" connectionStringName="ShopData" selectCommand="SELECT * FROM [category] ORDER BY [ParentID], [Name]" titleColumn="Name" urlColumn="ID" urlFormatString="~/Default.aspx?category={0}"/>
			</providers>
		</siteMap>

Rovnou budu reagovat na Váš příspěvek ohledne editace Web.Sitemap "Až se budete vztekat, že sitemapa se sice správně zobrazí, ale už nereaguje na změny databáze, napište."

No, na problém jsem jak jinak než narazil a jen přemýšlím jak bude efektivnější jej řešit. Jestli nastavením času Cache a nebo rovnou využít "Notifikaci cache v SQL Serveru 2008" a tím donutit vytvořir vždycky novou SiteMap, jakmile proběhne změna v tabulce v databázi.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Tahle sekce vypadá v pořádku, chyba v konfiguraci bude někde jinde.

Vemte všechny sekce, které jste do konfigurace přidal a zkuste je postupně odstraňovat, v okamžiku, kdy to přestane padat, uvidíte, ve které sekci je chyba.

Hlášky týkající se chyb v konfiguraci jsou pěkně blbé, protože neřeknou, kde přesně problém je.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Dobrý den.

Chtěl bych se zeptat, zda je možné nějak jednoduše donitit SiteMap načít znovu data do Cache. Jedná se o případ, kdy se siteMap mění třeba 10x po sobě a my nechceme aby proběhla notifikace na SQL Serveru 10x po sobě ale jen 1x po provedení všech těch 10 úprav v tabulce v SQL Serveru. Našel jsem jen jak to řešit časově přes Web.Config ale jak to udělat na vyžádání jsem nikde nenašel.

Moc děkuji za radu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ano, je. Stačí do proměnné rootNode nastavit Nothing (resp. null v C#). V providerovi si na to udělejte metodu a tu pak zavolejte.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Super, nějak takhle to vypadá:

public void ClearSiteMap()
    {
        lock (_lock)
        {
            Clear();
            _nodes.Clear();
            _root = null;
        }
    }

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Dobrý den,

řeším podobný problém, ale při Vašem kódu mi hierarchické řazení funguje, ale zobrazí se jen vždy první root položka a další už ne. Nevíte, čím to může být?

Děkuji

Martin

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Dobrý den.

Můžete k tomu napsat víc ? Případně zaslat strukturu tabulky, ze které berete data a tak... ?Mrknu na to.

S pozdravem

Tomáš

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Tabulka vypada takto:

IdKategorie Nazev ParentID

1 auto NULL

2 moto NULL

3 bmw 1

4 suzuki 2

Vypis do treeview by mel vypadat takto:

auto

bmw

moto

suzuki

Ale misto celeho vypisu se zobrazi jen (vypise se jen jedna hlavni polozka a k ni zarazena hierarchie):

auto

bmw

nastaveni ve web.config:

 
<siteMap enabled="true" defaultProvider="MujSitemapProvider">
			<providers>
				<clear/>
				<add name="MujSitemapProvider" type="System.Web.XmlSiteMapProvider" siteMapFile="Web.sitemap" securityTrimmingEnabled="true" />
        <add name="Menu" type="ParentSqlSiteMapProvider" title="" url="~/Default.aspx" connectionStringName="mujConnectionString" selectCommand="SELECT * FROM [Kategorie]" titleColumn="Nazev" urlColumn="IdKategorie" urlFormatString="~/Category.aspx?id={0}" />
      </providers>
		</siteMap>

Kód je totožný s tím Vaším:

public class ParentSqlSiteMapProvider : StaticSiteMapProvider
{
    #region "Properties and configuration"

    private string _title;
    /// <summary>
    /// Název kořenové položky
    /// </summary>
    public string Title
    {
        get { return _title; }
        set { _title = value; }
    }

    private string _url;
    /// <summary>
    /// URL kořenové položky
    /// </summary>
    public string Url
    {
        get { return _url; }
        set { _url = value; }
    }

    private string _connectionStringName;
    /// <summary> 
    /// Název connectionStringu, který použijeme k připojení do databáze 
    /// </summary> 
    public string ConnectionStringName
    {
        get { return _connectionStringName; }
        set { _connectionStringName = value; }
    }

    private string _selectCommand;
    /// <summary> 
    /// SQL příkaz, který vybere seznam položek 
    /// </summary> 
    public string SelectCommand
    {
        get { return _selectCommand; }
        set { _selectCommand = value; }
    }

    private string _titleColumn;
    /// <summary> 
    /// Název sloupce s názvy stránek 
    /// </summary> 
    public string TitleColumn
    {
        get { return _titleColumn; }
        set { _titleColumn = value; }
    }

    private string _urlColumn;
    /// <summary> 
    /// Název sloupce s hodnotami, které se budou dosazovat do URL 
    /// </summary> 
    public string UrlColumn
    {
        get { return _urlColumn; }
        set { _urlColumn = value; }
    }

    private string _urlFormatString;
    /// <summary> 
    /// URL adresa, do které se budou dosazovat hodnoty z datbaáze 
    /// </summary> 
    public string UrlFormatString
    {
        get { return _urlFormatString; }
        set { _urlFormatString = value; }
    }

    public override void Initialize(string name, System.Collections.Specialized.NameValueCollection attributes)
    {
        base.Initialize(name, attributes);

        //načíst hodnoty parametrů z konfigurace 
        if (attributes["title"] != null)
        {
            this.Title = attributes["title"];
        }
        else throw new ArgumentException("title");

        if (attributes["url"] != null)
        {
            this.Url = attributes["url"];
        }
        else throw new ArgumentException("url");

        if (attributes["connectionStringName"] != null)
        {
            this.ConnectionStringName = attributes["connectionStringName"];
        }
        else throw new ArgumentException("connectionStringName");

        if (attributes["selectCommand"] != null)
        {
            this.SelectCommand = attributes["selectCommand"];
        }
        else throw new ArgumentException("selectCommand");

        if (attributes["titleColumn"] != null)
        {
            this.TitleColumn = attributes["titleColumn"];
        }
        else throw new ArgumentException("titleColumn");

        if (attributes["urlColumn"] != null)
        {
            this.UrlColumn = attributes["urlColumn"];
        }
        else throw new ArgumentException("urlColumn");

        if (attributes["urlFormatString"] != null)
        {
            this.UrlFormatString = attributes["urlFormatString"];
        }
        else throw new ArgumentException("urlFormatString");
    }

    #endregion

    private SiteMapNode root;

    public override SiteMapNode BuildSiteMap()
    {
        SiteMapNode rootNode;
        lock (this)
        {
            if (root == null)       //pokud je třeba generovat, generujeme 
            {
                //vytvořit kořenovou položku 
                root = new SiteMapNode(this, Guid.NewGuid().ToString(), this.Url, this.Title);

                //podpoložky natáhnout z databáze 
                SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings[this.ConnectionStringName].ConnectionString);
                
                SqlDataAdapter da = new SqlDataAdapter(SelectCommand, con);
                DataSet ds = new DataSet();
                da.Fill(ds, "SiteMap");
                DataTable dtSiteMap = ds.Tables["SiteMap"];
                DataRow rowRoot = dtSiteMap.Select("ParentID IS NULL")[0];

                //projít vrácené záznamy 
                rootNode = new SiteMapNode(this, Guid.NewGuid().ToString(), string.Format(this.UrlFormatString, rowRoot[this.UrlColumn]), rowRoot[this.TitleColumn].ToString());
                string rootID = rowRoot["IdKategorie"].ToString();
                //string rootID = "1";

                AddNode(rootNode, root);

                AddChildren(rootNode, rootID, dtSiteMap);
            }

            return root;         //vrátíme kořenovou položku 
        }
    }

    private void AddChildren(SiteMapNode rootNode, string rootID, DataTable dtSiteMap)
    {
        DataRow[] childRows = dtSiteMap.Select("ParentID = " + rootID);
        foreach (DataRow row in childRows)
        {
            SiteMapNode childNode = new SiteMapNode(this, Guid.NewGuid().ToString(), string.Format(this.UrlFormatString, row[this.UrlColumn]), row[this.TitleColumn].ToString());
            string rowID = row["IdKategorie"].ToString();
            // Přidá SiteMapNode do kolekce ChildNodes
            // Metodou SiteMapNode AddNode
            AddNode(childNode, rootNode);

            // Kontroluje dceřiné uzly tohoto uzlu
            AddChildren(childNode, rowID, dtSiteMap);
        }
    }

    protected override System.Web.SiteMapNode GetRootNodeCore()
    {
        return BuildSiteMap();
    }
}

Moc Vám děkuji za pomoc.

Martin

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Problém bude asi v tom, že SiteMap musí mít právě jen jeden kořenový navigační uzel, ze kterého vychází všechny další. Vy ovšem definujete 2 kořenové navigační uzly:

1 auto NULL

2 moto NULL

Zkuste udělat jeden nadřazený (Navigace) kterému dáte NULL a od něj se budou další odvozovat asi následně:

1 Root NULL

2 auto 1

3 moto 1

4 bmw 2

5 suzuki 3

Pokud to bude fungovat tak super. Stačí pak jen v navigační komponentě nastavit, aby zobrazovat navigační uzly až od druhého stupně vnoření a tím předejdete zobrazení kořenového navigačního uzlo Root.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Jasne, to bude ono. Ze me to nenapadlo. Moc Vám děkuji.

Martin

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ještě bych Vás chtěl poprosit, jak nastavit navigační komponentě TreeView, aby zobrazovala navigační uzly až od druhého stupně vnoření?

Děkuji Martin

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback