michielpost.nl

Snel en eenvoudig een data-editing UI

Publicatie in .Net Magazine, juni 2008

Download als PDF

Dynamic Data in ASP.Net 3.5 Extensions CTP Bij het ontwikkelen van webapplicaties gaat nog altijd veel tijd zitten in het maken van lijst- en detailpagina's. Zeker als je voor tientallen tabellen een lijst- en detailpagina moet maken, vraag je je toch af: kan dit niet eenvoudiger en sneller? Met de Dynamic Data Controls uit de ASP.Net 3.5 Extensions CTP (versie: december 2007) is het nu heel gemakkelijk om een eenvoudige data-editing applicatie te maken voor de tabellen in je database. De Dynamic Data Controls halen het database schema runtime op en bepalen daaruit welke kolommen getoond worden en welke relaties er zijn. De controls presenteren de data vervolgens op de wijze die de gebruiker normaal zou verwachten. Bij foreign key relaties wordt er bijvoorbeeld automatisch een dropdownlist getoond. Je bespaart dus tijd, omdat je niet meer zelf hoeft te definiëren welke velden er op je pagina staan en hoe je ze uit de database ophaalt of wegschrijft. Natuurlijk kun je er ook voor kiezen om wel je eigen velden te definiëren en de presentatie ervan volledig aan te passen. Hoe dat in zijn werk gaat, laat ik in dit artikel zien.

De Dynamic Data Controls zijn zeer geschikt voor het maken van een beheeromgeving of prototype. In dit artikel gebruik ik als voorbeeld een case waar we een beheeromgeving willen maken voor een website waarop auto's worden verhandeld. Op de beheeromgeving kunnen stamtabellen beheerd worden, in dit geval de tabellen “Automerk” en “Autotype”. Deze tabellen hebben een 1-n relatie. Een uitwerking van deze applicatie is te downloaden, zie daarvoor de referenties aan het einde van dit artikel.
 

Dynamic Data

De Dynamic Data Controls werken op basis van LINQ to SQL. Nadat je een Dynamic Data Project hebt aangemaakt in Visual Studio hoef je alleen maar een LINQ to SQL bestand aan te maken met daarin de tabellen die je wilt kunnen benaderen. In de web.config verwijs je vervolgens naar deze aangemaakte DataContext. Het resultaat is dat je direct een website hebt waarop je alle gegevens kunt bekijken en wijzigen. De tabellen ondersteunen daarnaast standaard de mogelijkheid om te sorteren en te pagineren.

Afbeelding 1) Standaard Dynamic Data pagina voor de Autotype tabel.

Dynamic Data pagina’s zijn verbonden met één tabel aan de hand van overeenkomende namen. Er wordt gekeken of er een tabel in de Linq DataContext is met een naam die gelijk is aan de subdirectory waar de pagina in staat. Het lijst-control dat een lijst laat zien uit de tabel “Autotype” zal dus op een pagina in de subdirectory “Autotype” staan.

De naam van een pagina bepaalt binnen het Dynamic Data Framework de “viewname”. Een viewname refereert naar een template pagina. Standaard worden er drie templates meegeleverd: een lijstpagina, een detailpagina en een pagina met zowel lijst- als detailgegevens. Welke view een bepaalde template aanspreekt kun je configureren in de web.config. Verderop in dit artikel zal ik nog dieper ingaan op de templates.

Code voorbeeld 1

<mappings pattern="~/{table}/{viewName}.aspx">

<add actions="list" viewName="List"

templateFile="ListTemplate.aspx" />

<add actions="details" viewName="Details"

templateFile="DetailsTemplate.aspx" />

</mappings>

 

Zoals je in code voorbeeld 1 ziet, verwijst de view “List” naar het ListTemplate. Als je dus de url “~/Autotype/List.aspx” zou bezoeken, zou je een pagina volgens de ListTemplate krijgen met de gegevens uit de tabel Autotype. Dit is geen fysiek bestaande pagina, maar omdat de opgevraagde URL aan het gedefinieerde “pattern” voldoet, zorgt het Dynamic Data Framework ervoor dat er toch een pagina getoond wordt.

In code voorbeeld 1 zie je ook de mogelijkheid een “action” aan te geven. Er zijn drie ingebouwde acties te definiëren. Dit zijn list, details en de combinatie daarvan (list,detail). Het Dynamic Data Framework gebruikt dit om in een lijstpagina te kunnen bepalen waar de detail-link naar moet verwijzen. Ook wordt het gebruikt om navigatie naar tabellen waar een relatie tussen ligt, mogelijk te maken.

Het is belangrijk om te weten dat voor elke tabel slechts één list-actie en detail-actie kan worden geconfigureerd.

Lijstpagina

Standaard krijgen we dus al een lijst- en detailpagina voor elke tabel. Voor de beheerpagina van de autoverhandel-site willen we een aangepaste lijst maken voor de tabel Autotype. Een aangepaste pagina die dus niet via een template wordt opgebouwd, maar waar we zelf alle elementen op plaatsen. Om dit te kunnen doen, maken we een subdirectory “Autotype” met daarin een nieuwe pagina genaamd “AangepasteLijst.aspx”:
- ~/Autotype/ AangepasteLijst.aspx

Nu moeten we het Dynamic Data Framework nog duidelijk maken dat we deze aangepaste pagina willen gebruiken in plaats van de standaardlijst. Dit kan door de configuratie regel op te nemen uit code voorbeeld 2.

Code voorbeeld 2

<add actions="list" tables="Autotype" viewName="AangepasteLijst"/>

In dit code voorbeeld geven we aan dat we voor de tabel “Autotype” een view “AangepasteLijst” hebben en dat dit de standaard lijstpagina is. De pagina’s voor de overige tabellen blijven ongewijzigd, daarvoor wordt nog steeds het standaard template gebruikt.

Aan de nieuwe lijstpagina voegen we een Dynamic GridView en LinqDataSource toe. Het resultaat is een kant-en-klare tabel die de gegevens uit de database toont. Wat meteen opvalt als we de pagina bekijken, is dat de kolom Automerk niet het ID toont. De daadwerkelijke naam van het merk wordt getoond. De kolomnaam heet zelfs Automerk en niet fkAutomerkID zoals hij eigenlijk in de database heet. Het Dynamic Data Controls Framework ziet dat er een relatie ligt tussen fkAutomerkID in de Autotype tabel en ID in de Automerk tabel. De eerste kolom uit de Automerk tabel die geen key is wordt vervolgens getoond, in dit geval is dat de “Naam”-kolom.

Dit werkt in veel gevallen erg goed. Je kunt je echter voorstellen dat bijvoorbeeld bij een personentabel de achternaam de eerste kolom is. Er kunnen meerdere mensen zijn met dezelfde achternaam, waardoor je er minder aan hebt dat alleen de achternaam getoond wordt. Je kan er dan voor kiezen om een andere kolom te laten tonen. Dit kun je aangeven via het DisplayColumn attribuut op de door Linq gegenereerde klasse.

 Het Dynamic Data Framework probeert de data zo gebruiksvriendelijk mogelijk te tonen. Dit houdt ook in dat de primaire sleutelkolom niet getoond wordt. Deze heeft voor de gebruiker als het goed is toch geen enkele betekenis.

In ons voorbeeld willen we echter de primaire sleutel wel tonen. We kunnen dit doen door zelf de kolommen te definiëren die getoond moeten worden.


Afbeelding 2) De kolommen van het GRID aanpassen.

Afbeelding 2 laat zien hoe je alle kolommen van het grid handmatig kunt definiëren in plaats van ze automatisch te laten genereren.

Het handige van het DynamicGridView is dat deze afgeleid is van het standaard GridView. Alles wat je dus kunt aanpassen aan een standaard GridView, kun je ook aanpassen aan dit DynamicGridView.

 In de aangepaste lijst willen we alle introductiejaren die hoger zijn dan het jaar 2000 laten opvallen met een gele kleur. We gebruiken hiervoor het standaard GridView event RowDataBound. Hierin kunnen de eigenschappen van een rij aangepast worden, afhankelijk van de data die in de rij staat. In code voorbeeld 3 maken we elke rij waarvan het Introductiejaar hoger ligt dan 2000, geel.

Code voorbeeld 3

void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{

if (e.Row.RowType == DataControlRowType.DataRow)

{

Autotype type = (Autotype)e.Row.DataItem;

 

if(type.Introductiejaar > 2000)

e.Row.Cells[2].BackColor = Color.Yellow;

}

}

 

Detail Control

Het DynamicDetailsView laat de gegevens van één rij zien. Het is afgeleid van het DetailsView control dat standaard bij ASP.Net zit. Het control heeft ook de optie om de gegevens te wijzigen. Hiervoor kan het property “AutoGenerateEditButton” op true worden gezet. Op eenzelfde manier ondersteunt dit control ook het verwijderen en het toevoegen van een nieuwe rij.


Afbeelding 3) Verschil tussen standaard DetailsView en DynamicDetailsView.

Op het eerste gezicht lijken de verschillen tussen een standaard DetailsView en een DynamicDetailsView klein. Een standaard DetailsView kan namelijk ook al de gegevens tonen van een rij uit een tabel, een rij wijzigen, toevoegen of verwijderen. Maar het voordeel van het DynamicDetailsView is het automatisch detecteren van de vreemde sleutels waarvoor een dropdownlist wordt getoond. Ook ben je heel erg flexibel in de manier waarop je de gegevens kunt presenteren of wijzigen. Dit is mogelijk doordat het Dynamic Data Framework templates gebruikt voor elk afzonderlijk datatype.

 

Templates

De kracht van de Dynamic Data Controls ligt in het gebruik van templates. Natuurlijk kun je gewoon een masterpage gebruiken voor je Dynamic Data pagina’s, maar tegelijkertijd kan de opmaak voor een Dynamic Data pagina ook in een template gedefinieerd worden. Hierdoor ben je flexibel in de opmaak van je pagina’s. Ook hoef je niet voor elke afzonderlijke tabel een aparte pagina aan te maken, omdat elke tabel standaard beschikbaar is via een template.

Voor het tonen en wijzigen van gegevens uit één veld worden ook templates gebruikt. Er zijn templates voor elk afzonderlijk datatype. De templates zijn UserControls die vrij aan te passen zijn. Er is steeds één template dat gebruikt wordt om de gegevens van een bepaald datatype te tonen en één template voor de wijzig functionaliteit.

Een veld van het type boolean wordt standaard als checkbox getoond. We kunnen dit nu heel eenvoudig wijzigen naar radiobuttons. Hiervoor voegen we aan het “Boolean.ascx” template bestand twee RadioButtons toe en verwijderen we CheckBox. De code die de waarde van de checkbox zet wijzigen we zoals in code voorbeeld 4.

Code voorbeeld 4

object val = DataValue;

if (val != null)

{

RadioButton1.Checked = (bool)val;

RadioButton2.Checked = !(bool)val;

}

 


Dit heeft als resultaat dat boolean-waarden nu in RadioButtons worden gepresenteerd. Het wijzigen van een waarde is onveranderd gebleven. Dat gaat dus nog steeds via een CheckBox.

In het voorbeeld van de tabel Autotype hebben we twee boolean kolommen. We willen eigenlijk alleen de kolom “Stuurbekrachtiging” tonen met radiobuttons en de kolom “ABS” op de oude manier met een checkbox. Dit is mogelijk door je eigen templates te maken. We creëren een “BooleanRadio.ascx” en een “BooleanRadio_edit.ascx” template met daarin de radiobuttons. De originele boolean-templates laten we ongewijzigd.

Via een RenderHint kunnen we nu aangeven dat voor de kolom “Stuurbekrachtiging” altijd het BooleanRadio-template gebruikt moet worden. We maken hiervoor een partial class met de naam van de tabel waar de kolom inzit. In dit geval dus Autotype. Boven de partial class geven we het RenderHint attribuut mee, zoals in afbeelding 4.


Afbeelding 4) RenderHint voor Stuurbekrachtiging.


Afbeelding 5) Verschil in presentatie van boolean velden.

In afbeelding 5 zien we het uiteindelijke resultaat. De twee boolean-kolommen worden nu elk door een ander template afgehandeld. Door het gebruik van templates heb je dus een krachtig mechanisme om de Dynamic Data Controls aan te passen aan je eigen wensen. Je zou de templates bijvoorbeeld kunnen gebruiken om een datetime-kolom via een kalender interface aan te passen of een integer kolom via een slider te laten wijzigen .

 

 

Filters

Het Dynamic Filter control stelt je in staat om de gegevens in een lijst makkelijk te filteren. Er kunnen meerdere filters aan een pagina toegevoegd worden. Elk filter control filtert op één kolom, deze kolom hoeft niet per se zichtbaar te zijn in de lijst op de pagina.

Op dit moment is het alleen mogelijk om een filter te maken op een foreign key kolom en een boolean-veld. Door deze beperking is het met filters dus niet mogelijk een lijst gemakkelijk doorzoekbaar te maken op een vrij tekstveld.

In het voorbeeld van de beheerpagina waar we mee bezig zijn, voegen we aan de pagina met autotype’s een filter control toe om te filteren op merk. Het filter wordt altijd als een dropdownlist getoond. In de configuratie van het filter verwijzen we niet naar de foreign key kolom, maar naar de tabelnaam van de gekoppelde tabel.

Zoals we al eerder hebben gezien, wordt ook hier niet het ID getoond, maar de echte naam van het merk. Om de filtering te laten werken moeten we dit filter koppelen aan de LinqDataSource. Dit kan door een DynamicControlParameter als WhereParameter bij de LinqDataSource op te nemen. Zie daarvoor code voorbeeld 5.

 Code voorbeeld 5

<asp:DynamicFilter runat="server" ID="DynamicFilter" TableName="Autotype" DataField="Automerk" />

<asp:LinqDataSource ID="LijstDataSource" runat="server">

<WhereParameters>

<asp:DynamicControlParameter ControlID="DynamicFilter" />

</WhereParameters>
</asp:LinqDataSource>

Het gevolg is dat de query van de LinqDataSource wordt aangepast. Linq is vervolgens verantwoordelijk voor het ophalen van de gegevens uit de database. De query die Linq op de database zal uitvoeren, haalt alleen de gegevens op die voldoen aan het filter. Er worden niet onnodig veel gegevens uit de database gehaald, waardoor de performance goed blijft.

 

Dynamic Data en LINQ

De Dynamic Data Controls en Linq werken erg goed samen. De controls ,zoals het DynamicGridView en het DynamicDetailsView, hebben als input enkel een LinqDataSource nodig en doen dan hun werk. Ook de filters die je kunt aanmaken werken direct op de LinqDataSource. Deze passen de where-claus aan van de Linq-query.

Het Dynamic Data Framework zorgt ervoor dat wanneer je een pagina opvraagt, de koppeling met de juiste tabel wordt gelegd. Het enige dat er dan eigenlijk gebeurt, is dat het TableName property bij de LinqDataSource gezet wordt.

De Dynamic Data Controls zijn daardoor ook erg bruikbaar op al bestaande pagina’s. Je kunt dan zelf het TableName property bij de LinqDataSource invullen en de Dynamic Data Controls doen de rest.

Conclusie

Zoals je hebt kunnen lezen, is het met het Dynamic Data Framework erg gemakkelijk om snel een data-editing user interface te krijgen.

De Dynamic GridView en DynamicDetailsView die gebruikt worden, zijn afgeleid van bestaande ASP.Net controls. Hierdoor kun je gebruik blijven maken van alle opties die de standaard controls al bieden en is de drempel daartoe laag. Door het gebruik van templates heb je voldoende mogelijkheden om de presentatie van de gegevens aan te passen aan je eigen wensen.

De werking van de controls is goed geïntegreerd met Linq. Er kan een willekeurige Linq datasource worden opgegeven en de controls werken meteen. Een nadeel is dat de controls die update- en insert-mogelijkheden hebben, afhankelijk zijn van het gebruik van viewstate.

Als je echt maatwerk aan het maken bent, kost het waarschijnlijk teveel tijd om de controls helemaal naar je wens te laten werken. Wanneer de eisen echter iets lager zijn, zoals bij een prototype of beheeromgeving, kan er met de Dynamic Data Controls in weinig tijd iets erg moois in elkaar gezet worden.

Referenties

Download voorbeeld applicatie

ASP.Net 3.5 Extensions
http://www.asp.net/downloads/3.5-extensions/
 
Quickstart
http://quickstarts.asp.net/3-5-extensions/dyndata/
 
David Ebbo's Blog
http://blogs.msdn.com/davidebb