AspNet SmartSiteMapProvider

จาก Wiki Opensource

Custom SiteMapProvider Incorporates QueryString Reliance


จุดเริ่มต้นของปัญหา

Control: SiteMap ที่ติดมาบน ASP.NET (VS 2005) ไม่สนับสนุนการใช้ QueryString ทำให้เกิดปัญหาเมื่อต้องการกลับไปยังหน้าที่ผ่าน เช่น

มีหน้าเว็บ 3 หน้าที่มีการทำงานดังนี้

ListItem.aspx --> ItemDetails.aspx?ID=10 --> UpdateItem.aspx?ID=10

(หน้ารายการ Item --> รายละเอียด Item ที่ 10 --> ปรับปรุง Item ที่ 10)


แต่ SiteMap ที่ใช้งานจะแสดงผลเป็น

หน้ารายการ Item > รายละเอียด Item > ปรับปรุง Item


โดยที่ลิงค์ที่คู่กับ "รายละเอียด Item" จะชี้ไปที่ ItemDetails.aspx เท่านั้น ทำให้เมื่อ User คลิกลิงค์ "รายละเอียด Item" จากหน้า UpdateItem.aspx?ID=10 จะไม่กลับไปที่ Item ID 10


วิธีแก้ไข

  • สร้าง Class ชื่อ SmartSiteMapProvider ที่ Inherit จาก XmlSiteMapProvider ภายในประกอบด้วยคำสั่งดังนี้ (ขอยกตัวอย่างด้วย C#)
public class SmartSiteMapProvider : XmlSiteMapProvider
{
    public override void Initialize(string name, NameValueCollection attributes)
    {
        base.Initialize(name, attributes);
        this.SiteMapResolve += new SiteMapResolveEventHandler(SmartSiteMapProvider_SiteMapResolve);
    }

    SiteMapNode SmartSiteMapProvider_SiteMapResolve(object sender, SiteMapResolveEventArgs e)
    {
        if (SiteMap.CurrentNode == null)
            return null;

        SiteMapNode temp;
        temp = SiteMap.CurrentNode.Clone(true);
        Uri u = new Uri(e.Context.Request.Url.ToString());

        SiteMapNode tempNode = temp;
        while (tempNode != null)
        {
            string qs = GetReliance(tempNode, e.Context);
            if (qs != null)
                if (tempNode != null)
                    tempNode.Url += qs;

            tempNode = tempNode.ParentNode;
        }

        return temp;
    }

    private string GetReliance(SiteMapNode node, HttpContext context)
    {
        //Check to see if the node supports reliance
        if (node["reliantOn"] == null)
            return null;

        NameValueCollection values = new NameValueCollection();
        string[] vars = node["reliantOn"].Split(",".ToCharArray());

        foreach (string s in vars)
        {
            string var = s.Trim();
            //Make sure the var exists in the querystring
            if (context.Request.QueryString[var] == null)
                continue;

            values.Add(var, context.Request.QueryString[var]);
        }

        if (values.Count == 0)
            return null;

        return NameValueCollectionToString(values);
    }

    private string NameValueCollectionToString(NameValueCollection col)
    {
        string[] parts = new string[col.Count];
        string[] keys = col.AllKeys;

        for (int i = 0; i < keys.Length; i++)
            parts[i] = keys[i] + "=" + col[keys[i]];

        string url = "?" + String.Join("&", parts);
        return url;
    }
}
  • แก้ไข web.config
<system.web>
 <siteMap defaultProvider="SmartSiteMapProvider" enabled="true">
      <providers>
        <clear />
        <add name="SmartSiteMapProvider" type="SmartSiteMapProvider" siteMapFile="web.sitemap" securityTrimmingEnabled="true" />
      </providers>
    </siteMap>
</system.web>
  • เพิ่ม reliantOn ที่ SiteMapNode
<siteMapNode url="ListItem.aspx" title="หน้ารายการ Itemร"  description="">
   <siteMapNode url="ItemDetails.aspx"  reliantOn="ID" title="รายละเอียด Item" description="">
        <siteMapNode url="UpdateItem.aspx"  title="ปรับปรุง Item"/>
   </siteMapNode>
</siteMapNode>


เมื่อเพิ่ม reliantOn="ID" ที่หน้า ItemDetails.aspx แล้ว หลังจาก User เลือก UpdateItem ใดๆ และย้ายไปทำงานที่หน้า UpdateItem.aspx?ID=10 ลิงค์ที่ "รายละเอียด Item" จะมี QueryString ?ID=10 ต่อท้าย ทำให้เมื่อกลับมายังหน้า ItemDetails.aspx จะแสดงรายการ Item ที่ User เคยเลือกไว้อย่างถูกต้อง

หน้ารายการ Item > รายละเอียด Item > ปรับปรุง Item
ListItem.aspx  > ItemDetails.aspx?ID=10


Reference: