Een interactie dictionary in ASP.NET
Inleiding
Binnen veel informatiesystemen zijn data dictionaries niet meer weg te denken. Er zijn redenen genoeg om ze te gebruiken. Denk bijvoorbeeld aan:
- Eenvoudiger beheer omdat meta-gegevens zijn opgeslagen in een repository of relationele database;
- Releases zijn eenvoudiger omdat er geen web- of windows-clients meer hoeven te worden aangepast en uitgerold;
- Er kan maximaal gebruik gemaakt worden van functionaliteit in de repository of de relationele database; denk aan constraints, indexen en referentiële integriteit;
- Er zijn vele hulpmiddelen om gegevens uit een database of repository te benaderen zoals rapportage-tools e.d.;
- Het is mogelijk voor gebruikers zonder kennis van HTML en databases om de gegevens in de data dictionary te beheren als er een beheertool aanwezig is voor de data dictionary;
- Gegevens uit de data dictionary kunnen gebruikt worden voor het genereren van documentatie en eventueel testscripts.
Een data dictionary is het begin om met data driven toepassingen aan de slag te gaan. De vervolgstap is snel gemaakt om ook andere zaken in gegevensbestanden op te slaan. In dit artikel wordt hier een voorbeeld van gegeven, namelijk het opslaan van een gebruikersinterface in een data dictionary.
Interactie
Binnen een webapplicatie zijn een aantal interacties te realiseren. Deze interacties hebben meestal een scenario dat doorlopen wordt, bijvoorbeeld:
- Zoek de gegevens op van een entiteit binnen de webapplicatie (zoekactiviteit);
- Bewerk de gegevens van deze entiteit (bewerkactiviteit);
- Sla de gewijzigde gegevens op in de database van de webapplicatie.
In onderstaande afbeeldingen ziet u een voorbeeld van het scenario en een aantal schermvoorbeelden.

Fig. 1: Interactie-scenario

Fig. 2: Zoekactiviteit

Fig. 3: Bewerkactiviteit
Van een sourcecode formulier is geen voorbeeld beschikbaar, omdat dit niet resulteert in een gebruikersinterface, slechts in een interactie met de database.
Dit is slechts één voorbeeld van een interactie binnen een webapplicatie. Er zijn er vele te onderkennen, bijvoorbeeld: “maak een selectie in een formulier en toon vervolgens de gegevens in een rapportformulier”. Een tweede voorbeeld is “toon de gegevens uit een content entiteit en toon dit als HTML pagina aan de gebruikers”. In de voorbeeldtoepassing horend bij dit artikel kunt u een groot aantal voorbeelden van scenario’s vinden.
Datamodel interactie dictionary
Bij het toepassen van een interactie dictionary is het datamodel verreweg het belangrijkst. Begrijpelijk, want we gaan grote delen van de applicatielogica naar de database overbrengen. Een goed datamodel zal ervoor zorgen dat de dictionary voldoende mogelijkheden biedt voor het onderscheiden van de verschillende entiteiten. In de voorbeeldapplicatie zijn de entiteiten webcontrol en webform de kernentiteiten.
In figuur 4 ziet u een schets van het datamodel.

Fig. 4: Vereenvoudigd datamodel
In het datamodel is te zien dat een webapplicatie uit meerdere webformulieren bestaat. Een webformulier is van een bepaald webformtype. Op basis van het webformtype zal de webapplicatie de gegevens uit het formulier op een bepaalde manier tonen.
In onderstaande tabel staat een aantal webformtypes inclusief een korte beschrijving van het gedrag.
| Webformtype |
Omschrijving |
| List |
Een lijst met gegevens die getoond worden in tabelvorm waarbij de elementen van een record uit de database in een rij staan. |
| Sourcecode |
Aanroep van een SQL statement dat naar de database gestuurd wordt om de inhoud van één of meerdere tabellen te muteren. |
| Hyperlinks |
Een menuformulier met hyperlinks naar formulieren in de webapplicatie en naar hyperlinks op het web. |
| Input |
Invulformulier waarin controls zijn opgenomen voor het invoeren en muteren van gegevens uit de database. |
| Dialog |
Vergelijkbaar met een invulformulier, echter alleen voor het invullen van keuzeopties om te bepalen welke gegevens uit de database gehaald dienen te worden. |
| Detail |
Een formulier dat de inhoud van één rij uit de database toont in alleen-lezen weergave. |
| Freeform |
Vrije definitie van verschillende webcontrols waarbij geen standaard knoppen worden getoond en waarbij de plaats van de controls zelf bepaald kan worden. |
| ReportDetail |
Detailrapport vergelijkbaar met het detailformulier, echter nu in een rapportage cq. printopmaak. |
| ReportList |
Vergelijkbaar met de list-opmaak, maar nu in rapportage- cq. printopmaak. |
| ReportLabel |
Label is een rapportage-opmaak dat gebruikt kan worden voor etiketten. |
Een webform kan bestaan uit nul of meerdere webcontrols. Ook de webcontrols zijn van een bepaald webcontroltype. In onderstaande de tabel vindt u een deel van de beschikbare webcontrols in een interactie dictionary
| Webcontroltype |
Omschrijving |
| Text |
Eenregelig invulveld, hoogte en breedte kunnen worden ingesteld. |
| TextArea |
Meerregelig invulveld. |
| Select |
Keuzelijst met een lijst van waarden waarvan er één gekozen kan worden. |
| Hyperlink |
Hyperlink naar een ander webformulier of naar een andere website. |
| Label |
Een control die gegevens toont (alleen lezen) op een formulier. |
| Hidden< |
Een verborgen control die wel een waarde bevat maar niet zichtbaar is op het scherm. Wordt meestal gebruikt om id’s vanuit de database op te slaan. |
| CheckBox |
Ja/Nee keuzelijst. |
| Button |
Knop die het formulier valideert en vervolgens naar een ander formulier gaat (meestal een sourcecode formulier). |
| Password |
Zelfde als een text maar de ingetikte letters worden op het scherm getoond als * |
Naast deze entiteiten wordt nog een aantal entiteiten onderscheiden in de dictionary zoals weblevel (voor het niveau waarop een webcontrol wordt getoond), webform en weblogin (gekoppeld aan een weblevel). Hiermee kan men op eenvoudige wijze bepalen welke webcontrols door de ingelogde gebruikers zichtbaar worden. Een aantal entiteiten zoals webmenu en webconditie zijn wel opgenomen in de dictionary maar niet opgenomen in de afbeelding. Dit omdat de afbeelding ter illustratie van het datamodel is opgenomen, niet om een compleet beeld te geven. Zie voor een compleet beeld de voorbeeldtoepassing in combinatie met WebConNext (waarover later meer).
ASP.NET Form Factory
Om de gegevens vanuit de interactie dictionary te kunnen vertalen naar de webapplicatie wordt gebruik gemaakt van een formfactory. Deze factory maakt in het voorbeeld gebruik van de standaard HTML controls en Javascript voor client side validatie en voor het gebruik van verrijkte controls zoals een kalender control en een HTML editor control (tinyMCE).
Het belangrijkste onderdeel van de webapplicatie is één HTML pagina die ervoor zorgt dat op basis van een querystring parameter de juiste pagina gerenderd wordt. In het codevoorbeeld hieronder ziet u de opzet van deze pagina.
<%@ Page Language="VB"
MasterPageFile="~/MasterPage.master"
AutoEventWireup="false"
validateRequest="false"
CodeFile="frmFormManager.aspx.vb"
Inherits="frmFormManager"
title="DLA Ontwerp & Software" %>
ID="Content1"
ContentPlaceHolderID="ContentPlaceHolder1"
Runat="Server">
<%
Dim objFF As New DLAFormfactory.FormFactory()
objFF.SetSessionElements()
Response.Write(_
objFF.MakeForm(Request.QueryString("formid")))
%>
De pagina maakt gebruik van een master page zodat u de logica van de pagina geheel kunt scheiden van de opmaak en eventueel zelf nieuwe pagina’s kunt toevoegen die gebaseerd zijn op de master page die de opmaak bevat.
Verder ziet u hoe de pagina op basis van een MakeForm function de gegevens vanuit de formfactory wegschrijft naar de output-pagina. Hierdoor wordt het mogelijk om de formfactory te vervangen door een andere factory zonder dat hiervoor de ASP-pagina’s aangepast hoeven te worden.
In de MakeForm functie vindt de afhandeling plaats op basis van de webformtypes zoals beschreven in de vorige paragraaf. Dit gebeurt eenvoudigweg door een select case statement dat de webformtypes verwerkt.
Function MakeForm(ByVal strFormId As String) As String
Dim strStatement As String
Dim objSupplier As dlasupplier
Dim objPC As DlaParameterContainer
Dim objDB As DLADatabase
Try
objDB = Me.GetConnection()
strStatement =_
ConfigurationManager.AppSettings("sql_makeform")
strStatement =_
strStatement.Replace("#formid#", strFormId)
objDB.OpenConnection(False)
objSupplier = objDB.ExecuteSupply(strStatement)
objSupplier.Read()
objPC = objSupplier.GetCurrentRow()
objDB.CloseConnection(True)
Me.strNavigateTo =_
objPC.GetItemValue("navigatetoformid")
Me.strType = objPC.GetItemValue("formtype").ToUpper()
Select Case strType
Case "LIST FORM", "DETAIL FORM"
Me.MakeHtmlHeader()
Me.MakeHeader(objPC.GetItemValue("formtitle"))
Me.MakeFormDisplay(strFormId, strType,_
objPC.GetItemValue("formsql"))
Me.MakeHtmlFooter()
Case "SOURCECODE"
If Me.ValidateFormInput(strFormId) = True Then
MakeFormSourcecode(strFormId,_
objPC.GetItemValue("formsql"))
End If
…
End Select
Return Me.objSB.ToString()
Catch ex As Exception
Me.ErrorList += "Bericht" & ex.Message & "
"
Return Me.ErrorList
End Try
End Function
De makeform function begint met het ophalen van de gegevens vanuit de database op basis van het formid. Hierbij zorgen we ervoor dat het bijbehorende SQL-statement uit het web.config file komt. Wilt u zaken aanpassen, dan is dit eenvoudig door dit web.config file aan te passen.
Vervolgens gebruiken we het type van de webform voor verdere afhandeling. De methoden MakeHtmlHeader, MakeHeader en MakeFooter zorgen voor een standaard afhandeling voor het aanmaken van de kop en voet van de pagina; de body van de pagina wordt op basis van het formtype aangemaakt.
In het voorbeeld hieronder is de code te zien hoe een formulier wordt opgemaakt dat de gegevens uit één of meerdere tabellen in de database weergeeft. Hierbij wordt een onderscheid gemaakt tussen een detailformulier dat de gegevens toont in de vorm van een kaart en een lijstformulier dat meerdere records in een tabel toont.
In dit voorbeeld is alleen het detailformulier opgenomen.
Private Sub MakeFormDisplay(_
ByVal strFormid As String,_
ByVal strType As String,_
ByVal strSql As String)
Dim objDB As DLADatabase
Dim objSupplier As DLASupplier
Dim blnFirst As Boolean
Dim objPC As DlaParameterContainer
Dim strValue As String
Dim intTeller As Integer
strSql = Me.ProcessSQL(strSql)
objDB = Me.GetConnection()
objDB.OpenConnection(False)
objSupplier = objDB.ExecuteSupply(strSql)
If strType = "DETAIL FORM" Then
objPC = objSupplier.GetCurrentRow()
objSB.Append(_
"
")
objSB.Append(_
" | " >Omschr | " >Waarde
")
intTeller = 1
While intTeller <= (objPC.GetItemCount)
objSB.Append(_
" | " > " & objPC.GetItemName(intTeller) & "")
strValue = _
objPC.GetItemValue(objPC.GetItemName(intTeller))
If strValue.ToUpper() = "NULL" Then
strValue = " "
End If
objSB.Append(_
" "&_ strValue & " |
" & vbCrLf)
IntTeller = intTeller + 1
End While
objSB.Append("
")
Loop
End If
objDB.CloseConnection(True)
End Sub
In het voorbeeld ziet u dat er gebruik gemaakt wordt van een StringBuilder object. Deze zorgt ervoor dat op een snelle en eenvoudige manier een string opgebouwd wordt waarin de HTML code wordt opgenomen. Door deze stringbuilder aan te maken als instance variabele kan deze in elke methode of functie van de formfactory aangeroepen worden. Dit maakt het eenvoudig om de code te scheiden in verschillende subroutines terwijl men toch een complete HTML string aanmaakt.
Verder wordt er gebruik gemaakt van een Supplier object. Deze supplier kapselt het uitlezen van de gegevens vanuit de database in, waardoor het mogelijk is om ook andere relationele databases en zelfs bestandsformaten te gebruiken. Door dit te combineren met een Parameter Collection is het eenvoudig om op generieke wijze een formulier op te maken. Een bijkomend voordeel van de parametercollecties is dat de functie-aanroepen van de verschillende functies altijd hetzelfde zijn, terwijl de parametercollectie van inhoud kan wijzigen. Hierdoor wordt het mogelijk om de verschillende SQL statements die beheerd worden in de web.config file of de dictionary, te wijzigen zonder dat de source code in de formfactory hoeft te wijzigen.
In het laatste voorbeeld van de formfactory ziet u hoe een webform van het type sourcecode ervoor zorgt dat de inhoud van één of meerdere rijen in een tabel bijgewerkt wordt. Hiervoor is een apart formulier gemaakt omdat het wenselijk kan zijn extra validaties uit te voeren op de ingevulde gegevens in een invulformulier. In het invulformulier kan men valideren op verplicht en op datum- en numerieke waarde. Er zijn echter veelal meerdere validaties die op basis van de database-inhoud kunnen gelden. Denk bijvoorbeeld aan een factuur waarbij de factuurdatum altijd voor de betaaldatum moet liggen. Bij het bijwerken van de factuur in een sourcecode formulier kunt u hierop controleren. Hiertoe is in de dictionary een extra entiteit aanwezig te weten de webcondition. Dit is een conditie die een melding teruggeeft op basis van een SQL statement dat naar de database wordt gestuurd.
Private Sub MakeFormSourcecode(_
ByVal strForm As String, ByVal strSql As String)
Dim objDB As DLADatabase
Dim blnOk As Boolean
Dim strSub As String
Dim strMessage As String
strMessage = ""
strSql = strSql.ToLower()
strSql = Me.ProcessSQL(strSql)
objDB = Me.GetConnection()
objDB.OpenConnection(True)
blnOk = True
blnOk = blnOk And objDB.ExecuteModify(strSql)
objDB.CloseConnection(True)
If blnOk = True Then
HttpContext.Current.Response.Redirect(_
Me.strHtmlForm & "?formid=" & Me.strNavigateTo)
Else
Me.MakeHtmlHeader()
objSB.Append(_
objDB.GetErrorMessage() & strMessage & "
")
objSB.Append(Me.strNoSave & "
")
Me.MakeHtmlFooter()
End If
End Sub
HTML Control builder
Binnen de formfactory worden een aantal builders aangeroepen specifiek voor de interactie dictionary, bijvoorbeeld een emailbuilder en een reportbuilder die zorgen voor de afhandeling van specifieke formulieren. Daarnaast is een builder ontwikkeld voor het aanmaken van webcontrols van het type HTML. Door een builder te gebruiken om de code te scheiden is het in een later stadium mogelijk om builders te maken voor andere types controls, bijvoorbeeld door gebruik te maken van de ASP controls of door controls die AJAX enabled zijn. Omdat de webcontroltypes ook in de database zitten is dit eenvoudig aan te passen. Beperking voor nu is wel dat men tot op heden alle controls in één builder moet plaatsen. Mogelijk dat een toekomstige versie van de formfactory hierin wel voorziet.
In het onderstaande codevoorbeeld wordt een deel van de HTML controlbuilder getoond, waarbij een drietal controls zijn uitgewerkt; de rest van de controls wordt in dit artikel niet behandeld.
Private Function CreateSingleControl(_
ByVal strControlType As String) As String
Dim strValue As String
Dim objSB As New System.Text.StringBuilder()
objSB.Append("