Sep.19

ASP.NET Page refresh afvangen

Bij het bouwen van ASP.NET webapplicaties is het gebruik van de browser “back”-knop niet aan te raden. In het gunstigste geval treden er geen problemen op maar in andere gevallen zijn de problemen die op kunnen treden vervelend. Neem bijvoorbeeld het dubbel toevoegen van gegevens. Een gebruiker is niet zomaar ander gedrag aan te leren dus zal je als programmeur een oplossing moeten zoeken voor dit probleem. Er bestaan oplossingen voor dit probleem en 1 daarvan is de volgende.

Wat gebeurt er eigenlijk.

  1. Een gebruiker opent een pagina in de webbrowser, laten we zeggen inschrijven.aspx. Op deze pagina kunnen gegevens worden opgeslagen. (Roundtrip 1)
  2. De gebruiker voert zijn persoonsgegevens in en drukt op de opslaan knop. De pagina wordt gepost naar de server. Op de server pagina wordt het click event van de opslaan knop afgehandeld. De gegevens worden met behulp van een insert query in de database weggeschreven. Vervolgens wordt een specifieke view op de inschrijven pagina getoond waar bij de gebruiker wordt geinformeerd over het feit dat de gegevens succesvol zijn opgeslagen. Daarbij wordt een link getoond waarmee de gebruiker terug kan keren naar de homepage, home.aspx. (Roundtrip 2)
  3. De gebruiker klikt op de link en de homepage wordt getoond. (Roundtrip 3)

Roundtrips.jpg
Maar..
De gebruiker bedenkt zich, hij heeft de tekst niet goed gelezen en kiest de back button.

en dan beginnen de problemen…

De browser zal een post uitvoeren met de gegevens die zijn ingevoerd op bij inschrijving. Het click event opslaan wordt opnieuw uitgevoerd en de gegevens worden middels insert opnieuw in de database geschreven. šŸ™ .. dat willen we natuurlijk niet. Het zou mooi zijn als op dat moment duidelijk is dat een refresh wordt uitgevoerd. Ofwel een request dat al een keer eerder is uitgevoerd door de gebruiker.

Als eerste dient er een base page te worden aangemaakt die een property IsRefreshed heeft. Met behulp van deze property kunnen we in alle overerfde pages gebruik maken van deze property. De logica die in de base page wordt geimplementeerd is vrij eenvoudig. Bij elke request van de gebruiker maken we een ticket aan. Dit ticket behelst een uniek gegenereerd nummer in dit geval heb ik gekozen voor een Guid, deze zou ten alle tijden uniek moeten zijn.
Roundtrips2.jpg

Wat is een GUID
Voor degenen die niet weten hoe een Guid er uit ziet, hier een voorbeeld:
{56CCB843-F919-4bdf-AC18-823710BEDB3F}
Middels de methode Guid.NewGuid() kan men het .NET framework een nieuwe Guid laten genereren.

Bij response wordt het ticket in de viewstate van de pagina die de gebruiker voorgeschoteld krijgt opgeslagen. Daarnaast wordt de het ticket ook opgeslagen in de gebruikerspecifieke sessie. Deze acties worden uitgevoerd in het PreRender event van de page. Bij postback (request) door de gebruiker (opslaan inschrijving) wordt via de viewstate van de page het ticket teruggegeven. Bij het oproepen van de IsRefreshed property vlag wordt gecontroleerd of het laatste uitgegeven ticket, vastgelegd in de sessie overeenkomt met het ticket in de viewstate van de page. Wordt er dus een oude request uitgevoerd dan zal ook een oud ticket worden teruggegeven en zal dus niet overeenkomen met het laatste ticket in de sessie waarmee dus bekend is dat de page een refresh heeft gehad. Als er een gewone reguliere postback plaatsvind zal het ticket van de page overeenkomen met het ticket in de sessie en dus herkend worden als een niet-refresh.
Roundtrips3.jpg
Of een pagina gerefreshed is wordt overigens alleen gecontroleerd bij een postback. Bij postbacks treden namelijk de eerder genoemde problemen op. De gebruiker moet wel in staat zijn diverse pagina’s meerdere keren te bezoeken waarbij het niet relevant is of deze is gerefreshed.

De code:

Wil je gebruik maken van deze oplossing dan is het enige wat er moet gebeuren dat je webpagina’s niet laten overerven van Page maar van bovenstaande RefreshSupportPage.

Sep.12

Reflection

Wat is reflection?
Reflection maakt het mogelijk dat een programma zijn eigen structuur kan bekijken en zo mogelijk aanpassen.

Reflection is een onderdeel van het .NET framework (via de System.Reflection namespace). Met behulp van reflection kunnen we tijdens runtime programma-klassen bekijken, objecten instantieren, waarden wijzigen etc…

Het nut van reflection is bijvoorbeeld dat er gemakkelijker generieke code kan worden geschreven. Met behulp van reflection kan bijv. voor onderstaande of soortgelijke klassen SQL queries worden opgebouwd die de alle noodzakelijke communicatie naar een willekeurig DBMS mogelijk maken. Dit hoeft dan niet meer per klasse te gebeuren. NHibernate is een goed voorbeeld van gebruik van o.a. reflection.

Aan de hand van onderstaande klasse wil ik laten zien hoe het een ander met behulp van reflection gedaan kan worden.
Persoon.cs

De System.Type klasse is zo’n beetje de belangrijkste bij het gebruik van reflection. Om aan een System.Type instantie te komen kan op een instantie van een klasse (object) de .GetType() methode worden aangeroepen. Is er geen instantie beschikbaar dan kan doormiddel van typeof(..) functie het System.Type van een bepaald type worden opgevraagd.

Nu een System.Type ter beschikking staat kunnen gegevens worden opgevraagd, bijvoorbeeld:

resulteert in:

Name : Persoon
FullName : snoei.net.test.Persoon
Module.FullyQualifiedName : C:WINDOWSMicrosoft.NETFrameworkv2.0.50727Temporary ASP.NET Filesprostat124b611274f8497bApp_Code.jtzqqm81.dll
Namespace : snoei.net.test
UnderlayingSystemType : snoei.net.test.Persoon

Properties
De properties van de Persoon klasse kunnen alsvolgt worden uitgevraagd:

Deze retourneert een array met PropertyInfo objecten. Het PropertyInfo object beschrijft een property.
Met de volgende code kunnen we propertyinformatie ophalen:

Het resultaat:

Middels het PropertyInfo object is het mogelijk propertygegevens te lezen en te schrijven voor een bepaalde Persoon instantie.

  • Op regel 1-4 wordt een Persoon object gecreeerd.
  • Op regel 5 vragen wordt het System.Type opgevraagd van het type Persoon.

    zou in hetzelfde resulteren.

  • Op regel 6 wordt het PropertyInfo object opgehaald voor de property “Voornaam” van het System.Type Persoon (PersoonType).
  • Op regel 7 wordt er voor de instantie p van Persoon de waarde van de Voornaam property opgevraagd.
  • Op regel 9 wordt er een waarde gezet in de Voornaam property van object p.
  • Dit resulteert in het volgende:

    Methoden
    Naast het uitvragen van properties kunnen ook methodes worden uitgevoerd. Doormiddel van de functie PersoonType.GetMethods() kunnen we gegevens van alle methoden opvragen. Deze functie retourneert een array van MethodInfo objecten.
    Het persoon object heeft een methode “GetFullname”. Met behulp van de volgende code roepen we deze methode aan:

    Met behulp van de Invoke functie kan de methode worden aangeroepen. Het betrokken object moet worden meegegeven in het geval het niet om een static method gaat. Gaat het om een static method dan wordt dan ziet de invoke er alsvolgt uit:

    Aangezien de methode geen parameters heeft wordt een lege object array meegegeven. Mochten de methode parameters bevatten dan kunnen de mee te geven waarden (in de juiste volgorde) in de object array worden geplaatst. Stel dat we een GetFullname methode hebben die een string verwacht waarmee titel kan worden gezet dan ziet de code er alsvolgt uit:

    Instantiatie
    Er is uiteraard nog veel meer mogelijk met reflection. Maar voor nu wil ik alleen nog even laten zien hoe er een instantie van een klasse kan worden gemaakt via reflection. Dus het maken van een instantie van de Persoon klasse in runtime:

    Conclusie
    Dat is reflection in het kort. In veel gevallen kan het gebruik van reflection zaken generieker maken en maakt oplossingen met minder code mogelijk. Reflection is een redelijk alternatief voor codegeneratie. Dat wil echter niet zeggen dat codegeneratie in alle gevallen vervangen kan worden door reflection te gebruiken. Een nadeel van reflection is het ten koste gaat van enige performance. De performance is echter voor veel toepassingen ruim toereikend en dan hoeft het geen belemmering te zijn.