ASP.NET ja hierarhilised andmed

14.04.2007  |  Gunnar

ASP.NET 2.0 tõi endaga peale muu uue ja huvitava kaasa ka kontrollerid, mis toetavad hierarhiliste andmete esitamist. Käesolevas kandes tutvume IHierarchicalData ja IHierarchyEnumerable interface'idega, mille abil saame luua wrapper'id äriloogika kihi hierarhilisi andmeid esitavatele klassidele.

Hierarhilisi andmeid toetavad klassid ja interface'id asuvad System.Web.UI nimeruumis. Selles valikus ei ole midagi imelikku, sest tegemist on vahenditega, mis on suunatud hierarhiliste andmete toega kontrolleritele. Äriloogika kihti need üldiselt ei sobi, sest ülesehituselt on need wrapper'id, mitte klassid, mida äriloogika kihi klassid laiendama peaksid.

Omamoodi on see hea, sest wrapper'ite taha saab peita küllaltki erinevaid hierarhiate haldamise mehanisme. Seejuures jääb hierarhilisi andmeid toetavatele kontrolleritele alati sama liides, mille abil andmeid saab endaga siduda.

Alustuseks

Näitena vaatame IHierarchicalData ja IHierarchicalEnumerable interface'e. Olgu meil äriloogika kihis klass Section, mis esindab näiteks jaotust sisuhaldussüsteemis. Jaotused võivad sisaldada alamjaotusi ja seega on igal jaotusel olemas ka aste kõrgem jaotus, millele see allub. See ei kehti esimese taseme jaotuste kohta, sest nende aste kõrgemaks jaotuseks on null.

/// <summary>
/// Klass sisu jaotuste jaoks.
/// </summary>
public class Section
{
    private Int32 sectionId = 0;
    private string title;
    private string description;

    private Section parent;
    private IList subSections;

    /// <summary>
    /// Määrab või tagastab jaotuse unikaalse identifikaatori.
    /// </summary>
    public Int32 Id
    {
        get { return this.sectionId; }
        set { this.sectionId = value; }
    }

    /// <summary>
    /// Määrab või tagastab jaotuse kirjelduse.
    /// </summary>   
    public string Description
    {
        get { return this.description; }
        set { this.description = value; }
    }

    /// <summary>
    /// Määrab või tagastab jaotuse, millele käesolev jaotus allub.
    /// Esimese taseme jaotuste korral tagastatakse null.
    /// </summary>   
    public Section Parent
    {
        get { return this.parent; }
        set { this.parent = value; }
    }

    /// <summary>
    /// Määrab või tagastab jaotusele alluvate jaotuste loendi.
    /// </summary>   
    public IList SubSections
    {
        get { return this.subSections; }
        set { this.subSections = value; }
    }

    /// <summary>
    /// Määrab või tagastab jaotuse pealkirja.
    /// </summary>   
    public string Title
    {
        get { return this.title; }
        set { this.title = value; }
    }
}

Klassi eraldi dokumenteerima pole siinkohal mõtet hakata, sest inline dokumentatsioon
peaks olema igati piisav. Äriloogikakihi poolelt jätan teostuse lahtiseks, sest muidu kaldume teemast väga kaugele kõrvale.

IHierarchyData

Esimesena vaatame IHierarchyData interface'i. See on wrapper hierarhilistele andmeobjektidele. Antud interface defineerib järgmised omadused ja meetodid.

  • GetChildren() - tagastab käesolevale andmeobjektile alluvate objektide nimistu.
  • GetParent() - tagastab objekti, millele käesolev objekt allub.
     
  • HasChildren - tagastab tõese väärtuse kui käesoleval andmeobjektil leidub alamandmeobjekte.
  • Item - tagastab käesoleva objektiga seotud andmeobjekti.
  • Path - tagastab andmeobjekti asukoharaja hierarhias.
  • Type - objekti tüübi nimi.

Et jaotuste klass teha hierarhilise klassina arusaadavaks näiteks TreeView kontrollerile, peame looma klassi, mis järgib IHierarchyData interface'i. Vastava klassi nimi olgu SectionData.

/// <summary>
/// Jaotuse hierarhiline wrapper.
/// </summary>
public class SectionData : IHierarchyData
{
    private Section section;

    /// <summary>
    /// Klassi konstruktor.
    /// </summary>
    /// <param name="section">Jaotus, millele wrapper luuakse.</param>
    public SectionData(Section section)
    {
        this.section = section;
    }

    /// <summary>
    /// Tagastab alamjaotuste loendi.
    /// </summary>
    public IHierarchicalEnumerable GetChildren()
    {
        return new SectionCollection(this.section.SubSections);
    }

    /// <summary>
    /// Tagastab jaotuse, mille alla käesolev jaotus kuulub.
    /// </summary>
    public IHierarchyData GetParent()
    {
        return new SectionData(this.section.Parent);
    }

    /// <summary>
    /// Tagastab tõese väärtuse, kui käesoleval jaotusel on alamjaotusi.
    /// </summary>
    public bool HasChildren
    {
        get
        {
            if(this.section.SubSection == null)
                return 0;
            return (this.section.SubSections.Count> 0);
        }
    }

    /// <summary>
    /// Tagastab jaotuse, mida wrapper sisaldab.
    /// </summary>
    public object Item
    {
        get { return section; }
    }

    /// <summary>
    /// Tagastab käesoleva jaotuse asukoharaja hierarhias.
    /// </summary>
    public string Path
    {
        get
        {
            Section section = this.section;
            string s = this.section.Title;
            while ((section = section.Parent) != null)
                s = section.Title + ": " + s;
            return s.Trim();
        }
    }

    /// <summary>
    /// Tagastab käesoleva jaotuse tüübinime hierarhilise
    /// kontrolleri sõlme jaoks.
    /// </summary>
    public string Type
    {
        get { return this.section.ToString(); }
    }
}

SectionData objekti loomisel anname talle kaasa Section objekti, mille põhjal SectionData objekt teda kasutavale kontrollerile vastab.

IHierchicalEnumerable

Andmeallikad kujutavad endast andmeobjektide kollektsioone. Seepärast on meil vaja ka ühte interface'i hierarhiliste kollektsioonide jaoks. Selleks pakub System.Web.UI nimeruum meile välja IHierarchicalEnumerable interface'i. See lisab kollektsioonile kaks meetodi.

  • GetHierarchyData() - tagastab antud andmeobjektile vastava IHierarchyData interface-i järgiva wrapperi.
  • GetEnumerator() - tagastab enumeraatori, millega kollektsiooni elemendid järjest läbida.

Seega peame nüüd looma jaotuste jaoks kollektsiooni, mis järgib IHierarchicalEnumerable interface'i. Et me algset kollektsiooni ei peaks kopeerima, siis loome wrapper'i, millesse anname algse kollektsiooni argumendina kaasa.

/// <summary>
/// Jaotuste kollektsiooni hierarhiline wrapper.
/// </summary>
public class SectionCollection : IHierarchicalEnumerable
{
    private IList sectionList = null;

    /// <summary>
    /// Klassi konstruktor.
    /// </summary>
    /// <param name="sectionList">Jaotuste kollektsioon</param>
    public SectionCollection(IList sectionList)
    {
        this.sectionList = sectionList;
    }

    /// <summary>
    /// Tagastab andmeobjektile vastava wrapperi.
    /// </summary>
    /// <param name="o">Andmeobjekt, mille wrapperit soovitakse.
    /// </param>   
    public IHierarchyData GetHierarchyData(object o)
    {
        return new SectionData((Section)o);
    }

    /// <summary>
    /// Tagastab kollektsiooni enumeraatori.
    /// </summary>
    public IEnumerator GetEnumerator()
    {
        return sectionList.GetEnumerator();
    }
}

Nüüd on meil olemas kõik vajalik, et siduda jaotused automaatselt mõne hierarhilise kontrolleri külge. Selles näites võtame vaatluse alla TreeView-kontrolleri.

Seome andmed TreeView külge

Nüüd proovime siis andmed siduda TreeView-kontrolleri külge. Selleks on vaja ühte web form'i, millele veame TreeView-kontrolleri. Kontrolleri definitsiooni lisame alltoodud ploki.

<DataBindings>
<asp:TreeNodeBinding TextField="Title" ValueField="Id" />
</DataBindings>

Andmed seome kontrolleri külge GET-pöördumise korral kontrolleri laadimisel ning POST-pöördumise korral siis, kui postituse põhjustanud kontroller seda nõuab.

/// <summary>
/// Lehe laadimise sündmus.
/// </summary>
protected void Page_Load(object sender, EventArgs e)
{
    if(!this.IsPostBack)
        this.BindTree();
}
/// <summary>
/// Seob TreeView kontrolleri andmetega.
/// </summary>
private void BindTree()
{
    SectionManager manager = new SectionManager();
    SectionCollection sections;
    sections = new SectionCollection(manager.ListFirstLevel());
    this.trvSections.DataSource = sections;
    this.trvSections.DataBind();
}

Toodud koodis on SectionManager jaotuste haldusklass ning meetod ListFirstLevel() tagastab loendi kõigi esimese taseme jaotustega. Vormil olev TreeView-kontroller nimega trvSections kuvab andmetega sidumisel kenasti jaotuste hierarhia.

Märkusena veel nii palju, et Section-klassi loomise ja selle atribuutide väärtustamisega me siinkohal ei tegele, sest sellest saaks kirjutada terve eraldi artikli.

Kokkuvõte

Kokkuvõtteks võime öelda, et hierarhiliste andmete tuge pole ASP.NET 2.0 kontrollerite jaoks raske ehitada. System.Web.UI nimeruumi poolt pakutavate interface'ide abil on võimalik luua wrapper-klassid, kuhu saab ehitada väga mitmekesise hierarhiate toe.

Kommenteeri

sulge
Saada link e-postiga

© DT 2012 | Creative Commons Attribution-Noncommercial 3.0 License | WordPress