ASP.NET – Diskusní forum (6)

Výsledkem posledního dílu seriálu o diskusním fóru bude schopnost zobrazovat, které příspěvky byly daným čtenářem přečteny a které ne.

Co je základním problémem, pokud chceme implementovat označování přečtených a nepřečtených příspěvků v diskusním foru? Abychom na tuto otázku mohli odpovědět, musíme se nejdříve zamyslet nad realizací „paměti“ fora – místa kam se bude ukládat informace o tom, zda uživatel daný příspěvek četl.

Forum musí nejdříve identifikovat daného uživatele. Rozhodl jsem se použít cookie pro „zapamatování“ identity uživatele. Aplikace při prvním přístupu přiřadí uživateli cookie „UserID“ – což je řetězec, který identifikuje unikátně uživatele systému.

Samotná datová implementace je závislá na typu datového zdroje. To znamená, že bude v kompetenci datového adaptéru (třídy implementující rozhraní IForumDataAdapter), nebo samostatné nové třídy. Pokud jste sledovali seriál pozorně, víte již, že datový adaptér (a data v něm obsažená) je doposud shodný pro všechny uživatele systému. To také umožňuje snadné cachování dat pomocí objektu Cache. Stačí celý adaptér naplnit daty jednou pomocí metody Load(), poté jej umístnit do Cache, která se postará o uchování veškerých potřebných dat v paměti.

Pokud ale přidáme do datového adaptéru informace vztahující se ke konkrétnímu uživateli, nastává veliký problém. Sdílená data uložena globálně v cache nejsou právě ideálním nosičem informací, které se mění od uživatele k uživateli. Jak toto dilema vyřešit ? Uvedu některé z možností:

  1. Můžeme naprosto oddělit získávání globálních dat od dat specifických pro jednotlivé uživatele. To by znamenalo napsat nové třídy (a nový interface) pro získáváni specifických dat.
  2. Zkusit nějak zakomponovat do stávajícího datového adapteru možnost pracovat i s uživatelsky specifickými daty.

Já jsem se rozhodl pro použití druhého přístupu, nakolik první cesta by znamenala další rozšiřování počtu tříd a rozhraní, které je již teď hodně vysoké – a určitě by to zhoršilo výslednou čitelnost architektury celé aplikace.

Rozhraní IForumDataAdapter bylo tedy rozšířeno o následující metody:

  • LoadUserInfo
  • UnloadUserInfo
  • IsRead
  • MarkAsRead

Názvy jednotlivých metod vcelku vystihují, co metody dělají. V praxi to funguje tak, že pokud chce aplikace získat data o přečtených/nepřečtených článcích, zavolá nejdříve metodu LoadUserInfo, které předá jako parametr UserID používaný v cookies pro identifikaci uživatele.

Následně může aplikace zjišťovat, jestli je daný příspěvek (instance třídy ForumMessage) přečtený pomocí funkce IsRead. Ta vrací true, nebo false na základě dat získaných z LoadUserInfo.

Poté, co aplikace zjistí všechny uživatelsky specifické informace o příspěvcích, zavolá UnloadUserInfo, která zajistí uvolnění paměti v datovém adaptéru a připraví jej na přepnutí na dalšího uživatele.

Metodu MarkAsRead je možné volat kdykoliv, tzn. i po uzavření daného uživatele pomocí UnloadUserInfo. Tato metoda na základě zadaného UserID a příspěvku označí daný záznam jako přečtený nebo nepřečtený.

Podívejme se nyní na samotnou implementaci v OleDbForumDataAdapteru. V databázi jsem vytvořil novou tabulku ForumMessageRead podle následujícího schématu:

Pokud existuje záznam v této tabulce s odpovídajícím UserID a ForumMessageID, znamená to, že korespondující uživatel přečetl daný příspěvek. Rozšíření třídy OleDbForumDataAdapter tedy bude vypadat:

private string _userID = null;
private ArrayList _userInfo;

public void LoadUserInfo(string userID)
{
if(_userID==userID)return;
_userInfo  = new ArrayList();

if (_userID!=null) throw(new Exception("Another user`s info is currently loaded. Unload info by using UnloadUserInfo() first. Remark - this error shouldn`t happen. If it does, number of connected user exceeted limitation of the component. Please report this error to author."));

if (!loaded) throw(new Exception("You have to load Forum first by calling Load() method."));

_userID = userID;
_userInfo.Clear();

OleDbDataReader reader = Tools.ExecuteSQLReader("SELECT ForumMessageID FROM ForumMessageRead WHERE UserID=`"+userID+"`;");

while(reader.Read())
_userInfo.Add(IDToObject[reader["ForumMessageID"]]);
reader.Close();
}

public void UnloadUserInfo()
{
_userID = null;
_userInfo =null;
}

public bool IsRead(ForumMessage m)
{
if (_userID==null) throw(new Exception("No user active - please load user`s infor by calling LoadUserInfo prior to calling IsRead."));
return _userInfo.Contains(m);
}

public void MarkAsRead(ForumMessage m, bool read, string userID)
{
if (!loaded) throw(new Exception("You have to load Forum first by calling Load() method."));
if(read) // add message to "read" list
{
if(Tools.ExecuteSQLScalar("SELECT ID FROM ForumMessageRead WHERE UserID=`"+userID+"` AND ForumMessageID="+((int)ObjectToID[m])+";") == null)
Tools.ExecuteSQLNonQuery("INSERT INTO ForumMessageRead(UserID,ForumMessageID) VALUES(`"+userID+"`,"+((int)ObjectToID[m])+");");
}
else
Tools.ExecuteSQLNonQuery("DELETE FROM ForumMessageRead WHERE UserID=`"+userID+"` AND ForumMessageID="+((int)ObjectToID[m])+";");
}

Nakonec zbývá ještě úprava kódu samotné komponenty tak, aby mohla nových funkcí datového adaptéru využít. Do třídy ForumControl byla přidána metoda, která reaguje na ExpandedChanged událost u příspěvku. Pokud byl příspěvek rozbalen, můžeme ho považovat za přečtený a zavoláme funkci MarkAsRead:

private void msgExpandedChanged(object sender, EventArgs e)
{
ForumControlLinkedItem i =(ForumControlLinkedItem) sender;
RaiseEvent(ExpandedChanged,sender);
if(this.Page.Request.Cookies["UserID"]!=null)
_adapter.MarkAsRead(i.Message,true,this.Page.Request.Cookies["UserID"].Value);
i.Read=true;
}

Jak to celé funguje se můžete podívat zde.

Přístup uvedený zde umožňuje pozdější implementaci dalších uživatelsky specifických dat. Ku příkladu by bylo velice jednoduché přidat možnost sledovat daný příspěvek – označit si jej např. jinou barvou pro zapamatování.

Na závěr tohoto seriálu bych se chtěl zhodnotil komponentu jako celek. Podařilo se v podstatě implementovat věci, které jsme si určili jako požadavky na začátku seriálu. Některé věci jako notifikace pomocí e-mailů se sem už z časového důvodu nevešly. Počítám ale s dalším rozvíjením komponenty do budoucna nezávisle na tomto seriálu, takže pro zájemce doporučuji sledovat projektovou stránku na SourceForge.

Váš názor Další článek: Ceny pamětí strmě klesají

Témata článku: , , , , , , , , , , , , , , ,