1 MAGAZINE SOFTWARE DEVELOPMENT NETWORK 19 & 20 OKTOBER A.S. SDN CONFERENCE Papendal, Arnhem SCHRIJF NU IN! IN DIT NUMMER O.A.: Azure.Net Services < H...
Aan dit magazine werd meegewerkt door: Maurice de Beijer, Rolf Craenen, Anko Duizer, Marcel van Kalken, Stefan Kamphuis, Marcel Meijer, Mirjam van Olst, Johan Parent, Joop Pecht, Sandra de Ridder, Maarten van Stam, Bob Swart, Marianne van Wanrooij, Rob Willemsen en natuurlijk alle auteurs!
Listings: Zie de website www.sdn.nl voor eventuele source files uit deze uitgave.
Adverteerders Macaw VNU Barnsten/Embarcadero Bergler Microsoft Alladdin Sybase iAnywhere 4DotNet Sogeti Avanade Bridge Incubation Group b.v.
2 20 27 28 36-37 41 45 52 63 71 72
De zomereditie van het SDN-magazine ligt voor je: klaar voor gebruik en je hebt vast voldoende tijd om het tot de laatste letter door te nemen. B.v. omdat je vakantie hebt en na het eten lekker in de tuin van het zonnetje zit te genieten of op het strand zit of op je bootje ronddobbert op het strakblauwe water. Of omdat het op kantoor toch een fractie minder druk is dan een jaar geleden en je die tijd goed kunt gebruiken om je kennis weer helemaal up-todate te brengen. In beide gevallen vind je genoeg van je gading om weer helemaal mee te gaan tellen. Er wordt een groot aantal nieuwe versies, functionaliteiten en zelfs talen voorgesteld. Van SQL Server 2008 worden b.v. de nieuwe HierarchyID en de Sparse columns voorgesteld. De Azure .Net Services worden voorgesteld. Hiermee krijg je een omgeving tot je beschikking voor het draaien van applicaties en het opslaan van gegevens in Microsoft data centra, die zich overal ter wereld kunnen bevinden. Je kunt applicaties maken die je daar laat hosten, maar je kunt er ook voor kiezen bestaande applicaties te verrijken met individuele services die – en let op, daar komt een modern toverwoord - in de “Cloud” draaien. Delphi Prism krijgt ook uitgebreid aandacht. Eerst presenteert Bob Swart nieuwe features als Asyncs, Futures & Parallels, waarmee hij maar wil laten zien dat Delphi for .NET niet langer “achterloopt” op het gebied van .NET features, maar juist nu al gebruik maakt van functionaliteit die nog niet eens (volledig) beschikbaar is in .NET. Een geluid dat ook wel eens gehoord mag worden! En we hebben een artikel van de hand van Cary Jensen waarin hij een introductie van LINQ – de Language Integrated Query – in Delphi Prism geeft, iets dat we in .NET al langer kennen. Nog meer nieuwigheden in een artikel over F#, een nieuwe taal uit de Microsoft-stal. Aardig om te lezen - maar ook wel even wennen! – over een functionele taal als tegenhanger voor de imperatieve talen waar de meesten van ons gewend zullen zijn om mee te werken. Overigens gebiedt de eerlijkheid om te zeggen dat F# eigenlijk van beide walletjes snoept … En last but not least wil ik nog een artikel uit de User Experience hoek vernoemen dat een onderwerp behandelt waar vast ook nog niet iedereen zijn handen aan ‘gebrand’ heeft, nl. hoe je Windows Sidebar Gadgets kunt ontwikkelen die met Silverlight 2.0 werken, incl. het uitrollen daarvan. Al met al genoeg nieuws … en dan hebben we nog een stuk of 8 (!) artikelen onvernoemd gelaten. Niemand hoeft zich dus te vervelen in de zomer van 2009. Wat je in dit magazine ook terugvindt is de blik op de aankomende SDN Conference die 19 & 20 oktober gehouden wordt op Papendal (Arnhem), tegelijk met de DotNetNuke OpenForce '09 Europe conferentie. Je kunt er in dit magazine haast niet om heen: het programma, voor zover bekend op het moment van drukken, wordt gepresenteerd, maar via talloze ‘postits’ proberen we het event en de datum in je geheugen te prenten. Want als je dit jaar dan toch wilt gebruiken om weer helemaal up-to-date te geraken, dan moet je naar de SDN Conference 2009 komen … natuurlijk! Veel leesplezier! Rob Willemsen, [email protected] •
PS: Wellicht ten overvloede vermeld ik nog even dat we in dit magazine de lijn doortrekken die in de vorige editie- de WIT-special: Women In Technology – wel heel expliciet gemaakt is, nl. dat vrouwen ook heel goed in de IT uit de voeten kunnen … of liever artikelen van hun hand kunnen laten zien. We presenteren opnieuw een (flink aantal) artikelen van vrouwelijke hand; niet allemaal, zoals vorige keer, maar we hebben toch weer 4 vrouwelijke auteurs die acte de présence geven!
Adverteren? Informatie over adverteren en de advertentietarieven kunt u vinden op www.sdn.nl onder de rubriek Magazine. magazine voor software development 3
Inhoud 03
Voorwoord
Rob Willemsen
04 05
Inhoudsopgave Azure .Net Services
Jacqueline van der Holst
09
DevTweet
10 14
SDN Conference
Marianne van Wanrooij en Sander Hoogendoorn
The WebHub Way of Thinking
Ann Lynnworth
21
Silverlight en Open Source
Koen Zwikstra
24
HierarchyID in SQL Server 2008
André van ’t Hoog en Arjan Pot
29
FloraHolland Moderniseert Hart IT
Norbert Mimpen
31
Under the Hood: Provider-Based Authorization in DotNetNuke 5.1
Brandon Haynes
34
Delphi Prism: Async, Futures en Parallele Mogelijkheden
Bob Swart
38
Spell Check in SharePoint 2007
Gustavo Velez
42
SDN CONFERENCE 2 DAYS SOFTWARE DEVELOPER CONFERENCE
Fun met Webparts in ASP.Net - Deel 2 User Controls en Webparts
Bert Dingemans
46
Introduction to Language Integrated Query with Delphi Prism: Part 1
Cary Jensen
49
Interesting Things: Creative Plumbing
Sander Hoogendoorn
50
Sparse Columns in SQL Server 2008
Bert Dingemans
53
De Kick Ass Development Reeks
Freek Leemhuis en Maarten Metz
19 & 20 OKTOBER 2009 PAPENDAL, ARNHEM
57 58
Agenda ASP.NET onder de Motorkap: Ben jij al AJAX supporter?
Michiel van Otegem
59
Requirements Management voor Software Architecten
Marion Verwey en Nienke van de Brink
64
F#: Een Functionele Oplossing binnen een Imperatieve Wereld
Laurens Ruijtenberg
67
Windows Sidebar Gadgets Ontwikkelen met Silverlight 2.0
Fons Sonnemans
.NET C#
Jacqueline van der Holst
Azure .Net Services De Microsoft .Net Service Bus en de Microsoft Access Control Service gecombineerd Afgelopen jaar kondigde Ray Ozzie op de Professional Developer Conference het Windows Azure Services Platform aan. Het Azure Services Platform is een omgeving voor het draaien van applicaties en het opslaan van gegevens in Microsoft data centra, die zich overal ter wereld bevinden. Bedrijven kunnen ervoor kiezen om daar nieuwe applicaties te hosten, maar je kan er ook voor kiezen bestaande applicaties te verrijken met individuele services die in de “Cloud” draaien. Microsoft .Net services bestaan uit een set services voor developers en een SDK om .NET applicaties in de “Cloud” te kunnen draaien. Je kunt aan .Net services denken als een nieuw .Net framework maar dan specifiek voor dit type applicaties. Dit framework bestaat uit services, die gebaseerd zijn op standaard protocollen, zodat elk service platform erop kan aanhaken. Denk daarbij aan SOAP, REST en WS* technieken. Wanneer je als developer gewend bent om te werken met Windows Workflow Foundation en Windows Communication Foundation, dan zul je het niet moeilijk vinden om met de .Net services SDK te werken. Wil je er direct mee aan de slag? Je kunt dan de SDK downloaden van http://www.microsoft.com/azure en een code aanvragen om je eigen solution aan te kunnen maken. Op dit moment bestaan de .Net services uit een drietal services voor het regelen van connectiviteit, toegang en workflow, te weten: • Microsoft .Net Service Bus: een netwerk infrastructuur voor het verbinden van applicaties over het Internet; • Microsoft .Net Access Control Service: op claims gebaseerde toegang waarbij het kan werken met identity providers als Active Directory en Windows Live ID; • Microsoft .Net Workflow Service: een infrastructuur voor het managen en hosten van workflows.
Figuur 1 Om een applicatie in de “Cloud” te laten draaien moet je een nieuwe Azure solution aanmaken op http://www.microsoft.com/Azure. Voor dit artikel heb ik de solution AzureExample aangemaakt (zie figuur 2).
Dit artikel beschrijft de Microsoft .Net Service Bus in combinatie met de Microsoft .Net Access Control Service. Microsoft .Net Service Bus In de praktijk zien we dat het nog steeds een uitdaging is om applicaties met elkaar te verbinden. Om dit mogelijk te maken gebruiken organisaties een Enterprise Service Bus (ESB). De Microsoft .Net Service Bus heeft een gelijkwaardige architectuur. Het verschil zit hem in het blikveld en de probleemstelling. Immers de .Net Service Bus moet op een globaal internet niveau kunnen draaien en dat ook nog eens met hoge inzet. Met de Microsoft .Net Service Bus kun je je huidige ESB verbinden met services die in de “Cloud” draaien, maar je kunt de .Net Service Bus ook inzetten om applicaties met elkaar te verbinden die zich op verschillende fysieke locaties achter een firewall bevinden. Figuur 2 Architectuur Om beter te kunnen begrijpen hoe de Microsoft .Net Service Bus werkt, is de architectuur ervan te zien in figuur 1. Hieronder worden verschillende onderdelen van de architectuur besproken aan de hand van een listing.
In het voorbeeld gebruiken we een client project en een service project. Listing 1 toont het service contract en een operatie die twee integers verwacht. Listing 2 definieert de service die twee getallen met elkaar vermenigvuldigt. Hieronder wordt besproken hoe we deze
magazine voor software development 5
.NET C# service via de servicebus beschikbaar gaan maken en hoe we hem kunnen gaan gebruiken. namespace Services { [ServiceContract (Name="SampleContract", Namespace= "http://azureexample.microsoft.com/ServiceModel/Relay") ] interface ISampleContract { [OperationContract] int Sample(int getal, int multiplier); } }
Listing1 namespace Services { [ServiceBehavior(Name="SampleService", Namespace= "http://azureexample.microsoft.com/ServiceModel/Relay/") ] public class SampleService: ISampleContract { public int Sample(int getal, int multiplier) { return getal * multiplier; } } }
Listing2
//Definieer credentials. Username en //password van de solution. TransportClientEndpointBehavior userNamePasswordServiceBusCredential = new TransportClientEndpointBehavior(); userNamePasswordServiceBusCredential.CredentialType = TransportClientCredentialType.UserNamePassword; userNamePasswordServiceBusCredential.Credentials. UserName.UserName = solutionName; userNamePasswordServiceBusCredential.Credentials. UserName.Password = solutionPassword; //Endpoint adres Uri address = new Uri( String.Format("sb://{0}/services/{1}/SampleService/", ServiceBusEnvironment.DefaultRelayHostName, "AzureExample")); ServiceHost host = new ServiceHost(typeof(SampleService), address); // Voeg de credentials aan alle endpoints toe foreach (ServiceEndpoint endpoint in host.Description.Endpoints) { endpoint.Behaviors.Add (userNamePasswordServiceBusCredential); } // Open de host host.Open();
Naamgeving Laten we allereerst eens naar de naamgevingstrategie kijken. De basis van het naamgevingsysteem is oplosbaar met traditionele DNS technieken. Het is aan de eigenaar van de solution om de hiërarchische namespace te controleren. De manier waarop je het moet indelen is als volgt: [scheme]://[solution-naming-scope]/[name]/[name]
De naamgeving van de URI is onder te verdelen in een drietal onderdelen. De CTP release van november ondersteunt de schemes SB en HTTP, waarbij je HTTP gebruikt voor de op HTTP gebaseerde endpoints en SB voor alles wat anders is (bijvoorbeeld TCP). De solution-naming-scope beschrijft de solution binnen het Azure platform en bestaat uit
Listing 3 In listing 3 is te zien hoe we een endpoint adres aanmaken voor onze SampleService. Voor authenticatie met de Service Bus worden de username en het password gebruikt die je hebt opgegeven bij het aanmaken van de solution in Azure. Op basis van de configuratie (listing 4) maken we een host aan. De credentials worden aan elk endpoint toegevoegd. In dit geval is dat er maar een, namelijk SampleService. Vervolgens wordt de host geopend (figuur 3).
servicebus.windows.net/services/[solution]
Als laatste definieer je de endpoint namen. Alles wat na [solution] komt in het pad is onderdeel van de door de gebruiker gedefinieerde namespace. Je kunt een onbeperkte hoeveelheid endpoint namen opgeven door een hiërarchie van namen achter [solution] te plakken in het pad. Voor het listing in dit artikel ziet de uri er als volgt uit: sb://servicebus.windows.net/services/AzureExample/ SampleService
Twee verschillende services binnen de solution hoeven niet op dezelfde machine of in hetzelfde netwerk te worden gehost. Het is de taak van de Service Bus om te bepalen waar de endpoints zich bevinden. Service Registry Naast de naming convention biedt de Service Bus een Service Registry voor het publiceren en ontdekken van service endpoints binnen een solution. Dit kan zowel automatisch als handmatig. Het handigst is om dit automatisch door de Service Bus te laten regelen.
Listing 4 De client applicatie is op een zelfde wijze geconfigureerd (listing 5) als de service applicatie. <system.serviceModel> <endpoint name="RelayEndpoint" contract="AzureSample.ISampleContract" binding="netTcpRelayBinding" bindingConfiguration="default" address="" />
• Connection-oriented bi-directional socket communication. Wanneer je gebruik maakt van de Relay Service leg je de luisterverantwoordelijkheid bij de Relay Service. Om gebruik te kunnen maken van de Relay Service leg je een connectie tussen je lokale service en de Relay Service. Binnen .Net doe je dit door het WCF programmeermodel en -bindings die in de SDK zitten te gebruiken. Deze bindings worden dan omgezet naar nieuwe transport bindings om WCF listeners te maken die met de Relay Service integreren. Voor de Relay Service is het nodig dat een aantal outbound poorten open staan, te weten 808 (TCP connecties), 828 (TCP/SSL connecties) en poorten 818 en 819 voor geavanceerde TCP connecties. Wanneer je binnen een omgeving werkt die alle outbound connecties blokkeert, behalve diegene die gebruikt worden door HTTP/HTTPS, dan kun je een speciale HTTP gebaseerde verbindingsoptie gebruiken. De listing gebruikt de binding NetTCPRelayBinding aangezien Microsoft deze binding als default aanraadt. Hij is zeer efficiënt en maakt een publiekelijk te bereiken endpoint aan in de Relay Service. Client applicaties moeten geconfigureerd worden met dezelfde binding. Deze binding ondersteunt drie soorten verbindingen: • Relay: alle communicatie gaat via de relay service; • Hybride: de initiële communicatie gaat via de relay service waarna zender en ontvanger een directie communicatie met elkaar hebben. Deze communicatie wordt wel gecoördineerd door de relay service; • Direct: zonder relay service. In de CTP werkt dit nog hetzelfde als de hybride connectie. Naast de binding getoond in bovenstaand voorbeeld, zijn er nog de volgende mogelijkheden:
Listing 5 Relay In het hart van de Service Bus bevindt zich de Relay Service (figuur 4) om door firewalls en NAT heen te kunnen communiceren. De relay maakt het mogelijk voor een zender om met een ontvanger te communiceren door middel van een adres. In het code voorbeeld is dat [sb://servicebus.microsoft.net/services/AzureExample/ SampleService]
Standaard WCF Binding
Vergelijkbare Relay Binding
BasicHttpBinding
BasicHttpRelayBinding
WebHttpBinding
WebHttpRelayBinding
WSHttpBinding
WSHttpRelayBinding
WS2007HttpBinding
WS2007HttpRelayBinding
WSHttpContextBinding
WSHttpRelayContextBinding
WS2007HttpFederationBinding
WS2007HttpRelayFederationBinding
NetTcpBinding
NetTcpRelayBinding
NetTcpContextBinding
NetTcpRelayContextBinding
N/A
NetOnewayRelayBinding
N/A
NetEventRelayBinding
Tabel 1: Relay bindings
Figuur 4 De ontvanger is door middel van een naar buiten openstaande poort verbonden met de Relay Service en specificeert het adres waarnaar hij wil luisteren. De zender kan dan een bericht verzenden door hetzelfde adres te specificeren. Vervolgens kan de Relay Service het bericht doorgeven aan de ontvanger die op hetzelfde adres zit. De ontvanger hoeft geen inbound poorten open te hebben staan. De Relay Service ondersteunt de volgende messaging patronen: • One-way • Request/response • Peer-to-peer • Publish/subscribe scenarios
De relay bindings werken ongeveer identiek aan de WCF bindings. Het is een uitdaging om te weten welke binding je exact nodig hebt. De NetTcpRelayBinding wordt als default aangeraden, maar je zou je kunnen voorstellen dat deze wellicht niet zou volstaan omdat outbound TCP poorten nodig zijn voor de luisteraar. Je zou dan bijvoorbeeld de NetOnewayRelayBinding kunnen gebruiken. Deze is wat agressiever en maakt gebruik van HTTP poorten 80/443 die meestal open staan. Een Relay Service werkt als een omgeving die compleet los staat van de lokale omgeving, wat het mogelijk maakt om ongewild verkeer tegen te houden voordat het bij je service-omgeving uitkomt. Het verbergt ook alle informatie over de netwerklocatie. Het idee van een Relay Service is niet nieuw. Ook Groove gebruikt een Relay Service voor het uitwisselen van documenten. Authenticatie en autorisatie De Microsoft .Net Service Bus vraagt van elke ontvanger om geauthenticeerd en geautoriseerd te zijn voor een specifieke URI,
magazine voor software development 7
.NET C# voordat het een listener maakt voor die ontvanger. Dit geldt ook voor clients die willen dat de Service Bus berichten voor hen relayt, al kun je dat voor de client wel uitzetten wanneer je je endpoints configureert. Authenticatie kun je laten regelen door de Access Control Service. Vervolgens is het dan aan de Access Control Service om de claims aan de .Net Service Bus te produceren voor autorisatie. Het verschil tussen authenticatie en autorisatie zit hem in wie je bent en wat je mag doen. De Service Bus is zo ontworpen dat het de Access Control Service vertrouwt. De Service Bus zoekt naar claims in de “security tokens” die meegegeven worden door zenders en ontvangers. Er zijn maar twee claims waarnaar gezocht wordt, namelijk #send en #listen. Wanneer die gevonden zijn en er is niet gerommeld met het securitytoken dan wordt toegang gegeven. Wanneer een zender of ontvanger met solution credentials of met een security-token op de proppen komt dan maakt de .Net Access Control Service een rules gedreven beslissing of een autorisatie-token wordt afgegeven aan in dit geval de Microsoft .Net Service bus. De Access Control Service signt en encrypt het security-token zodat de claims alleen gelezen kunnen worden door de .Net Service Bus. Alleen de Service Bus kan de informatie decrypten en lezen. Wanneer het bericht richting de ontvanger gaat wordt het autorisatie-token verwijderd. Dit heeft de ontvanger immers niet nodig. De listing maakt gebruik van een managed card van de Security Token Service, welke bekend staat als Microsoft Identity Lab (https://ipsts.federatedidentity.net/MgmtConsole/Default.aspx), waar een account is aangemaakt. Daarnaast moeten we voor de Access Control Service nog wat aanpassingen maken aan de solution in Azure (http://portal.ex.azure.microsoft.com/default.aspx). Allereerst moeten we de scope toevoegen aan de solution, waarbij je kiest voor de Service Bus. Vervolgens klik je op identity issuer en voeg je https://ipsts.federatedidentity.net toe. Binnen het lab is een account aangemaakt onder de groep Domain Users. Daarom wordt ook een claim type toegevoegd met de naam Group en de waarde Domain Users. Wanneer je dan naar Rules gaat dan zie je dat er al een Send en Listen rule is aangemaakt. Daar maak je nog een extra rule aan om aan te geven dat de receiver tot de groep Domain Users moet behoren. De servicebus authentiseert in listing 6 de client door Windows Cardspace informatie te geven. Hier kun je aangeven, dat je verwacht dat het token een claimtype group heeft. Vervolgens wordt een channel geopend en kan de service geconsumeerd worden. Tijdens het draaien van de code wordt om je credentials gevraagd (figuur 5).
string serviceUserName = Console.ReadLine(); //Adres Uri serviceUri = new Uri( String.Format("sb://{0}/services/{1}/SampleService/", ServiceBusEnvironment.DefaultRelayHostName, serviceUserName)); //Federatie via cardspace TransportClientEndpointBehavior behavior = new TransportClientEndpointBehavior(); behavior.CredentialType = TransportClientCredentialType.FederationViaCardSpace; behavior.Credentials.FederationViaCardSpace. ClaimTypeRequirements.Add(new ClaimTypeRequirement ("http://ipsts.federatedidentity.net/group")); EndpointAddress address = new EndpointAddress(serviceUri); //Channel ChannelFactory channelFactory = new ChannelFactory("RelayEndpoint", new EndpointAddress(serviceUri)); channelFactory.Endpoint.Behaviors.Add(behavior); ISampleChannel channel = channelFactory.CreateChannel(); channel.Open();
Listing 6 Conclusie De .Net Service Bus is een veilige, op standaarden gebaseerde manier van het verbinden van applicaties over het internet. .Net Developers kunnen hun voordeel doen met de Service Bus door simpelweg te kiezen uit een geheel nieuwe set WCF-bindings. De rest van de WCF-code blijft eigenlijk hetzelfde. Op security vlak verbergt de Service Bus alle informatie over de netwerklocatie en biedt het een omgeving die compleet los staat van de lokale omgeving. Op deze manier kunnen kwaadwillenden door de Service Bus worden gestopt voordat het te laat is. Door de Microsoft .Net Service Bus en de Microsoft Access Control Service te combineren is het mogelijk om op een makkelijke en veilige manier applicaties met elkaar te verbinden. Zelfs door firewalls heen. Links Je kunt alles vinden over dit onderwerp op http://www.microsoft .com/azure. Bekijk tevens alle presentaties over Azure op http://www.microsoft.com/pdc. •
Jacqueline van der Holst Jacqueline van der Holst (j.van.der. [email protected]) is als consultant werkzaam bij Avanade, een samenwerkingsverband tussen Microsoft en Accenture. Zij houdt zich bezig met onderwerpen als SharePoint, Azure, LINQ, Patterns and practices.
Figuur 5
8
MAGAZINE
DevTweet: Software Development with a wink DevTweet’s zijn de conversaties tussen Marianne van Wanrooij en Sander Hoogendoorn over de (on)zin die IT-ers bezig houdt: Software-ontwikkeling met een knipoog. Regelmatig zullen ook gasten worden uitgenodigd. Volg ze ook op Twitter via @mariannerd en @aahoogendoorn!
Highway to hell? @mariannerd and @aahoogendoorn reviewing code of the boys Listening to Black Sabbath's Master of Reality. @aahoogendoorn Look at you... new avatar! Is that an AC/DC shirt? @mariannerd Howdy partner! It is AC/DC all right. Your avatar new too? Something wrong with the old one? @aahoogendoorn Nope, just a change in scenery! Like the project I'm doing. I'm asked to do a code review. @mariannerd Cool! Whenever I'm asked to do a code review, the client always knows, or at least suspects, something's terribly wrong. @aahoogendoorn Oh yes... We have the suspect... Now it's a case of getting the evidence ... I feel like Columbo.. @aahoogendoorn You know the suspects from the beginning, but you still have to look for the evidence for conviction! @aahoogendoorn So when you are asked to do a code review, what is the first thing you do? @mariannerd The first thing I do? Ask the client what they're hoping to achieve: do they actually want to know how deep the rabbit hole goes? @mariannerd Next, I make a list of all things I should look at during the review. Architecture, domain, data access, layers, extensibility.. @mariannerd Security, authorization, reuse, code copying (especially in VB), way of working, documentation, design, quality of coding.
@mariannerd Haha, nice one. I once audited the enterprise web portal for a very large international company where ALL communication ... @mariannerd between front end and back end went through one single class. Imagine the effect of a single change.. @mariannerd Sometimes, it gets really bad. Code a whole company depends on, which is so bad, that productivity will definitely be below zero soon. @mariannerd Anyway, I love being asked for code reviews. It gives the opportunity to help improve the quality of applications. @mariannerd That is, if they follow up on your advice from your code review. Unfortunately, that is not always the case. @aahoogendoorn Well maybe it's not needed... Or they just want to know the risks and are happy to take them! @aahoogendoorn Which could be the outcome of a code review. It's not good, but far from worse… And it may be fixed with a couple of quick wins. @mariannerd Well yes, that could be the outcome. Maybe I'm to idealistic about writing good code. If it ain't broken, don't fix it. @mariannerd But I just love clean code... @aahoogendoorn .... But even bad code can function.
@aahoogendoorn So far so good!
@mariannerd My favorite example: an ASP.NET web site that was never compiled by the developers. They just ran it from the browser. Great stuff!
@mariannerd Rule #1 in code reviews: more time means more detail.
@mariannerd As a consequence, of the over 40 pages in the application, only 6 compiled (the ones they visited running it in the browser)...
@aahoogendoorn Code Reviews are fun! No deadlines. just looking at somebody else’s code. @aahoogendoorn ... Oh and telling what's wrong with it... I must say this is the first time I get such an assignment. @mariannerd You know, there's so much horrible code out there, we could do code reviews for the rest of our lives. @aahoogendoorn Well, a lot of this stuff is subject to opinion... 100 developers, 100x different code for the same functionality... @mariannerd Yes true, but some code is definitely worse than other code. And believe me, I've seen some bad coding in my time.
@aahoogendoorn LOL "we got a live one here!". This business application compiled at runtime? No test cases, just put it in production! @mariannerd And then things can get pretty messy. Especially when your poor company lives of this software. @aahoogendoorn That is the whole point isn't it… Developers just start coding without a plan, or knowing best practices. @mariannerd Yes, a lot of developers code without having a decent architecture, without patterns, without layers - but with best intentions.
@aahoogendoorn Any examples?
@aahoogendoorn Oh yes, best intentions... The road to hell is paved with good intentions...
@mariannerd Examples of bad code? How many do you need. Check out this old blog post of mine (in Dutch). http://htxt.it/reVB
@mariannerd No stop signs, speed limits. Nobody's gonna slow me down. Highway to hell. AC/DC# ?
@mariannerd Think of SQL statement in web pages, or of a single class that handles 50% of all functionality.
@aahoogendoorn Nope... 4 minutes by Madonna LOL.
@mariannerd Or think of the same business rule implemented multiple times on different locations, in different ways.
@mariannerd Madonna? Girl you need a music review instead of a code review!
@aahoogendoorn One of the things I came across: internal web application: authentication by doing a LDAP query to see if the user exists.
@aahoogendoorn ....I guess even bad music can function. ;-)
@aahoogendoorn Haven't they heard of Windows Authentication in IIS? They didn't even check if the user was enabled or disabled... :-S
magazine voor software development 9
•
.NET (C#, VB, etc.)
•
User eXperience (ASP.Net, Silverlight, Expressions, Flash, etc.)
•
Information Worker (MOSS, BizTalk, OBA, etc.)
•
DotNetNuke (OpenForce Europe ’09 Conference)
•
Delphi
•
Core Systems
•
Databases
•
Architecture
•
General topics
2 DAYS SOFTWARE DEVELOPMENT CONFERENCE 2009 19 EN 20 OKTOBER 2009 PAPENDAL, ARNHEM
.
19 & 20 OKTOBER A.S. SDN CONFERENCE Papendal, Arnhem
SCHRIJF NU IN!
Also included:
Daarom is er het Software Development Network!
EEN ALL-IN PRIJS Het SDN is een No Nonsense Club, dus geen “verrassingen” achteraf. De prijs is duidelijk en “All-In”, dus mét consumpties in de pauzes, mét lunch (2x) en mét diner op maandagavond. Zelfs het parkeren is inbegrepen! Wie wil overnachten in het hotel van Papendal (een aanrader!) kan bij het aanmelden meteen een hotelkamer boeken. Wees er wel snel bij want het aantal beschikbare kamers is beperkt.
SDN leden betalen € 595,- voor deelname, niet-leden betalen € 695,-
S o f t wa r e D eve l o p m e n t N e t w o r k , Po s t bu s 5 0 6 , 7 1 0 0 A M W i n t e r sw i j k
Te l : 0 5 4 3 - 5 1 8 0 5 8
w w w. s d n . n l
overzicht sprekers en sessies .NET Richard Campbell Stephen Forte
CAN USA
Robert Green
USA
Chad Hower
CYP
Nik Kalyani Beth Massi
USA USA
Ted Neward Dennis van der Stelt Shawn Wildermuth
USA NL USA
Performance Tuning ASP.NET (Understanding Performance Tuning) Building RESTful Applications with Microsoft Tools Data Access Hacks and Shortcuts WCF and WF: Two Great Technologies That Go Well Together An Introduction to Windows Communication Foundation Windows Communication Foundation Security Fundamentals ADO.Net Entity Framework ASP.Net MVC Framework Hands-on Windows Azure: Building a Twitter Clone Future Directions for Microsoft Visual Basic and C# Taking Advantage of LINQ and XML in Office 2007 The Busy .NET Developer's Guide to DSLs in Oslo Velocity Building your first MGrammar Data Validation in Silverlight 3 Why Oslo Matters
Architecture Kent Alstad
CAN
Richard Campbell Udi Dahan
CAN IL
Sander Hoogendoorn
NL
Michael Stiefel
USA
Queuing with Azure The Great Database in the Sky From iPhone to Azure From One Web Server to Two: Making the Leap Intentions & Interfaces - Making Patterns Concrete Avoid a Failed SOA - Business & Autonomous Components to the Rescue Designing High Performance, Persistent Domain Models Modeling services using smart use cases Applying model driven development in developing Silverlight 2.0 applications How To Partition and Layer a Software Application Claims Based Security: What it is and Why it will be in your Future Does the Relational Database Make Sense in the Cloud? Architecting Software as a Service
Delphi Marco Cantù
I
Pawel Glowacki Hadi Hariri
NL SP
Chad Hower
CYP
Cary Jensen
USA
Barry Kelly
USA
Bob Swart
NL
Creating Windows 7 Applications in Delphi Multi-Threading in Delphi Domain Specific Languages in Delphi Delphi Natural Input ASP.NET MVC in Delphi Prism Advanced ASP.NET MVC in Delphi Prism Connecting to .NET with CrossTalk IntraWeb: Then, Now, and Future Introduction to WPF Development with Delphi Prism An Introduction to LINQ in Delphi Prism 9 Thread Synchronization Options in Delphi Compared Delphi Compiler RTTI Enhancements Design of a combined garbage-collection and serialization mechanism Delphi 2010 DataSnap Enhancements
User eXperience Nik Kalyani Don Kiely
USA USA
Joost Nuijten
NL
Klaasjan Tukker
NL
Shawn Wildermuth
USA
MAGAZINE
Raising the UX Bar with ASP.NET MVC and jQuery AJAX 4.0: Rich Internet Applications Come of Age Revenge of the Client: AJAX 4.0 Data Binding De ultieme gebruikerservaring Rich Internet Applications Building rich internet applications with Flash Builder and .NET Interactive video expierences Building Behaviors in Silverlight 3
overzicht sprekers en sessies Information Worker / SharePoint Nick Boumans
NL
Donald Hessing
NL
Beth Massi Dick Moffat Wouter van Vugt
USA CAN NL
Social Networking in SharePoint E-Commerce using SharePoint and Commerce Server 2009 SharePoint and Jquery Integratie Silverlight en SharePoint Building Office Business Applications with Visual Studio Excel Development - Rules of Engagement Localizing SharePoint Solutions Developing SharePoint Workflows
Core Systems Scott Ambler
USA
Martin Knobloch
NL
Ted Neward
USA
RUP: Agile Development Agile Development Workshop Security Advanced The Secure development lifecycle Effective Enterprise Java A Tour of the Open Source Java Community
Database Cary Jensen Don Kiely
USA USA
Dick Moffat
CAN
Advantage Database Server and Delphi: A Perfect Match SQL Server 2008 Security for Developers User-Defined Types and Aggregates in SQL Server 2008 Using Microsoft Access 2010 With SharePoint The new features of Microsoft Access 2010
General Richard Campbell Sander Hoogendoorn Dennis van der Stelt
CAN NL NL
Death of a Web Server: Crisis in Caching Do’s and don’t in implementing and applying extension methods Introduction into the TDD Mantra Making sense of the buzz words
DotNetNuke OpenForce ‘09 Europe Erik van Ballegoij Cathal Connolly
NL UK
Peter Donker
NL
Steve Fabian
USA
Nik Kalyani
USA
Stefan Kamphuis Vicenç Masanas
NL SP
Charles Nurse
CAN
Shaun Walker
USA
Leverage the DotNetNuke framework for your own application DotNetNuke performance tips & tricks DotNetNuke and Dynamic Data Auditing and logging - Who, what and when? Advanced Module Development Extension software protection in DNN WCF and DotNetNuke Windows Workflow and DotNetNuke Templating : User-Controlled Output Advanced Skinning with DotNetNuke 5 Using and Extending the DotNetNuke Widget Framework DotNetNuke 101 for ASP.NET Developers Using MSBuild to automate building and packaging Creating a SiteMap provider for DotNetNuke DotNetNuke in the Cloud DNN and .NET v4 Module Development Chalk and Talk DotNetNuke Localization DotNetNuke Professional DotNetNuke State of the union
DELPHI
Ann Lynnworth
The WebHub Way of Thinking This is part 2 of the article published in magazine 101. How Exactly does a Team Cooperate? The user manual for the WebHub extension for Dreamweaver discusses the topic of team cooperation in great detail, and includes best-practice advice for file organization, naming conventions, and so on (see http://www.href.com/edeliver:whdw). Nonetheless, I can give you an overview here now. We have already talked about the fact that the WHTEKO files are kept separate from the WebHub application. Generally, a graphic design person would be given access to those files, plus the images, css and javascript files. All such files can be easily maintained in version control. Refreshing when files change If the designer changes an image or a CSS file, reloading the web page in the browser should be enough to see the change. If the designer changes a WHTEKO file and wants to see the effect in a web browser (without using Dreamweaver), he or she needs to get the WebHub application to refresh. There are many ways to do this, including using WebHubAdmin on the server, right-clicking and selecting Refresh from the tray menu of the running app on the server, or using a browser to trigger a special page with a refresh command for this purpose. I find that using the browser is usually quickest. The special page would be declared as shown in listing 1, <whpage pageid="remoterefresh"> (~app.refresh~)
Listing 1: A page used to tell the app to reload WHTEKO and config details
When the developer sees the sketch, he or she could realize that only two variables are needed to make that section work for any company. At that point, the sketch would be copied to a droplet, expecting two parameters, as shown in Listing 3: <whdroplet name="drBanner"> <whdoc for="drBanner"> DYN1 – name of image used for the logo DYN2 – company slogan/text Usage: call via web action named 'waBanner'
(~DYN2~)
Listing 3: A droplet, expecting two parameters to fill in variable data and called from Firefox or IE using http://www.mydomain.com/scripts/runisa.dll?appid:remoterefresh
Of course, you might add security to make sure the remoterefresh page was only called from certain ip numbers or only on a restricted subdomain.
The developer could then make a web action to find the data and display it. There are many approaches. Listing 4 shows how it can be done by calling the PARAMS command, which runs a droplet, passing in parameters. uses webApp;
From sketch to actual A designer often needs to put in some placeholder content, before the Delphi programmer can create a perfect web action to generate the content dynamically. WebHub supports this process with a <whsketch_tag>. Listing 2 shows an example, where part of a page banner is built.
procedure TForm1.waBannerExecute(Sender: TObject); var OrgImageFilename: string; OrgSlogan: string; begin // do some database lookup to find these values OrgImageFilename := 'abc.jpg'; OrgSlogan := 'A.B.C. Company, the best since 1885';
Listing 4: Web action component loads data then calls the drBanner droplet, passing two parameters which become available as DYN1 and DYN2)
Listing 2: A sketch, showing sample data in position, for design purposes
14
MAGAZINE
When that was in place, most likely the Delphi programmer would disable the sketch by changing show="yes" to show="no", and adding (~waBanner.execute|~)
DELPHI
Once that was in place, the designer could make further adjustments to the drBanner droplet (listing 3) to add CSS tags, etc., independently from the Delphi programmer. The <whdoc_tag> in listing 2 is optional. We recommend using it whenever droplets expect parameters, so that everyone knows what is required. In some future version of WebHub, we hope to make the web action components more self-documenting, so that they could report their usage automatically. For now, coders should use the <whdoc_tag> or a WebHub comment to annotate tricky sections of the file. Text Substitution Macros WebHub supports unlimited text substitution macros. The restrictions on macros is that each must fit on one line (but that can be a long line). A droplet, by contrast, can include unlimited lines. Listing 5 shows imaginary macros. <whmacros> mcAssetRoot=/files/ mcLegalDir=(~mcAssetRoot~)/misc/legal/ mcTrademarkPic=
Listing 5: Text substitution macros can be defined in any WHTEKO file. You may have already realized that calling a macro is accomplished in W-HTML by putting its name inside parentils: (~mcLegalDir~) Page Settings Internally, WebHub has an object for each page in the application, a TwhPage component. Each page can carry some configuration detail, known as Page Settings. That is basically a TStringList that can be used to write generic code. The following example (listing 6) shows how page settings can be used to enter details which are later used to generate a Google sitemap file. (The Google sitemap feature is included as a standard module in WebHub. It works as-is for sites where every page has its own PageID. It requires modification for sites that use WebHub pages as templates and need Google to a wide range of database-driven content.) <whpage pageid="Login" desc="Customer Login"> <whpagesettings> googlesitemap=include_all_lingvo googlechangefreq=monthly googlepriority=0.7 metarobot=index,nofollow metadescription=Customers may login here. metakeywords=login; interactive; dynamic … <whpage pageid="HomePage" desc="Distribution"> <whpagesettings> googlesitemap=include_all_lingvo googlechangefreq=monthly googlepriority=1.0 metarobot=index,follow metadescription=Full service east-coast distribution metakeywords=courier service; trucking; <whoutput> (~mcDocType2006~) (~PageDesc~) (~drHead~) …
Listing 6: Page settings defined, then used to generate sitemap and shared header tags Application Configuration You may be wondering about custom settings needed for the application as a whole, such as a database name or the path to some utility used during processing. This is handled through the applicationlevel configuration file. In that file, you can have settings which are again basically a stringlist, although they are stored in XML format. Listing 7 shows a few sample settings and how they could be referenced in Delphi and in WebHub-HTML. In XML: <AppSettings> <AppSetting name="DemoGraphic" value="yes" /> <AppSetting name="DBName" value="alias123" /> In PAS: ShowMessage(pWebApp.AppSetting['DemoGraphic']); ShowMessage(pWebApp.AppSetting['DBName']); In WHTEKO: (~AppSetting.DemoGraphic~) (~AppSetting.DBName~)
Listing 7: Application settings in the config file; used in Delphi; used in WHTEKO Dozens of aspects of the application can be configured. Some people like to use Notepad to edit the XML file directly. Beginners will find that the configuration utility, ZMAdmin.exe, is very convenient for exploring and changing settings. Figure 1 shows AppSettings as they look in ZMAdmin.
Fig. 1: ZMAdmin provides the GUI for configuring the application; hints are often more detailed! Live Content in Dreamweaver If the page layout expert on your team uses Dreamweaver, I highly
magazine voor software development 15
DELPHI
recommend that you look through the user guide for the WebHub Extension for Dreamweaver. That manual contains many screen shots showing exactly how it works. Remember, the big difference between WebHub's live content and that from ASP, PHP, etc., is that the WebHub content is generated specifically for Dreamweaver and provides 100% of the HTML for each WebHub expression. Other plug-ins use approximations for many of their dynamic pieces, especially for database content. If you are not a Dreamweaver fan, do not despair! People have built extreme sites using the WHTEKO editor provided with WebHub, or even their favorite text editor.
a different configuration file for each customer. Whatever technique you use, the key is to use macros and droplets for each piece of the design that will be swapped out. If you have an extra-talented CSS person on your team, you might not have to vary much more than the path to the CSS file. If you want to skin your web app for two or more purposes, WebHub will probably be a better product for you than IntraWeb.
Content Management Straight "out of the box," WebHub supports simplistic content management. A WebHub site is easier to maintain than a static web site because one usually shares all the headers and footers across the pages. However, if you want to use a database to store content, and manage users and permissions, you need to build something for that purpose. One of the demos provides a simple example for doing so. One of our customers, www.netcat.com.au, has built a full-featured CMS using WebHub. Another customer, www.webcentre.co.nz, built a CMS specifically for magazine and "news" content, and that is used on the award-winning site, www.scitechdaily.com.
MultiTypeApp; TtpProject MultiTypeApp is the name of a unit which, when included in your DPR instead of Forms, makes your application compile easily as a normal Windows app, a Windows service, or as a console application. Because most WebHub developers eventually deploy their app as a Windows service, that is the default behavior. However by adding the PREVENTGUI compiler directive, you eliminate the as-service feature (and overhead).
Web Apps that look like Delphi Apps Do you want your web application to look and act exactly like an existing Delphi application? WebHub includes a fancy component nicknamed "F2H" meaning "form to html converter". This component can take any Delphi container, such as a form or a groupbox, and convert it to HTML. This content can be used as the basis for any page, such that the surfer looks at something which looks like a Windows application. Whether this is desirable or not, depends on the target market and the type of application. You may know that IntraWeb specializes in this type of web application, and uses AJAX in creative ways to make web applications operate like Delphi applications. There was an excellent presentation about this at CodeRage 2008, and if you missed it, I recommend you find the recorded session. Look for "Rich User Interfaces with IntraWeb" by Olaf Monien at http://conferences.codegear.com/coderage08/sessions. WebHub supports AJAX and includes three general-purpose commands to make AJAX features extremely easy to use. A Delphi developer in Florida has used the WebHub AJAX commands to create a web version of his proprietary application. So if you want your web application to look and act like a Windows application, you certainly can. If you want **all** your web applications to look and act like your Delphi application, IntraWeb will be a better product for you than WebHub.
WebHub developers often build an application with the idea of "skinning" it for multiple customers Apps with Skins; Re-use Would you agree that developers are most profitable when they can build something and then re-use it two or more times? WebHub developers often build an application with the idea of "skinning" it for multiple customers. This can be done in many ways. The simplest is to use an AppSetting to define the values that need to change for each customer, and use
16
MAGAZINE
Techniques inside Delphi There are a few innovations inside Delphi which make WebHub applications unique and fun.
TtpProject is the name of a component which we use to organize and sequence all the CreateForm statements relating to forms and datamodules, plus any initialization. When combining standard WebHub datamodules and forms with custom units, an ordinary DPR can become quite messy, and often only the original developer understands the required sequence. TtpProject has a series of events which offer a place to put the nuts and bolts of project startup that would normally be in the DPR. It includes some features which are especially helpful to WebHub coders and can be ignored in other types of projects. The use of TtpProject is always combined with the use of MultiTypeApp. Both are part of TPack and available with full source. Let's look at one real-world example, keeping in mind two goals: 1. Create objects in the correct order to avoid referencing something which is still nil; 2. Enable easy compilation of the app with zero GUI in case that is needed later.
Fig. 2: Delphi Object Inspector with all the events on TtpProject (in Delphi 7) Each event is called in the order shown. Thus all the datamodules are created FIRST (over 3 steps, as needed for WebHub); then any Init methods are called on those datamodules in order to run code which cannot run inside OnCreate due to interdependencies among modules; then the GUI is created; then the GUI is initialized. Listing 8 shows the code inside those events for an app in production named CSShop, from the DMForCSShopPR.pas unit:
DELPHI
procedure TDMforCSShopPM.ProjMgrDataModulesCreate1( Sender: TtpProject; var ErrorText: String; var Continue: Boolean); begin CreateCoreWebHubDataModule; // includes TwhApplication end; procedure TDMforCSShopPM.ProjMgrDataModulesCreate2( Sender: TtpProject; const SuggestedAppID: String; var ErrorText: String; var Continue: Boolean); begin if SuggestedAppID = '' then begin ErrorText := 'Usage: csshop.exe /ID=shopname'; Continue := False; Exit; end; if AppIDToConfigFilespec(SuggestedAppID) = '' then begin ErrorText := Format( 'WebHub Configuration Error in WHCentralInfo.xml%s' + 'No configuration file for AppID "%s".', [sLineBreak, SuggestedAppID]); Continue := False; Exit; end; with pWebApp do begin AppID := SuggestedAppID; Refresh; // load all config and WHTEKO information end; // provide notice to use while the rest is loaded CoverApp(SuggestedAppID, 2, 'reloading application', False, ACoverPageFilespec); end; procedure TDMforCSShopPM.ProjMgrDataModulesCreate3( Sender: TtpProject; var ErrorText: String; var Continue: Boolean); begin try // uses MultiTypeApp, not Forms Application.CreateForm(TdmCommon, dmCommon); Application.CreateForm(Tdmdb, db); Application.CreateForm(TdmFastTax, dmFastTax); Application.CreateForm(TdmAccount, dmAccount); Application.CreateForm(TdmCart, dmCart); Application.CreateForm(TdmVersionControl, dmVersionControl); Application.CreateForm(TdmPlaceLookup, dmPlaceLookup); Application.CreateForm(TdmAS400, dmAS400); CreateStandardWHModules; // based on app config Application.CreateForm(TdmwhNavigation, dmwhNavigation); Application.CreateForm(TDataModuleProductPhotos, DataModuleProductPhotos); except on E: Exception do begin ErrorText := E.Message; Continue := False; end; end; end; procedure TDMforCSShopPM.ProjMgrDataModulesInit( Sender: TtpProject; var ErrorText: String; var Continue: Boolean); begin try db.Init; dmFastTax.Init; dmAccount.Init; dmCart.Init; dmVersionControl.Init; dmAS400.Init(dmCommon.cn1, dmCommon.tr1); InitCoreWebHubDataModule; // TwhApplication InitStandardWHModules; // based on app-config dmwhNavigation.Init; DataModuleProductPhotos.Init; except on E: Exception do begin
ErrorText := E.Message; Continue := False; end; end; end; procedure TDMforCSShopPM.ProjMgrGUICreate( Sender: TtpProject; const ShouldEnableGUI: Boolean; var ErrorText: String; var Continue: Boolean); begin if ShouldEnableGUI then try Application.CreateForm(TfmWebHubMainForm, fmWebHubMainForm); CreateStandardWHModulesGUI; // based on app-config Application.CreateForm(TfmwhSponsor, fmwhSponsor); Application.CreateForm(TfmMemory, fmMemory); Application.CreateForm(TfmAS400, fmAS400); except on E: Exception do begin ErrorText := E.Message; Continue := False; end; end; end; procedure TDMforCSShopPM.ProjMgrGUIInit(Sender: TtpProject; const ShouldEnableGUI: Boolean; var ErrorText: String; var Continue: Boolean); begin if ShouldEnableGUI then try InitCoreWebHubDataModuleGUI; InitStandardWHModulesGUI; fmwhSponsor.Init; Application.MainForm.Caption := pWebApp.AppID; UncoverApp(ACoverPageFilespec); WebMessage(''); // clear splash screen except on E: Exception do begin ErrorText := E.Message; Continue := False; end; end; end; end.
Listing 8: Delphi code linked to the events shown in figure 2 To understand why all this is necessary, you need to think about how objects are created and initialized. Getting the sequence correct is essential. For a WebHub application, you must instantiate a TwhApplication (OnDataModulesCreate1), set its AppID property and refresh it (OnDataModulesCreate2), and then create other non-GUI modules which work once the TwhApplication is ready (OnDataModulesCreate3). After all of those have been instantiated, we do another pass to call an Init procedure on any datamodule which needs to do something special. For example, it's usually better to connect to a database in the Init method, in case one needs to use any properties on other datamodules. The GUI forms are created and initialized completely separately. While the GUI is great during development and testing, it is often undesirable during production. By isolating the CreateForm statements for forms in OnCreateGUI, the whole project can quickly be compiled as a non-GUI edition when needed (by adding the PREVENTGUI directive). It is possible to build WebHub apps without MultiTypeApp or TtpProject. However we recommend using them because the resulting code is more self-documenting.
magazine voor software development 17
DELPHI
Too difficult? Talk to the Wizard! We built a few wizards to help you get your files started. They are accessible inside the Delphi IDE. 1. New AppID - creates the XML and WHTEKO files needed for a new application - do this first! Use the menu: Project > WebHub > New AppID 2. New Project - create the DPR and the unit containing TtpProject, with the events all coded for you. Use the menu: File > New > Other > Projects > WebHub Application and reference http://webhub.com/dynhelp:alias::newprojwizhowto for detailed steps for creating your first hello-world app. 3. New Form – use this to add a module into an existing project. File > New > Form > WebHub Form Modules aka Panelware When Delphi first came out with form inheritance, TPack started leveraging inheritance to allow for user interfaces that would snap together in a way that was suitable for prototyping and testing. As WebHub applications on production servers usually prefer a minimalist GUI, this made sense. At first, we called these snap-together forms "panelware". WebHub panels were often re-used in multiple projects. More recently, we have been talking about standard and custom "modules", where the GUI is optional. Let's illustrate this by reworking the banner idea we worked with earlier in listing 3. Of course, you would want that triggered from a web page through a TwhWebAction component. In addition, for testing, you might want the feature triggered by a button click. Ideally, you would create two new units, a datamodule and a form. You would do this using the New Form Wizard. You would put the non-visual functionality into the datamodule (i.e. the TwhWebAction), and you would put the visual button into the form. Now the form would be a special form, descending from TutParentForm in TPack. This type of form automatically adds itself as a "panel" into the WebHub GUI. If that form is created first, it will be the first panel. If it is created third, it will be the third panel. For advanced WebHubbers who want extra credit, it is possible to invent a keyword to associate with the new feature, say "banner". (This is worth doing if you intend to on-sell your module to other WebHub programmers.) You could then add a bit of code to your TtpProject events such that the core code in the datamodule and the GUI would be brought in based on configuration entries in your application-level config file. The custom configuration is shown in figure 3. Notice that the banner feature is "Enabled with GUI" by default, yet it is "Enabled" on a server which has been labeled as "production." The Delphi code which makes the feature come and go based on the configuration is shown in listing 9.
procedure TDMSampleForWHProject.ProjMgrDataModulesCreate3( Sender: TtpProject; var ErrorText: String; var Continue: Boolean); begin CreateStandardWHModules; if (pWebApp.Startup.CustomModuleStatus('banner') <> mstatusDisabled) then Application.CreateForm(TDM001, DM001); { Special Comment for DataModules - do not delete! This comment is used by the WebHub Wizard to position non-gui CreateForm statements for you, just above here. You may add your own CreateForm statements as well, either above or below this comment, as you wish. } end; procedure TDMSampleForWHProject.ProjMgrDataModulesInit( Sender: TtpProject; var ErrorText: String; var Continue: Boolean); begin InitCoreWebHubDataModule; InitStandardWHModules; if (pWebApp.Startup.CustomModuleStatus('banner') <> mstatusDisabled) then DM001.Init; end; procedure TDMSampleForWHProject.ProjMgrGUICreate( Sender: TtpProject; const ShouldEnableGUI: Boolean; var ErrorText: String; var Continue: Boolean); begin {$IFNDEF PREVENTGUI} if ShouldEnableGUI then begin Application.CreateForm(TfmWebHubMainForm, fmWebHubMainForm); CreateStandardWHModulesGUI; if pWebApp.Startup.CustomModuleStatus('banner') = mstatusEnabledWithGUI then Application.CreateForm(TfmAppPanel, fmAppPanel); { Special Comment for Forms - do not delete! This comment is used by the WebHub Wizard} end; {$ENDIF} end;
Listing 9: Creating modules based on status in application-level config file When you are in a rush, you probably would not take the time to separate the GUI, give the feature a name, and load it conditionally based on configuration entries. However, if you wanted to develop a module to sell to other WebHub developers, or if you wanted to compile one large application for multiple purposes and needed to be able to turn major features on and off for various customers, you might take advantage of this approach.
Any WebHub application can surface some or all of its features via the standard SOAP interface
Fig. 3: ZMAdmin, used to vary the use of the banner feature for development vs production.
18
MAGAZINE
SOAP Any WebHub application can surface some or all of its features via the standard SOAP interface, and we definitely recommend this. Precisely, any TwhWebAction derivative can be SOAP enabled by toggling the SOAPCallOk property from False to True. This is demonstrated in the "spam aversion" demo, http://demos.href .com/htun. If you click into that demo, you can see how SOAP documentation is generated automatically, both human-readable and
DELPHI
everything needed for software interfacing. In that demo, use the [Source] link to see all the Delphi code, especially the whStopSpam_fmWh.pas file which shows how to add a custom method with in and out parameters. Customer Comments I thought you might like to hear from "ordinary" Delphi programmers have to say about WebHub. The following two comments are from people who have built significant ventures using WebHub and Delphi. The first operates in Germany and the second in the USA. “We have been working with WebHub since March 2000. At that time, we intensively evaluated web development options: Java, PHP, ASP, diverse frameworks in each of these languages. Even at that time, none of these evaluated tools impressed us with their performance. Also, the cost effectiveness and usability was often unbelievably lacking. Whoever has even once labored with Java and Struts and their XML will understand exactly what I am talking about. After months of searching and evaluating, I found href.com. It was like a miracle. We could finally develop our real estate portal in Delphi, logically and consistently. It was wonderful. The performance, the stability, the ease of use, the simplicity -- all this was, and still is today, the basis for us to say that the WebHub framework is the number 1 choice for anyone who wants to develop a stable, high-traffic, modular, high-performance web application. We know Java, we know PHP, we know Ruby and Ruby on Rails. Yet, if we were to build our real estate portal again, starting today, we would do it with 100%-awareness with the friendly WebHub. If it were possible to give 5 stars to WebHub, we would give it 6 stars. At this point, many, many thanks to Ann, for the always-friendly, high quality technical support, the quick clarification of ideas and quite simply for the best web tool which I use daily.” -- Andreas Orth, www.my-next-home.de, customer from 2000 to 2009 “We have had several iterations of migrating to ASP.Net and all have failed in matching or even approaching the performance and the productivity of Delphi + WebHub. All of these attempts have been by professional ASP.Net developers. Our competition (using ASP.Net, ASP, PHP, Java) have not been able to approach the complexities, performance, etc., we have attained on our site -- regardless of the number of developers they commit.” -- John Correa, www.res.net, customer from 1997 to 2009 Licensing and Source Code Availability We have a fine-grained way of providing source code. We recommend that you start with a free evaluation license (www.href.com/trywhub) and make sure that you are in love with WebHub. The latest prices are always available at http://www.href.com/hrefshop. WebHub is available for Delphi 6 and above; Delphi 2009 is recommended. TNativeXml, ZaphosdMap and most of TPack also compile in Kylix 3 and Delphi for Dot Net through D2007. TNativeXml additionally compiles under FreePascal. Conclusion In these articles, I've walked you through the facets of WebHub which contribute to its unique level of power and flexibility. I invite you to use this knowledge -- in Delphi, with WebHub -- or otherwise. Who should build apps with WebHub? Ideally, WebHub is for programmers who love expressing themselves in Object Pascal, who are interested in skinning their web application for multiple audiences, who need to translate their site content into two or more lingvos, who see advantages in partnering with a web artist, and who want to build something long-lasting.
Fig. 4:. Once more where we started off at the cables suspending the Brooklyn Bridge, April 2004. I have been very quiet about WebHub since 2002; we were consumed with extensive refactoring. We are now officially open again for new customers, and I look forward to hearing from readers with questions or comments. Reach me through our site, http://www.href.com/ contact. •
Ann Lynnworth Ann Lynnworth was born near Boston, Massachusetts USA at the right time to catch the information technology wave. In 1978, she found she could not resist learning to program fancy macros for WordPerfect, and she has never been afraid to pick up a manual and/or guess her way into a new technology. She started her "Software Doctor" consulting business in 1983 on Apple's early computers, published WordStar at Your Fingertips, wrote Situation Analyst for the IBM PC, helped create Ronstadt's Financials and then joined XL/Proteus to learn all about Paradox and a bit about SQL. Skipping ahead to 1994, Ann learned Delphi and soon co-founded HREF Tools and has worn many hats with the internet tools company, the most enjoyable of which was producing “Tech Talk Radio” for several years for WebHub customers. A dynamic speaker with an unusual point of view, Ann has had rave reviews from attendees in training seminars and at technical conferences. Ann holds an associate art's degree from Simon's Rock College and a bachelor’s degree in psychology from Wellesley College, also in Massachusetts.
magazine voor software development 19
Advertentie VNU
UX
Koen Zwikstra
Silverlight en Open Source Silverlight is een relatief jong platform voor het ontwikkelen van zogenaamde Rich Internet Applicaties op basis van .NET. De komst van een nieuw applicatieplatform is veelal het begin van de opkomst van Open Source projecten en Silverlight is hier geen uitzondering op. Er zijn inmiddels een aantal waardevolle Open Source projecten voor Silverlight beschikbaar. In dit artikel wordt een overzicht geschetst van deze projecten en worden er suggesties gedaan voor het zelf opzetten en beheren van een Open Source project. Op codeplex.com, een Open Source Project community, zijn inmiddels een kleine 600(!) Open Source projecten te vinden die gerelateerd zijn aan Silverlight. Dit artikel biedt bij lange na geen volledig overzicht, maar geeft een korte beschrijving van een tiental populaire Open Source projecten. De geselecteerde projecten zijn opgedeeld in de categorieën Controls, Design Patterns, Graphics Engines en Runtime extensies.
Op codeplex.com zijn inmiddels een kleine 600(!) Open Source projecten te vinden die gerelateerd zijn aan Silverlight Controls De Silverlight runtime bevat een verzameling controls als een button, combo, list, tab, enz. Naast deze basisverzameling zijn er in verschillende Open Source projecten andere controls beschikbaar, variërend in functionaliteit van eenvoudig tot zeer complex.
Deep Earth http://deepearth.codeplex.com Wanneer je een interactieve kaart van de wereld op wilt nemen in je applicatie, is het zeker de moeite waard om te kijken naar Deep Earth. Dit project maakt gebruikt van Deep Zoom en Virtual Earth om een map control op te bouwen, vergelijkbaar met Google Maps. De control is volledig aan te passen, en biedt mogelijkheden voor het toevoegen van eigen navigatie controls en layers waarop de ontwikkelaar zelf visuele elementen kan tekenen. Microsoft Silverlight Toolkit http://silverlight.codeplex.com De Microsoft Silverlight Toolkit is zonder twijfel het meest bekende Silverlight Open Source project. Het project wordt beheerd door een team van Microsoft ontwikkelaars en bevat een uitgebreide verzameling controls en professionele UI themes. De controls bestaan onder andere uit een AutoCompleteBox, DockPanel, TreeView, ViewBox en Charting controls. De populaire controls die uitontwikkeld zijn, worden uiteindelijk opgenomen in de Silverlight runtime.
De Silverlight Toolkit is een must-have De Silverlight Toolkit is een must-have; naast de Silverlight SDK is elke Silverlight developer min of meer verplicht om ook de Toolkit te gebruiken.
Fig. 1: DeepEarth control
Silverlight Extensions http://slextensions.codeplex.com Silverlight Extensions is een zeer uitgebreid Open Source project en bevat vele controls en andere hulpmiddelen. Silverlight Contrib, een vergelijkbaar project, is recentelijk samengevoegd met Silverlight Extensions en het resultaat is een API met honderden bruikbare classes. Het project bestaat onder andere uit een color picker, menu, virtueel stack panel, zip functionaliteit, dynamic imaging en tekst parsers. Naast de controls biedt het project ook designtime hulpmiddelen als ReSharper templates en Visual Studio code snippets. Silverlight Extensions wordt onderhouden door een aantal prominente developers in de Silverlight community.
magazine voor software development 21
UX In het project worden onder andere de Command, Adapter en Facade design patterns toegepast. Prism is een project wat zeer bruikbaar kan zijn voor het ontwerp en de ontwikkeling van grote en complexe business applicaties. Het Prism Open Source project bestaat uit een referentie implementatie, een library en uitgebreide documentatie. Silverlight.FX http://projects.nikhilk.net/SilverlightFX Een project wat zeker niet onvermeld mag blijven is Silverlight.FX van Nikhil Kothari. Nikhil is een software architect in de Microsoft Developer Divisie en is onder andere bekend van zijn Script# project (een C# compiler die JavaScript genereert). Silverlight.FX is een applicatieframework voor het ontwikkelen van Rich Internet Applicaties met Silverlight. Het framework voorziet in een applicatiemodel, UI componenten, declaratieve views en UI effects. Silverlight.FX past verschillende bekende design patterns toe als Model-View-ViewModel, MVC, IoC containers, enz. Fig. 2: Slide.Show 2 Silverlight Video Player http://sl2videoplayer.codeplex.com Ben je op zoek naar een kant en klare video speler voor Silverlight, dan is de Silverlight Video player zeer waarschijnlijk een goede kandidaat. De Player is een relatief eenvoudige user control die naar eigen inzichten visueel aangepast kan worden met behulp van styles en templates. Slide.Show 2 http://slideshow2.codeplex.com Slide.Show 2 is een control voor het publiceren van foto- en videopresentaties. De oorspronkelijke versie was ontwikkeld voor Silverlight 1.0 maar deze is nu volledig herschreven voor Silverlight 2. Slide.Show 2 is volledig configureerbaar en biedt standaard ondersteuning voor XML-data en de fotosite Flickr. De control is uit te breiden met zelf ontwikkelde foto- en video-providers. Slide.Show 2 is een Open Source project van Vertigo, een bedrijf met een goede reputatie op het gebied van WPF- en Silverlight-applicaties.
Graphics Engines Op grafisch gebied zijn de mogelijkheden van Rich Internet Applicatie platformen, zoals Silverlight, superieur aan HTML. Met Silverlight kunnen complexe grafische 2D en 3D applicaties ontwikkeld worden. Voor de ontwikkeling van deze applicaties wordt vaak gebruik gemaakt van zogenaamde grafische engines. Een grafische engine biedt de ontwikkelaar via een model een hulpmiddel voor het tekenen van entiteiten. In de Silverlight Open Source wereld is een aantal grafische engines beschikbaar. Farseer Physics Engine http://farseerphysics.codeplex.com De Farseer Physics Engine is een eenvoudig te gebruiken 2D physics engine. Een physics engine zorgt ervoor dat objecten in een virtuele wereld zich realistisch gedragen. De engine berekent welke krachten, zoals bijvoorbeeld de zwaartekracht, een object beïnvloeden en beweegt het object overeenkomstig deze krachten. Een physics engine berekent ook of objecten met elkaar in botsing komen (object collision) en biedt de ontwikkelaar de mogelijkheid om daar op in te haken. De Farseer Physics engine is de meest uitgebreide Open Source physics engine die nu beschikbaar is voor Silverlight.
Design Patterns Wanneer je een serieuze applicatie ontwikkelt is het goed om bij het ontwerp en de ontwikkeling van de applicatie gebruik te maken van beproefde design patterns. Een design pattern biedt een oplossing voor een veel voorkomend probleem wat, mits goed toegepast, de onderhoudbaarheid van de applicatie ten goede komt. Verschillende Open Source projecten definiëren design patterns die specifiek voor Silverlight bedoeld zijn. Prism http://compositewpf.codeplex.com Prism (ook wel Composite Application Guidance genoemd) is oorspronkelijk ontwikkeld voor WPF, en biedt nu ook hulpmiddelen voor het ontwikkelen van Silverlight-applicaties. Prism komt uit de hoek van Microsoft patterns & practices, waar beproefde praktijkoplossingen worden aangeboden voor de verschillende aspecten binnen software ontwikkeling. Prism richt zich op de ontwikkeling van modulaire applicaties.
22
MAGAZINE
Fig. 3: Kit3D in actie
UX De engine is oorspronkelijk ontworpen voor het XNA platform (XBOX 360 en Windows) maar is herschreven voor Silverlight. Kit3D http://kit3d.codeplex.com Kit3D is een 3D grafische engine met een API die overeenkomt met de 3D API van Windows Presentation Foundation, zoals deze in de System.Windows.Media.Media3D namespace is gedefinieerd. Kit3D is nog niet productierijp, maar biedt een goede indruk van wat de mogelijkheden zijn voor het ontwikkelen van 3D applicaties. Met Kit3D kunnen meer geavanceerde 3D applicaties ontwikkeld worden dan mogelijk is met de beperkte perspective 3D transformaties van Silverlight 3.
Microsoft neemt Open Source serieus Runtime extensies Microsoft neemt Open Source serieus en dat is goed te merken aan de hoeveelheid grote Open Source projecten voor Silverlight die afkomstig zijn van Microsoft zelf. Silverlight Toolkit en Prism zijn daar mooie voorbeelden van, maar Microsoft gaat nog veel verder door een aantal runtime extensies als Open Source ter beschikking te stellen.
Open Source sites bieden een uitgebreid scala aan beheerfuncties Silverlight Dynamic Languages SDK http://sdlsdk.codeplex.com Met behulp van de Silverlight Dynamic Languages SDK kunnen Silverlight applicaties ontwikkeld worden met dynamische programmeertalen als Ruby en Python. Een dynamische programmeertaal kenmerkt zich door de mogelijkheid om tijdens het draaien aanpassingen aan het programma te doen. Variabelen hoeven niet gedeclareerd te worden en code en objecten kunnen runtime worden aangepast. Bij de huidige versies van statische talen als C# en VB.NET is dit onmogelijk of niet eenvoudig te doen. Dynamische talen staan tegenwoordig steeds meer in de belangstelling. De SDK implementeert een koppeling tussen Silverlight en de Dynamic Language Runtime (DLR). De DLR is ook volledig Open Source. Hebrew & Arabic Language support http://silverlightrtl.codeplex.com De Silverlight runtime biedt geen ondersteuning voor talen die van rechts naar links (RTL) geörienteerd zijn. Wil je deze taalondersteuning toch in je applicatie aanbieden, dan kun je gebruik maken van dit Open Source project. Het project bevat een verzameling controls en helper functies voor het correct tekenen van tekst die zowel links-naar-rechts (LTR) als de rechts-naar-links (RTL) georiënteerd is. Open Source software ontwikkelen Het is vrij eenvoudig om zelf een Open Source project te starten en te beheren. Er bestaat een aantal gerenommeerde sites die Open Source projecten hosten, zoals sourceforge.net en codeplex.com. Deze Open Source sites bieden een uitgebreid scala aan beheerfuncties. Zaken als source control, versiebeheer, discussieforum, wiki en statistieken zijn in de meeste gevallen uitstekend geregeld. Kijk goed om je heen voor je een Open Source project start. Wellicht kun je participeren in een bestaand project. Neem daartoe contact op met het betreffende projectteam en in de meeste gevallen zul je een zekere reputatie opgebouwd moeten hebben, wil je worden toegelaten.
Licentie Het bespreken van de voors en tegens van de verschillende Open Source licenties valt buiten de scoop van dit artikel. Het is wel verstandig om goed na te denken over de licentie waaronder je de source code wilt vrijgeven. Met de licentie geef je aan wat een ontwikkelaar wel en niet met de source code mag doen. Een aantal licenties leggen soms verregaande restricties op aan de applicatie die wordt ontwikkeld met behulp van het Open Source project. Zo kan het zijn dat een Open Source licentie de ontwikkelaar dwingt om zijn eigen applicatie ook als Open Source te distribueren. Het opstellen van een eigen licentie is meestal niet nodig, Open Source hosting sites bieden een ruime keuze uit een aantal standaard licenties die in de meeste gevallen voldoen. Conclusie Er is inmiddels een groot aantal waardevolle en bruikbare Open Source projecten voor Silverlight beschikbaar. Ervaren en enthousiaste ontwikkelaars uit de Silverlight community en van Microsoft maken zich sterk voor de ontwikkeling van kwalitatief hoogwaardige Open Source projecten. Wanneer je een applicatie in Silverlight wilt bouwen, is het verstandig om op de hoogte te zijn van wat er in de Open Source wereld te koop is. Het is immers goed mogelijk dat de functionaliteit die je wilt ontwikkelen al beschikbaar is. In dit artikel zijn slechts een beperkt aantal projecten besproken, er bestaan vele honderden Open Source projecten die gerelateerd zijn aan Silverlight. De hoeveelheid projecten is een indicatie dat de Silverlight Open Source community groeit en bloeit. Wanneer je zelf een Open Source project wilt opstarten, bieden sites als codeplex.com uitstekende hulpmiddelen om je project succesvol te beheren. •
Koen Zwikstra Koen Zwikstra is een ervaren Silverlight ontwikkelaar van het eerste uur. Met zijn onderneming richt hij zich volledig op de Silverlight technologie en verzorgt hij Silverlight consultancy diensten aan zijn opdrachtgevers. Daarnaast is hij verantwoordelijk voor de ontwikkeling van verschillende producten, waaronder Silverlight Spy, en levert hij een actieve bijdrage aan de wereldwijde Silverlight developer community.
Noteren in agenda: 19 & 20 oktober a.s. SDN Conference magazine voor software development 23
DATABASES
André van ’t Hoog en Arjan Pot
HierarchyID in
SQL Server 2008 Relationele databases zoals SQL Server zijn sterk in het opslaan van relationele gegevens. Sommige gegevens zijn echter beter te ordenen in een hiërarchisch model, b.v. de structuur van een organisatie. SQL Server 2008 introduceert een nieuw datatype voor hiërarchische gegevens: HierarchyID. HierarchyID is een eenvoudig datatype met slechts 8 relevante functies, maar wijkt af van de manier van werken die we gewend zijn in SQL Server. In dit artikel leggen we het concept uit en laten we zien hoe je HierarchyID kunt gebruiken. Hierarchie Een hiërarchische structuur noemen we ook vaak een boomstructuur. Klassieke voorbeelden zijn werknemers onder leiding van andere werknemers, een stamboom, een bestandsysteem, postings in een forum, gebruikers van computers. Gegevens in XML zijn per definitie hiërarchisch. In dit artikel gebruiken we een organisatiestructuur als voorbeeld. Om hiërarchische gegevens in een relationele database op te slaan wordt vaak gebruik gemaakt van recursie: één kolom in de tabel wijst naar de primary key van dezelfde tabel (zie figuur 1).
Het datatype HierarchyID bevat een path naar een item; dit wordt opgeslagen in een varbinary veld. Het path is als string makkelijk te lezen. Tussen slashes “/” staan nummers die de parents van het item voorstellen. Zie b.v. de structuur in figuur 3.
Fig. 1: Klassiek datamodel Deze structuur is simpel en doeltreffend en staat ook wel bekend als “adjacency list model”. Wanneer je er mee gaat werken, wordt snel duidelijk dat een relationele database weinig ondersteuning biedt voor hiërarchische gegevens. In SQL Server 2000 moest je aan de slag met CURSORS en tijdelijke tabellen. Sinds SQL Server 2005 kun je gebruik maken van Common Table Expressions. Met SQL Server 2008 is er een nieuw datatype dat we kunnen gebruiken: HierarchyID. Het datamodel dat we in ons voorbeeld gebruiken staat in figuur 2. De kolom “Node” is van het type HierarchyID.
Fig. 2: Datamodel met HierarchyID
24
MAGAZINE
Fig. 3: Voorbeeldhiërarchie User-defined type HierarchyID is geimplementeerd als een CLR user-defined type (UDT). Dit betekent dat er net zoals in C# methodes zijn aan te roepen. Hieronder volgt een samenvatting van de methodes. Let op dat alles case-sensitive is. De voorbeelden zijn gebaseerd op de tabel in figuur 2 en de boomstructuur in figuur 3. Proven technology HierarchyID maakt intern gebruik van het .NET type OrdPath. Dit type is in SQL Server 2005 geintroduceerd om het XML type te ondersteunen. XML wordt in SQL Server “platgeslagen” en geordend met OrdPath. Het HierarchyID type is daarmee gebaseerd op bewezen technologie. Wil je precies weten hoe HierarchyID werkt, dan is het zeker de moeite waard om eens te zoeken op OrdPath. HierarchyID is onderdeel van System.Data.SqlTypes, waardoor je het kunt gebruiken buiten SQL Server, gewoon in een C# of Visual Basic project.
DATABASES
ToString() Vertaalt de inhoud van het varbinary veld naar een leesbaar path. Zie listing 1. SELECT Node, Node.ToString() AS NodePath FROM Organization Node -----------0x 0x58 0x5AC0 0x5B40 0x68 0x6A241BE8EC
Listing 1 De ToString method gebruik je om het path te zien of om gegevens over te zetten naar een extern system. Parse(path) Vertaalt een leesbaar path naar een HierarchyID. Dit is de omgekeerde bewerking van ToString(). Dit is een static method. Zie listing 2. SELECT hierarchyid::Parse('/5/4/3/2/1/') AS Node Node -----------0x8E17B560
Listing 2 Parse gebruik je om gegevens met een editor in te voeren, b.v. vanuit SQL Server Management Studio, of om gegevens te importeren vanuit een ander systeem. Als je een ongeldig path opgeeft, krijg je een .NET exception. Overigens wordt de Parse method impliciet uitgevoerd zodra je een string toekent aan een veld van het type hierarchyid. GetRoot() Levert de root node; in de huidige implementatie van HierarchyID is dat altijd “/”. Dit is een static method. Zie listing 3. SELECT hierarchyid::GetRoot() AS Node, hierarchyid::GetRoot().ToString() AS NodePath Node NodePath ------------ -----------0x /
Listing 3 GetLevel() Levert het nivo van de node in de hierarchy. De root heeft level 0. Zie listing 4. SELECT Node.ToString() AS NodePath, Node.GetLevel() AS Level FROM Organization NodePath -------------/ /1/ /1/1/ /1/2/ /2/ /2/-1.5.-123/
Level -------------0 1 2 2 1 2
In het voorbeeld van een organisatie kun je hiermee alle suborganisaties op een bepaald nivo ophalen. GetAncestor(n) Levert de parent van de node, die n niveaus hoger staat. Zie listing 5. Vraag je naar een nivo dat niet bestaat, dan wordt er NULL geretourneerd. SELECT Node.ToString() AS NodePath, Node.GetAncestor(1).ToString() AS ParentPath, Node.GetAncestor(2).ToString() AS ParentsParentPath FROM Organization NodePath -------------/ /1/ /1/1/ /1/2/ /2/ /2/-1.5.-123/
Listing 5 In het voorbeeld van een organisatie kun je hiermee makkelijk vragen onder welk punt van de organisatie je zit. IsDescendantOf(parent) Levert “1” op als de node valt onder parent, anders “0”. Opvallend is dat IsDescendant() ook zichzelf oplevert. Ook alle subnodes voldoen aan IsDescendantOf(). Deze methode is zeer geschikt om records te selecteren in de WHERE clause. Listing 6 is een voorbeeld van het ophalen van alle child records van item /2/. SELECT Node.ToString() AS Descendant_Of_2 FROM Organization WHERE Node.IsDescendantOf('/2/') = 1 Descendant_Of_2 ------------------/2/ /2/-1.5.-123/
Listing 6 In het voorbeeld van een organisatie kun je hiermee suborganisaties ophalen. De drie methodes GetLevel(), GetAncestor() en IsDescendantOf() gebruik je om records te selecteren. In combinatie met SQL is het aantal mogelijke queries eindeloos. GetDescendant(child1, child2) Als je denkt dat je hiermee de records kunt opvragen die liggen onder de huidige node, dan heb je het mis. Zoals alle methods van HierarchyID is er geen kennis van de tabellen of de kolommen. Alle HierarchyID methods zijn static of ze voeren een berekening uit op de huidige node, verder niks. De GetDescendant method levert een “mogelijk” child onder de huidige node. De parameters chlid1 en child2 worden gebruikt om de positie te beperken, beide mogen null zijn. Je kunt dit bijvoorbeeld gebruiken wanneer je een nieuwe node toevoegt. Zie listing 7. SELECT Node.ToString() AS NodePath, Node.GetDescendant(NULL,NULL).ToString() AS DescendantPath FROM
Listing 7 In listing 8 zie je wat het resultaat is, als je vraagt naar een node onder / en tussen /1/ en /2/. SELECT hierarchyid::Parse('/').GetDescendant('/1/','/2/') .ToString() AS NodePath NodePath -----------/1.1/
Listing 8 De parameters child1 en child2 worden gevalideerd binnen de context van de node. Dit kan resulteren in .NET exceptions. In het voorbeeld van een organisatie gebruik je deze method als je de organisatie gaat uitbreiden. Volgorde Naast de hiërarchie leg je met HierarchyID ook de volgorde van nodes vast. Dit in tegenstelling tot het adjacency list model. In figuur 3 zie je bijvoorbeeld dat B links van C ligt en dat D links van E ligt. GetReparentedValue(oldparent, newparent) Bij het verplaatsen van records moet je zelf alle onderliggende records verplaatsen. Dat kan best lastig zijn. Je moet er bijvoorbeeld rekening mee houden dat alle node’s een uniek path hebben. De method GetReparentedValue biedt hulp. SELECT Node.ToString() AS NodePath, Node.GetReparentedValue('/1/','/2/').ToString() AS NewNodePath FROM Organization WHERE Node.IsDescendantOf('/1/') = 1 NodePath -------------/1/ /1/1/ /1/2/
NewNodePath -------------/2/ /2/1/ /2/2/
Listing 9 In listing 9 wordt een nieuw path bepaald voor alle nodes onder /1/ bij het verplaatsen naar /2/. De node /1/1/ wordt dan /2/1/. Zoals je ziet wordt er geen rekening mee gehouden dat er al een node bestaat met path /2/1/. Het is puur een berekening die uitgevoerd wordt, zonder te kijken naar de data die aanwezig is in de tabel. Er wordt zelfs geen update op het record uitgevoerd. Verderop laten we zien hoe je dat zelf in SQL kunt schrijven. De parameter oldparent wordt gevalideerd binnen de context van de node. Dit kan resulteren in .NET exceptions. In het voorbeeld van een organisatie gebruik je deze methode als je de organisatie anders gaat indelen.
26
MAGAZINE
Limieten Een path mag maximaal 256 bytes lang zijn. Het aantal mogelijke nodes is daarmee voldoende gewaarborgd. De enige limiet die misschien ooit bereikt wordt, is de lengte van een path. Wanneer nodes vaak verplaatst worden, zal het path snel langer worden. Een klein experiment laat zien dat er circa 160 nivo’s mogelijk zijn bij nodes van 10 cijfers. Wanneer een node uit 50 cijfers bestaat zijn 33 nivo’s mogelijk. Geen reden tot ongerustheid. En mocht de grens bereikt zijn, dan kun je altijd nodes voorzien van nieuwe getallen zonder de hiërarchie aan te tasten. Database ontwerp Bij het ontwerpen van een database is het verstandig rekening te houden met de eigenschappen van HierarchyID. Het zal inmiddels duidelijk zijn dat HierarchyID geen kennis heeft van de context waarin het gebruikt wordt. De programmeur of databasebeheerder zal zelf nog het nodige werk moeten verrichten. Hiërarchie breken Het is erg eenvoudig om een hiërarchie te breken; je kunt er gewoon een node tussenuit halen. Bij het aanpassen van je hiërarchie moet je zelf alle onderliggende nodes bijwerken en conflicten voorkomen. In listing 10 staat een voorbeeld van een update statement waarmee je dat zou kunnen doen. UPDATE Organization SET Node = Node.GetReparentedValue('/1/', '/2/') FROM Organization WHERE Node.IsDescendantOf('/1/') = 1
Listing 10 Constraints Database-inconsistentie kan worden voorkomen door een unique constraint te plaatsen op de HierarchyID kolom. Een foreign key constraint op een computed column van de parent en de kolom zelf kan de referentiële integriteit borgen. Het is geen goed idee om de primary key van een tabel op de HierarchyID kolom te zetten, de nodes kunnen immers wijzigen waardoor relaties met andere tabellen zullen breken. Stored procedures Het gebruik van stored procedures is aan te bevelen om records toe te voegen, te verplaatsen en te verwijderen. LINQ, Entity Framework en ADO.NET De LINQ to SQL designer van Visual Studio 2008 loopt vast op HierarchyID. Het Entity Framework doet het een stuk beter: het negeert de kolom met HierarchyID maar loopt gelukkig niet vast. ADO.NET doet het uitstekend; de DataReader herkent HierarchyID. De DataSet wordt goed ondersteund, de HierarchyID kolom wordt getoond als string en bij een update wordt de string geparsed zodat je de inhoud van de HierarchyID kolom kunt wijzigen. Samenvatting HierarchyID is een nieuw data type in SQL Server 2008. HierarchyID reikt een nieuwe manier aan om gegevens op te slaan in een database. Het principe is betrekkelijk simpel en efficiënt. Voor programmeurs en databasebeheerders zal het even wennen zijn om HierarchyID te begrijpen en toe te passen. Ook de database-ontwerper doet er verstandig aan om zich in de techniek te verdiepen. Met name de combinatie van .NET methods en SQL vergt gewenning.
André van ’t Hoog André van ’t Hoog is senior software ontwikkelaar bij Evident Interactive B.V. Hij is nu ongeveer 20 jaar werkzaam in de ICT-sector en heeft zich sinds 1996 gespecialiseerd in Microsoft technologie, met name .NET en SQL Server. Recent is hij met name bezig geweest met de ontwikkeling van internet applicaties waarbij onder andere gebruik is gemaakt van (eerst) MCMS 2002 en (later) Sharepoint 2007. Daarnaast is hij momenteel in deeltijd actief als database administrator voor een aantal op SQL Server gebaseerde applicaties.
UX
Arjan Pot is een door Microsoft gecertificeerde senior software ontwikkelaar bij Evident Interactive B.V. Hij specialiseert zich in Web Development, zowel back-end als front-end. Van assembly programmeren op Micro controllers tot configureerbare workflow heeft Arjan zich ontwikkeld tot allround programmeur. Speerpunten in zijn kennis zijn C#, SQL Server en ASP.NET. Hij ontwikkelt bij voorkeur iteratief en in een hecht multidisciplinair team.
TIP: 50 beautiful Flash websites
Met de komst van Silverlight en Flash is het voor designers en developers mogelijk een nieuwe generatie websites te maken. Animatie en interactiviteit dragen bij aan een indrukwekkende visuele experience. Smashing magazine verzamelde 50 mooie Flash websites om inspiratie op te doen: http://www.smashingmagazine.com/2009/06/07/50-beautiful-flash-websites/.
Advertentie Barnsten/Embarcadero
Advertentie Bergler
CORE SYSTEMS
Norbert Mimpen
FloraHolland Moderniseert Hart IT FloraHolland, de grootste producent en handelaar van snijbloemen ter wereld, worstelde met een erfenis uit het verleden. Verschillende Cobol-systemen op een oude infrastructuur kostten FloraHolland veel geld aan onderhoud en beheer. Bovendien werd de technische kennis bij FloraHolland steeds schaarser om het onderhoud en beheer adequaat uit te voeren. FloraHolland koos ervoor om het hart van haar IT te moderniseren door de Cobolsystemen geautomatiseerd te laten migreren naar C# op een moderne infrastructuur.
FloraHolland koos ervoor om het hart van haar IT te moderniseren door de Cobol-systemen geautomatiseerd te laten migreren naar C# op een moderne infrastructuur FloraHolland had diverse bedrijfskritische Cobol-maatwerksystemen op een oude infrastructuur (VAX/VMS) draaien ter ondersteuning van haar logistieke en financiële processen, waaronder de facturatie. De continuïteit van de IT-ondersteuning van deze processen dreigde ernstig in gevaar te komen door een oplopende schaarste aan Cobol-expertise binnen de eigen organisatie. Hiernaast was FloraHolland zich ook bewust van het feit dat de operationele kosten van hun oude infrastructuur factoren hoger lagen dan die van een moderne infrastructuur, zoals Windows. Ook was het voor FloraHolland duidelijk dat de productiviteit van het onderhoud op hun Cobol-systemen door migratie naar een nieuwe technologie kon worden verbeterd. Migratie naar een moderne technologie was voor FloraHolland dan ook een logische keuze. Ook de keuze van de doeltechnologie kostte FloraHolland geen moeite. De standaard architectuur binnen FloraHolland is geënt op Microsoft C#. Deze expertise heeft FloraHolland al ruimschoots beschikbaar. FloraHolland koos in eerste instantie ervoor om zijn Cobol-systemen zelf te converteren naar C#, en wel handmatig. In deze conversie zou meteen de platformmigratie van de oude infrastructuur naar Windows worden meegenomen.
de bron- en doeltaal leken op het eerste gezicht dezelfde werking te hebben, maar bleken uiteindelijk heel verschillend te zijn. Een concreet voorbeeld hiervan is de toekenning van waarden. In bepaalde gevallen zal Cobol de toegekende waarde afkappen, terwijl dat in C# niet gebeurt, waardoor een verschillende functionele werking van de programma’s optreedt. Zonder diepgaande kennis van zowel bron- als doeltaal kunnen statements dus onjuist worden vertaald. Ook de platformmigratie bood de nodige uitdagingen. Diverse utilities op de oude infrastructuur, zoals sorteringroutines met filterfunctionaliteit, vereisen nabootsing op Windows. Bovendien verschillen de karaktersets op de oude infrastructuur en onder Windows van elkaar, hetgeen de nodige problemen tijdens de conversie en het testen oplevert, zoals extra benodigde conversie van testbestanden. Hoe heeft FloraHolland deze uitdagingen opgepakt en tot een goed einde gebracht zonder haar businessprocessen te verstoren?
Diverse utilities op de oude infrastructuur, zoals sorteringroutines met filterfunctionaliteit, vereisen nabootsing op Windows Investering Het handmatig nabouwen van de Cobol-programma’s is een te lastige en langdurige opdracht. En meestal accepteert de Business het niet wanneer gedurende een lange migratieperiode géén productiewijzigingen mogelijk zijn: de zogenaamde freeze-periode. Daarom is voor het succesvol uitvoeren van complexe migraties automatisering van de conversie essentieel.
Uitdagingen Vanwege grote technische verschillen tussen Cobol en C# verliep de handmatige conversie door FloraHolland moeizaam. Statements van
Statements van de bron- en doeltaal leken op het eerste gezicht dezelfde werking te hebben, maar bleken uiteindelijk heel verschillend te zijn magazine voor software development 29
CORE SYSTEMS
FloraHolland ging op zoek naar leveranciers die ervaring hebben met migraties en hier geschikte tools voor hebben. Een grotendeels geautomatiseerde conversie van Cobol naar C# bleek echter nog nooit eerder te zijn uitgevoerd. De keuze van FloraHolland viel op de G4tool van Cornerstone vanwege de goede basis die deze tool biedt voor de ontwikkeling van een geautomatiseerde conversie van Cobol naar C#. Vanuit Sogeti werd de migratiemethodiek Mikado™, integratiecapaciteit en ruimschootse ervaring met migraties ingezet. Voor de toolontwikkeling stelde Sogeti de conversieregels op, die Cornerstone in zijn conversietool implementeerde. Mikado™ Mikado™ is een door Sogeti ontwikkelde migratieaanpak, gebaseerd op vele jaren ervaring. Mikado™ is ontwikkeld om migratieprojecten beheerst en gecontroleerd uit te voeren. Migraties worden daarbij gezien als een projectmatig proces, waarbij middels een één-op-één transformatie de functionaliteit van de bestaande (bron-) naar een nieuwe (doel)omgeving wordt overgezet en waarbij de continuïteit in de bedrijfsvoering wordt gegarandeerd. Vaak is er sprake van systemen die zijn ontwikkeld met ‘verouderde’ of achterhaalde technologie, die uiteindelijk niet meer voldoen aan de eisen en wensen van deze tijd en die vaak hoge kosten met zich meebrengen. Hierdoor ontstaat de noodzaak om de systemen en/of data over te zetten naar een modernere omgeving, eventueel met architectuurvernieuwing, en met behulp van analyse- en conversietooling.
Eerste helft Sogeti initieerde het migratieproject en startte het volgens Mikado™ op: na een migratiescan (in Mikado-termen “Scenario analyse”) vond de projectinrichting plaats en startte het opstellen van de conversieregels en het parallel implementeren van de conversieregels in de G4tool van Cornerstone. Het opstellen van de conversieregels bleek een lastig en langdurig proces te zijn, met veel iteraties en veel uitzonderingen. Met name het vertalen van de dataopslagstructuur in Cobol naar een object-oriented opslagstructuur in C# was te geïsoleerd, per programmadeel, opgepakt en niet als totaalconcept uitgewerkt. Na een lang traject was het nog steeds niet mogelijk om vanuit een automatische conversie een programma te compileren, terwijl de verzameling conversieregels al flinke proporties aannam. Door het niet kunnen compileren was er ook geen zicht op de kwaliteit van de conversie qua functionaliteit: is de output van de geconverteerde programma’s wel identiek aan die van de oorspronkelijke? Wisselen van spoor Voor de ontwikkeling van een conversietool is het aan te raden om eerst een representatief programma volledig handmatig te converteren. Hieruit kunnen vervolgens de conversieregels worden afgeleid. Dit spoor is ook initieel bewandeld, maar vanwege tijdsdruk losgelaten. Het terugwisselen naar deze aanpak, maar met als uitgangspunt de al voor meer dan 90% automatisch geconverteerde programma’s, doorbrak deze impasse. De resterende 10% van enkele representatieve programma’s werd handmatig afgebouwd, totdat ze compileerbaar waren. Vóór het handmatig afbouwen zijn afspraken gemaakt
30
MAGAZINE
over de vertaling van de data-opslagstructuur en over de vertaling van de utilities op de oude infrastructuur, zodat deze meteen in het handmatig afbouwen meegenomen konden worden. Het resultaat was enkele compileerbare en testbare representatieve programma’s. Door middel van een functionele test werd de kwaliteit van de conversie aangetoond. Dit was tevens het punt waarop gewisseld kon worden naar het opstellen en implementeren van de ontbrekende conversieregels op basis van de handmatige afbouw. Hierdoor is de conversietool gecompleteerd en kon de hoofdconversie op een beheerste manier worden uitgevoerd. Het blijkt dus essentiëel te zijn om een Proof of Concept uit te voeren op een representatief deel van de te migreren systemen, om zodoende de complexiteit naar voren te halen, en daarmee de risico’s van het migratieproject te minimaliseren. Tweede helft De gewijzigde aanpak maakte de resterende projectinspanning meteen meetbaar. Er konden goede schattingen worden gemaakt voor het implementeren van de resterende conversieregels, het testen en het bugfixen. Ook het kleine restant aan handmatige acties om de uitzonderingen in de geconverteerde programma’s te coderen werd direct inzichtelijk. De gewijzigde projectplanning werd in één klap realistisch en dus voorspelbaar. Na enkele kleine overschrijdingen van tussenmijlpalen vond de eindoplevering enkele werkdagen voor de geplande datum plaats. Het realiseren van de vereiste functionaliteit en performance staat echter altijd op de eerste plaats. Uit het beperkte aantal bevindingen (minder dan één per geconverteerd programma) blijkt dat zowel de proces- als productkwaliteit van de tweede helft van het project dik in orde waren. FloraHolland heeft op basis van de acceptatietest en na het oplossen van de bevindingen de migratie geaccepteerd. De in productie name van de geconverteerde programma’s verliep zonder noemenswaardige problemen.
Ná de finish Het FloraHolland migratieproject leverde een aantal waardevolle ervaringen op die in Mikado™ zijn geïntegreerd. De opgerichte unit Migratie Services is verantwoordelijk voor de integratie van deze migratieleerpunten. Doordat elke projectmanager van een migratieproject verplicht is om zijn ervaringen aan te leveren en omdat alle migratieprojecten waarin Sogeti betrokken is volgens de Mikado™ methode worden aangepakt, zorgt de borging van deze ervaringen ervoor dat niet opnieuw dezelfde knelpunten kunnen ontstaan. •
Norbert Mimpen Norbert Mimpen is Projectmanager en Migratie Specialist bij Sogeti. Norbert maakt deel uit van de unit Migratie Services, van waaruit hij in alle fasen van migratieprojecten betrokken is, vanaf het eerste klantcontact tot en met de afronding. Norbert draagt verder bij aan de productontwikkeling van migraties en is docent van migratieopleidingen. Norbert is een begenadigd hardloper en is een paar weken per jaar te vinden in Brazilië.
DOT NET NUKE
Brandon Haynes
Under the Hood: Provider-Based Authorization in DotNetNuke 5.1 Beginning with version 5.1, DotNetNuke introduces provider-based authorization as a firstclass framework extension point. Prior to this enhancement, authorization in DotNetNuke took place entirely within the core (generally implemented through classes in the DotNetNuke.Security.Permissions namespace) and was not customizable in any way. Herein we explore the motivations, implementation, and configuration associated with these changes, and discuss what this might mean for future customization. Authorization, Authentication, and Providers Prior to delving into the details of authorization in DotNetNuke, it is important to distinguish the often-confused difference between authentication – the binding of an identifier and one or more credentials (e.g., a password) to a principal (e.g., a user) – and authorization, which is the process of verifying that a principal is indeed allowed to perform an action or access a resource. Herein we focus upon this latter function. Authentication (and, as we shall soon see, authorization) is customizable in the DotNetNuke framework through classes configured via the provider pattern. This pattern allows runtime selection and configuration of a provider so long as that class conforms to the expected interface (or, less frequently, base class) (1). DotNetNuke relies heavily on this pattern to effectuate extendibility for the services (data, membership, navigation, et cetera) upon which it relies.
But, just as the DotNetNuke Corporation will leverage this extension point for its enterprise customers, so too may any developer. This will ultimately allow both for third-party extensions to accommodate common advanced scenarios, and custom development for specific oneoff situations. This enhancement is an improvement for all involved. In addition to the benefits described above, the consolidation of authorization services into a single, dedicated class allows for a reduced security surface area and thereby serves to increase overall framework security. Whereas in the past there might have been several places to attack authorization logic, this functionality will now be consolidated into a single-purpose assembly dedicated to one task. Authorization Prior to Version 5.1 Having discussed the reasons why authorization was abstracted, we now examine the state of authorization in DotNetNuke prior to version 5.1. Before this version, authorization logic was internal to the framework (located in the DotNetNuke.Security.Permissions namespace) and not extendable or customizable by developers in any way (save for a draconian recompilation of the entire core framework). For example, module permission-related logic was contained within the ModulePermissionController class, while file-based authorization was handled entirely by the FilePermissionController. The methods in these classes were static, not virtual, and not customizable in any manner, as is demonstrated in the ModulePermissionController.HasModulePermission method detailed in listing 1. Public Class ModulePermissionController ... Public Shared Function HasModulePermission(...) As Boolean Return PortalSecurity.IsInRoles(...) End Function
Why Allow Authorization to Be Extended? Before we touch on the details associated with the abstraction of authorization in the DotNetNuke framework, it is worth exploring why such a change would be made. For the DotNetNuke Corporation, the answer is simple: granular permissions are one of the differentiating factors planned between the Professional and Community Editions (2). Consumers of the former will be able to assign permissions at the very detailed level required by many enterprise customers (to whom that edition is targeted), while Community Edition deployments will continue to benefit from the (robust) level of authorization that has always been available on the platform.
This will ultimately allow both for thirdparty extensions to accommodate common advanced scenarios, and custom development for specific one-off situations
... End Class
Listing 1: Pre-5.1 implementation of ModulePermissionController.HasModulePermission (simplified for reading). This method is static and not customizable without modifying the core DotNetNuke framework. While it does perhaps make some sense for these authorizations to be performed by those controller classes that utilize the functionality, this approach makes it difficult to adjust, extend, and refine the framework for novel or unforeseen design requirements. Decoupling policy-enforcement functionality from its authorization-related counterpart does have some attractive design characteristics, including cleaner overall separation of concern. Authorization in DotNetNuke 5.1 Authorization services in version 5.1 have been abstracted into a separate provider assembly and the relevant functionality has been
magazine voor software development 31
DOT NET NUKE moved therein. The out-of-the-box provider included with the Community Edition is defined as DotNetNuke.Security.Permissions.CorePermissionProvider. The class diagram in figure 1 depicts the relationship of this class to the core framework.
Public Shared Function HasTabPermission(...) As Boolean Dim hasPermission As Boolean = Null.NullBoolean If permissionKey.Contains(",") Then For Each permission As String In permissionKey.Split(",") If provider.HasTabPermission(objTabPermissions,_ permission) Then hasPermission = True Exit For End If Next Else hasPermission = provider.HasTabPermission_ (objTabPermissions, permissionKey) End If Return hasPermission End Function
Fig. 1: Class diagram demonstrating the relationship between the core framework controller classes, the PermissionProvider class, and the concrete CorePermissionProvider authorization implementation.
Listing 3: TabPermissionController.HasTabPermission modified in version 5.1 to utilize the new permission provider. Note that this method routes all authorization decisions to the provider (accessed through a private field named “provider”). Public Overridable Function HasTabPermission(...) As Boolean Return PortalSecurity.IsInRoles(...)
Unlike many other DotNetNuke providers, the permission provider is not interface-driven, and must currently be inherited from DotNetNuke.Security.Permissions.PermissionProvider (though as of press time this version was still in beta and the final architecture remains subject to change). This is somewhat unfortunate as it potentially complicates some testing patterns (in particular the ability to easily mock a custom provider), but may nonetheless be generally worked around to meet most design goals. Like all the other DotNetNuke providers (e.g., data, membership, profile), the permission provider is configured in the web.config. The default configuration is presented in listing 2. Any custom provider would require a new entry under the <providers> node with the desired type (and the containing assembly must be deployed into the application’s bin directory).
End Function
Listing 4: The HasTabPermission method of the default Community Edition permission provider class, which has subsumed the functionality that was previously internal to the core. The DotNetNuke 5.1 Permission Provider Implementation The DotNetNuke 5.1 authorization provider may be generally subdivided into three classes of authorization: folder permissions, module-related permissions, and page-related permissions. Each of these groups contains a number of overridable methods as outlined in table 1. These methods generally indicate whether a user may perform a particular action (e.g., CanDeleteModule returns a Boolean indicating whether a user may delete a particular module instance), with a few exceptions that update, delete, or apply to collections of permission objects.
Category Module Permissions
... <dotnetnuke> ... <providers> ...
Page Permissions
Listing 2: Default web.config Configuration for the 5.1 Permission Provider (with extraneous details omitted) The existing classes that utilized previously-internal permission functionality have been modified to operate against the new provider. For example, the TabPermissionController was modified to route authorization to the provider instance via a provider.HasTabPermission call (listing 3). The provider itself has assumed the functionality previously implemented directly in the TabPermissionController class, an example of which is shown in listing 4.
DOT NET NUKE CanDeleteFolder CanManageFolder CanViewFolder DeleteFolderPermissionsByUser GetFolderPermissionsCollectionByFolder HasFolderPermission SaveFolderPermissions Table 1: Methods available in the DotNetNuke 5.1 permission provider While an understanding of how the core framework performs authorization is important for those who wish to better understand how DotNetNuke works, it is worth mentioning that a module developer typically will not utilize this provider directly. Instead, modules should continue to use the methods exposed by the relevant controller (e.g., TabController.HasTabPermission). The information presented herein is especially important, however, for those who wish to extend or augment authorization services. Extending Authorization Services Developers who wish to extend or enhance the built-in DotNetNuke authorization services will likely do so via simple inheritance (when base functionality is largely sufficient), decoration (for runtime flexibility in authorization functionality), or adaptation (to allow a foreign authentication system to be used within the DotNetNuke framework). In many cases some combination of these design patterns might be selected for advanced integration requirements.
Brandon Haynes Brandon Haynes (brandonhaynes.org) is a member of the DotNetNuke core team, and serves primarily by providing security-related guidance. He is the chief executive officer at Everysport.net Inc., which delivers enterprise resource planning, web-presence, e-commerce, and integration-related functionality to recreational facilities. A graduate of the University of Illinois at Urbana-Champaign - consistently ranked among the top-five computer-science programs worldwide - Brandon has a long history of intellectual curiosity and accomplishment. With more than twenty years of experience in software development, Brandon’s professional interests are currently focused on the nexus between intellectual property law, technology, and business. He is currently pursuing a graduate degree at Harvard University.
UX
TIP:
Alternatief voor Silverlight Badge The ability to extend DotNetNuke using authorization as a first-class extension point allows for a number of interesting possibilities. For example, the page-related authorization methods may be utilized to allow users belonging to one portal to be able to edit the pages on another. In addition to these obvious direct permission manipulations, more esoteric enhancements might include STS federated authorization using the new Microsoft Geneva framework or even the enabling of Amazon S3 as a backing store for files (although authorization would only be one piece of this particular puzzle). The Bottom Line The decision to offer authorization services as a first-class extension point on the DotNetNuke platform is an important step forward, and allows for customization that is sure to be filled by third-party extensions to cover novel requirements. For developers, an appreciation of how this provider is implemented, utilized, and configured is an important part of understanding the framework as a whole.
For developers, an appreciation of how this provider is implemented, utilized, and configured is an important part of understanding the framework as a whole
Bij het maken van een Silverlight applicatie wordt de ‘install experience’ nog wel eens over het hoofd gezien. De standaard knop die door Visual studio meegeleverd wordt nodigt een bezoeker niet uit om de plugin te downloaden. Probleem hiervan is dat bezoekers deze knop niet begrijpen en denken dat het een banner - dus reclame! - is. Doordat er geen informatie te vinden is over de applicatie nemen ze niet de moeite om de Silverlight plugin te downloaden. Ze kunnen zelfs bang zijn om de plugin te downloaden: hebben we immers niet geleerd niet zomaar alles te installeren? Om te voorkomen dat bezoekers je pagina verlaten zonder de plugin te downloaden en de Silverlight experience die je ze wilt bieden mislopen, hierbij de volgende tips: • Maak het gemakkelijk voor de gebruiker om de plugin te downloaden; maak b.v. een custom button; • Geef duidelijk aan dat Silverlight en de plugin een Microsoft product is en daarmee veilig te downloaden; • Geef een korte beschrijving van de applicatie; • Laat de gebruiker zien wat ze kunnen verwachten, b.v. via een afbeelding als de volgende:
References 1. Howard, R. Provider Model Design Pattern and Specification. Microsoft Developer Network. [Online] March 4, 2004. [Cited: May 24, 2009.]; http://msdn.microsoft.com/en-us/library/ms972319.aspx. 2. Willhite, S. RE: Granular Permissions Stripped from DNN5 Community Edition? DotNetNuke. [Online] April 30, 2009. [Cited: May 24, 2009.]; http://www.dotnetnuke.com/Default.aspx?ThreadId=305055&Sc ope=Posts&TabId=795. •
magazine voor software development 33
DELPHI
Bob Swart
Delphi Prism: Async, Futures en Parallele Mogelijkheden Delphi Prism Delphi Prism is de nieuwe .NET variant van Delphi, en bestaat uit drie onderdelen. Allereerst is daar de Oxygene compiler, gemaakt en onderhouden door RemObjects Software (destijds uitgebracht onder de naam Chrome). Deze Oxygene compileer draait als een plug-in in de Microsoft Visual Studio IDE (de gratis Shell of Express editie is voldoende), en levert daarmee als tweede onderdeel alle designers voor WinForms, WPF, ASP.NET, Silverlight, etc. Behalve een Compact Framework designer dan, omdat die nog steeds vastgebakken zit aan C# of VB. Het derde onderdeel van Dephi for .NET is het enige deel dat nog van CodeGear afkomstig is: de database connectivity in de vorm van de dbExpress drivers en DataSnap mogelijkheden. Op dit moment beperkt tot InterBase en BlackfishSQL voor dbExpress drivers, en alleen nog DataSnap clients, maar in toekomstige uitbreidingen kunnen we extra dbExpress drivers en DataSnap server functionaliteit verwachten.
De database connectivity is het enige onderdeel van Dephi for .NET dat nog van CodeGear afkomstig is Async De Oxygene compiler van Delphi Prism ondersteunt een taal die het async keyword bevat. Dit keyword kan gebruikt worden om bij een methode aan te geven dat deze asynchroon moet worden uitgevoerd; in een aparte thread dus. De declaratie van een asynchrone method kan als volgt zijn: method DoeIetsMetInputParameters(x,y,z: Integer); async;
Let op dat we in Delphi Prism geen procedure of function meer hoeven te schrijven, maar het generiekere method-keyword kunnen gebruiken. En voor een asynchrone methode voegen we het keyword async toe aan het eind van de declaratie. Zoals de naam van de method al doet vermoeden, heeft een async method een voordeel maar ook een aantal beperkingen. Het grote voordeel is uiteraard dat de main thread meteen terugkeert na de aanroep van de async method. De async method zal in een aparte thread uitgevoerd worden, waardoor de main thread meteen verder kan. Nadeel daarbij is dat de main thread niet zal weten wanneer de async method is afgelopen. Daarnaast kan de method alleen maar input parameters bevatten, en geen resultaat terugeven omdat deze natuurlijk pas bekend worden tijdens of na afloop van het uitvoeren
34
MAGAZINE
van de async method. En de main thread die de async method aanroept zou niet kunnen weten wanneer het resultaat daadwerkelijk beschikbaar is. Dit leidt al snel tot de vraag waar en wanneer een async method dan nuttig kan zijn. In praktijk gebruik ik het veel in situaties waar je iets wilt doen dat verder zonder interactie kan worden afgewerkt. Het genereren en/of printen van een report bijvoorbeeld, of het versturen van een e-mail. Dan is het ideaal dat de aanroep van de async method geen verdere tijd in beslag neemt en de main thread gewoon doorgaat. Async Code Behalve async methods kunnen we het async keyword ook gebruiken om van blokken code aan te geven dat deze asynchroon (in een aparte thread) moet worden uitgevoerd. Dit kan bijvoorbeeld als volgt: class method ConsoleApp.Main; var x: Integer; begin x := 42; async begin x := x + x; Console.WriteLine('x = ' + x.ToString); end; x := x / 2; Console.WriteLine('x = ' + x.ToString); Console.ReadKey end;
Ook hierbij geldt dat het async code block geen persistente wijzigingen kan aanbrengen in lokale variabelen. In bovenstaand voorbeeld wordt de variabele X echter wel aangepast. Om hiermee (concurrency) problemen te voorkomen wordt, voordat het async code block start, eerst de gehele context gekopieerd. In dit geval is dat alleen de variabele X, maar dat kan in de praktijk heel wat meer zijn. De output van het stuk code is dan ook x = 84, en x = 21. Misschien in deze volgorde, of wellicht andersom (de x = 21 eerst). Ze kunnen in theorie zelfs door elkaar geschreven staan, maar dat heb ik zelf nog niet kunnen reproduceren. Behalve het feit dat de variabele x in het async code block niet de echte x is, maar een kopie, is het ook niet gegarandeerd wanneer het async code block klaar is. Maar daar is wel een oplossing voor: we kunnen het gehele async code block toekennen aan een System.Action variabele en die gebruiken om op een bepaald moment deze variabele als statement aan te roepen waarna - indien het async code block nog niet klaar is `- gewacht wordt tot deze thread afgerond is. Dat gaat dan als volgt: class method ConsoleApp.Main; var x: Integer; begin
DELPHI
x := 42; var xtask := async begin x := x + x; Console.WriteLine('x = ' + x.ToString); // 21 end; x := x / 2; Console.WriteLine('x = ' + x.ToString); // 84 xtask(); // wachten tot de thread klaar is Console.ReadKey end;
De aanroep van xtask() zal er nu voor zorgen dat er op dat moment gewacht wordt – indien nodig – tot het async code block dat we aan xtask hebben toegewezen is uitgevoerd. Die truc kunnen we helaas niet uithalen met async methods, maar alleen met async code blocks. Futures Nadeel van een async code block blijft dat je wel iets kunt doen of uitrekenen, maar het resultaat niet meer kunt “terugzetten” naar de main thread. Het async code block wordt als het ware in een eigen wereldje uitgevoerd, met een kopie van de omgeving. Als we wel nog iets terug willen geven, maar ook gebruik willen maken
We maken gebruik van een andere Delphi Prism feature: het future keyword``` van de kracht van asynchrone code, dan moeten we een andere Delphi Prism feature gebruiken: het future keyword. Door gebruik te maken van dit keyword geven we aan dat een variabele een waarde heeft die op dit moment nog niet bekend is (bijvoorbeeld omdat de toekomstige waarde ervan in een async code block of method wordt berekend), maar op het moment dat we hem nodig hebben, zal de waarde er zijn (of blijven we erop wachten). Stel bijvoorbeeld dat we voor een hypotheekberekening twee maandbedragen bij elkaar moeten optellen: het rente bedrag en het premie bedrag. Beide bedragen zijn afkomstig uit een wellicht complexe berekening, en het zou zonde zijn om die op elkaar te laten wachten. Zeker als je een dual-core of multi-processor machine hebt en een deel ervan toch niks staat te doen. De syntax van het gebruik van futures voor dit voorbeeld is als volgt: method MainForm.BerekenMaandbedrag: Double; var RenteBedrag: future Double; PremieBedrag: future Double; VerborgenKosten: Double; begin RenteBedrag := async BerekenRenteBedrag; PremieBedrag := async BerekenPremieBedrag; VerborgenKosten := BerekenVerborgenKosten; Result := RenteBedrag + PremieBedrag + VerborgenKosten end;
Zowel de waarde van RenteBedrag als die van PremieBedrag worden in aan aparte thread berekend. Door dat op deze manier te doen zijn de waardes waarschijnlijk nog niet bekend als we aan de verborgen kosten berekening gaan beginnen. Maar dat maakt niet uit, want op het moment dat we alles nodig hebben (bij het Result statement) zal blijken of de async code al klaar is, en de future variabelen hun waarde al hebben, of dat we daar nog even op moeten wachten. Je zou in de code eventueel een soort indicator op het scherm aan kunnen zetten (“waiting…”) voordat je de waarde van een future variabele ophaalt (of erop blijft wachten), zodat de gebruiker in ieder geval door heeft wat er aan de hand is als we hier enige tijd moeten wachten tot de waarde van de future bekend is.
In feite is de xtask variabele die we eerder gebruikten om op een async code block te wachten ook een future. En het “aanroepen” van de variabele is niets anders dan het gebruiken van de waarde, waardoor we blijven wachten indien deze (het resultaat van het code block) nog niet bekend is. PFX Framework: Threads vs. Tasks De async voorbeelden runnen in een eigen thread, in de .NET thread pool. Echter, de volgende versie van het .NET Framework bevat een PFX Framework voor Parallele Extensies, en als we dat erbij gebruiken zal de async method of code als een echte parallele task draaien in plaats van alleen maar als een nieuwe thread in the thread pool. Parallel Een nieuwe feature die verbonden is aan het PFX Framework is het parallel keyword. Hiermee kunnen we aangeven dat een stuk code parallel uitgevoerd moet worden, b.v. een for-loop die in stukken geknipt zal worden. method MainForm.Doe; begin for parallel i: Integer := 0 to 10 do begin BerekenIetsMetI(i); end; end;
Deze code zal alleen compileren als het PFX wordt aangetroffen (de benodigde assemblies zijn nodig), en zal ook alleen draaien op machines waar de PFX aanwezig is. Meer experimenten met PFX en het parallel keyword (waarin we zullen zien hoe de loop in stukken wordt geknipt, en wat er met de i gebeurt) zullen volgen als ook de ontwikkeling aan .NET 4.0 en PFX wat meer gevorderd is. Het goede nieuws is dat Delphi Prism nu al deze komende features ondersteunt. Conclusie In dit artikel heb ik laten zien hoe enkele taalelementen van Delphi Prism werken - taalelementen zoals async en futures die overigens al enige tijd in de Oxygene compiler zaten, dus niet zo heel verschrikkelijk nieuw zijn. Het leuke is in ieder geval dat Delphi for .NET niet langer “achterloopt” op het gebied van .NET features, maar juist nu al gebruik maakt van functionaliteit die nog niet eens af is of volledig beschikbaar. Wie nog vragen of opmerkingen heeft, of Delphi Prism zelf een keer wil proberen, kan me altijd per e-mail bereiken op [email protected]. •
Bob Swart Bob Swart is werkzaam in de IT sinds 1983 en heeft daarbij een voorliefde voor (Turbo) Pascal en Delphi. Bob spreekt regelmatig op (internationale) conferenties over Delphi en heeft honderden artikelen geschreven, alsmede zijn eigen Delphi cursusmateriaal voor Delphi trainingen en workshops. Behalve voor het geven van trainingen, is Bob ook beschikbaar voor consultancy, coaching, ontwerp- en bouwwerkzaamheden, of andere ondersteuning op het gebied van software ontwikkeling met Delphi - voor zowel Win32 als .NET. Sinds de zomer van 2007 is Bob ook reseller van CodeGear producten zoals Delphi en RAD Studio. Bob Swart Training & Consultancy is gevestigd in Helmond Brandevoort, en beschikt over een eigen trainingsruimte van ruim 42 vierkante meter, inclusief een testlab voor alle mogelijke toepassingen. De voorliefde van Bob voor Pascal en Delphi heeft hij ook tot uiting laten komen in de namen van zijn kinderen Erik Mark Pascal en Natasha Louise Delphine.
magazine voor software development 35
Advertentie Microsoft
Advertentie Microsoft
INFORMATION
WORKER
Gustavo Velez
Spell Check in
SharePoint 2007 SharePoint is Microsoft’s server dedicated to the interchange of information and thus a system where writing is a critical component. As with other Office family products, Microsoft Office SharePoint Server (MOSS) provides tools to check and correct the orthography in texts. The service is used in some default components of MOSS, allowing developers to utilize it in custom software, such as WebParts or specialized pages. The SharePoint Object Model offers spell check as a webservice to control the orthography in different languages, providing suggestions and alternatives to correct errors. It’s important to point out that the service is not equal to the system used by other Office products. Consequently - as an on-line server - the SharePoint system is less advanced, and provides limited functionality in contrast to the default spell check of Word or Excel; for example, there is no possibility to check grammar nor does it offer a thesaurus. MOSS uses spell check in various locations; for example, in each List where there are text fields there is a “Spelling” button in the horizontal tools bar. When you click on the button, a popup window appears allowing you to loop through the words on the page, making a comparison with the internal dictionary and displaying words with errors, together with suggestions to rectify them.
The same window allows users to select the language and dialect for the corrector (the configured default language is initially used). The corrector only compares words with the internal dictionary, without grammar or syntax corrections. Another disadvantage is that it is not possible to add new words to the dictionary; to circumvent that situation a list of words can be created that will be ignored by the corrector, in fact a specially-designed dictionary . Webservice All the corrector’s functionality is available as a default Microsoft Office SharePoint Server (MOSS) webservice. The “SpellCheck.asmx” webservice has three input parameters: • “chunksToSpell”: an array of strings with the texts to check; • “declaredLanguage”: an integer that gives the language identifier to be used by the checker. This identifier is not the same as that used by SharePoint for its internal configuration; the values can be found on the Microsoft site http://msdn2.microsoft.com/en-us/library/0h88fahh.aspx; use the value of the column “decimal value”; • “useLad”: a boolean that indicates if the spell check should detect the MOSS default language. At first glance, the webservice is very straightforward to apply following a simple sequence: create a reference, give the service user credentials, configure the input parameters and execute the service. Unfortunately, the MOSS implementation has two crucial errors that hamper its use. First, if the parameter “useLad” is employed in such a way that spell check uses the default MOSS language, the checker will apply the language identifier of MOSS, not the target language identifier to be checked. This is not a problem in English, because the two language identifiers are the same (1033), but in other languages it is always different. For example, in Spanish the webservice will use 3082 (MOSS Language Identifier for Spanish) instead of 1034 (Language Identifier for Spanish Spell Check). Consequently, the corrector never provides accurate results in languages other than English.
At first glance, the webservice is very straightforward to apply To avoid this problem, declare the “useLad” parameter as false and give the correct language identifier in the “declaredLanguage” parameter. The downside to this workaround is that you always need to use a constant identifier or you must create a lookup table with the necessary logic to link the MOSS language identifier to the spell check language identifier.
Fig. 1: SharePoint List with the Spelling button and popup window
38
MAGAZINE
The second problem may be more serious. Because the webservice has irregularities in the code, vocabulary suggestions are not
INFORMATION
WORKER provided. To resolve this problem it is obligatory to call the webservice using the methods of the class “HttpWebRequest” from the NameSpace “System.Net”, creating the SOAP envelope and XML query manually, as outlined in the following paragraphs. In the source code where the webservice will be called, create references (“using” in CSharp) to System, System.IO, System.Net, System.Text and System.Xml. The code lines below call the webservice using the HttpWebRequest class: StringBuilder myStringBuilder = new StringBuilder(EnvelopeSoap); int index = myStringBuilder.ToString(). IndexOf(""); myStringBuilder.Insert(index, MyQuery); XmlDocument EnvelopeSoapXml = new XmlDocument(); EnvelopeSoapXml.LoadXml(myStringBuilder.ToString()); HttpWebRequest myQueryWeb = (HttpWebRequest)WebRequest.Create(UrlChequerWS); myQueryWeb.UseDefaultCredentials = true; myQueryWeb.Headers.Add("SOAPAction", AccionSoap); myQueryWeb.ContentType = "text/xml;charset=\"utf-8\""; myQueryWeb.Accept = "text/xml"; myQueryWeb.Method = "POST"; using (Stream myStream = myQueryWeb.GetRequestStream()) { EnvelopeSoapXml.Save(myStream); } IAsyncResult ResultAsync = myQueryWeb.BeginGetResponse(null, null); ResultAsync.AsyncWaitHandle.WaitOne(); string ResponseSoap = string.Empty; using (WebResponse ResponseWeb = myQueryWeb.EndGetResponse(ResultAsync)) using (StreamReader myStreamReader = new StreamReader(ResponseWeb.GetResponseStream())) { ResponseSoap = myStreamReader.ReadToEnd(); }
Listing 1: Calling the webservice using the HttpWebRequest class In the code, create a StringBuilder that contains the SOAP call to the webservice. The SOAP envelope is directly added by using the syntax: static string EnvelopeSoap = @"<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'> <soap:Body> ";
Listing 2: Adding the SOAP envelope Then, immediately before closing the envelope, add the XML query with the following syntax: static string MyQuery = @"<SpellCheck xmlns='http://schemas.microsoft.com/sharepoint/publishing/ spelling/'> <string>This rou <string> has an error <declaredLanguage>1033 <useLad>false ";
There are two strings in the query that need to be adjusted, namely “This rou” and “has an error”. The language identifier is declared (1033) and the MOSS language identifier must be blocked with the code “useLad=false”. As stated earlier, at this point you need to define the spell check language identifier if you are using a language other than English. After the creation of the envelope and the query in a string, they are converted to a XmlDocument that can be used in the service call. The object “myQueryWeb” (of the “HttpWebRequest” type) is created using as input parameter the URL of the spell check webservice: static string UrlChequerWS = "http[s]:// ServerName/_vti_bin/SpellCheck.asmx";
The object receives the default credentials of the user, the content type, the type of the method to be used, and finally, the form to send the envelope. The SOAP action type is added to the header in the following manner: static string AccionSoap = "http://schemas.microsoft.com/”+ “sharepoint/publishing/spelling/SpellCheck";
With this action, the envelope is assigned to the webservice call and using the interface, IAsyncResult, the anticipated result is asynchronous; this avoids a delay in the functioning of SharePoint while awaiting the response. Finally the call to the webservice is executed and the results come back in the form of a “WebResponse” object that is converted to a “StreamReader” which needs to be interpreted; the final result is a XML string “ResponseSoap”. The response of the example is in the form: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd=http://www.w3.org/2001/XMLSchema <soap:Body> <SpellCheckResponse xmlns="http://schemas.microsoft.com/sharepoint/ publishing/spelling/"> <SpellCheckResult> <errorCode>Ok <detectedLanguage>1033 <spellingErrors> <SpellingErrors> 0 <word>rou UnknownWord3 <spellingSuggestions> <SpellingSuggestions> <word>rou <sug> <string>roue <string>rout <string>roux <string>roe <string>row <string>rob
Listing 3: XML query
magazine voor software development 39
INFORMATION
WORKER In the SOAP response note that the corrector - in the first string (chunkIndex=0) - has identified an unknown word (FlaggedWordword=rou), that probably has an error in the third character (offset=3). The suggestions section indicates that the unidentified word has six possible corrections in the dictionary. The second string is not found in the response, signifying it is accepted by the corrector.
Finally create the content controls where the user can write a text to be checked. In the example it is one text box, but as many as are required can be created and spell check will loop through all of them scanning for errors. Use the syntax: Some text to check
The remaining code implementation is elemental and can be used in each SharePoint component (WebParts, WebControls, Web pages, etc). The last task is to extract the relevant information from the XML response and present it to the user in the desired way.
The button at the end will call the JavaScript and spell check: Spell Checker
Call the ASPX from MOSS using the URL (“http[s]://ServerName/_layouts/ spellcheck.asp). After clicking the button, a popup window will appear showing the incorrect words and the correction suggestions:
In the event the language cannot be detected, the ASPX-page initially shows a menu to select it from the available languages list. The default MOSS corrector page can be initiated from each ASPXpage as well. To view the process, create a new file with the .ASPX extension (“spellcheck.asp” for example) in the directory “C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\”. Open the file with any ASCII text editor (Note Pad) and add a reference to the SharePoint assembly: <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,
Fig. 2: ASPX custom page using default spell check
PublicKeyToken=71e9bce111e9429c" %>
Then create references to the files with the JavaScripts: <SharePoint:ScriptLink language="javascript"
With the code above, all the texts found on the page will be scanned. If you want to exclude any of the text from correction, use the attribute “excludeFromSpellCheck” in the HTML syntax. For example, to create a text box controlled by the server where no spell check is desired, use:
And a JavaScript section to call Spell Check: <script language="javascript" type="text/javascript"> function CheckSpell()
Note: The attribute ”excludeFromSpellCheck” can be used in a similar way in client-side controls using HTML code. If necessary, it is also possible to modify the files SpellChecker.aspx, SpellChecker.js and/or SpellCheckerEntirePage.js, but it is not recommended or effective as any future Microsoft Service Pack can revert the files to the original state.
Dictionary modifications MOSS has default correction dictionaries for each supported language but it is impossible to modify them. However it is possible to add words in such a way that they are not identified as errors.
The following process creates a type of custom dictionary for these words: • Create a new Document Library “Spelling” at the root of the Portal; • Create a text file “Custom Dictionary.txt” locally; • Add all the words that should not be recognized as spelling errors in the text file, one word per line; • Upload the text file to the newly-created Library above. Only one “Custom Dictionary” Document Library is possible for each site collection; the names of the library and text file need to be exactly as indicated and the library must be located at the root of the portal. Conclusion A valuable feature of SharePoint 2007 is the introduction of an on-line spell check program. The system is presented as a webservice, but the feature has some errors that need workarounds to function properly. Spell Check is a default component of Microsoft Office SharePoint Server (MOSS) but is not available in Windows SharePoint Services (WSS). The system is a welcome adaption and covers the supported SharePoint Languages. It is effective for detecting orthography errors and provides correction suggestions; however the downside is that it lacks a grammar and a thesaurus and the dictionaries cannot be modified. The default window of the corrector can be used in custom components inside SharePoint, as ASPX-pages, with the inclusion of a few lines of code. Finally, although the dictionaries cannot be modified, it is possible resolve this inadequacy by creating a list of words to be excluded from Spell Check control. •
Gustavo Velez Advertentie Aladdin Gustavo Velez is a Manager in the Solutions Development department at Avanade Netherlands, a global consultancy company. He has many years experience developing Windows and Office applications, and more than six years of daily programming experience with SharePoint. The author's articles can be found in many of the leading trade magazines in English, Dutch and Spanish. He is also pleased to be Webmaster of http://www.gavd.net/servers, the only Spanishlanguage site dedicated to SharePoint. Spanish-language readers may want to consult Velez's book, “Programación con SharePoint 2007”. His new book “Workflows and SharePoint: Going with the flow” has been published recently. Velez has been awarded MVP status in the area of SharePoint MOSS.
Noteren in agenda: 19 & 20 oktober a.s. SDN Conference
.NET C#
Bert Dingemans
Fun met Webparts in ASP.Net - Deel 2
User Controls en Webparts Inleiding Webparts zijn een nieuw soort besturingselementen in webapplicaties geïntroduceerd door Microsoft in ASP.Net versie 2.0. Webparts zijn vooral bekend vanwege de toepassing binnen Sharepoint en MOSS. In deze twee platformen zijn webparts één van de mogelijkheden om eenvoudig toegang te krijgen tot legacy systemen. Maar ook in maatwerk ASP.Net applicaties zijn webparts “fun”. In een moderne webtoepassing is het meer en meer gebruikelijk dat gebruikers zelf een indeling kunnen maken van hun “eigen pagina”. Kijk naar sites als hyves.nl en de verschillende elementen lijken verdacht veel op webparts. Dit artikel is een tweede deel in een serie artikelen over webparts. In het eerste deel zijn we ingegaan op de basisfunctionaliteit. In dit artikel zullen we ingaan op het werken met user controls binnen webparts, de wizard control en het gebruik van helper classes bij specifieke webparts. De webparts zijn ontwikkeld in C#. Dat is voor mij niet mijn dagelijkse programmeertaal (Vulcan.Net en VB.Net). Reden om hiervoor te kiezen is het feit dat de Sharepoint installatie van webparts die niet geschreven zijn in C# lastig is. De webparts zijn op deze wijze in een handomdraai geschikt te maken voor Sharepoint.
Ook in maatwerk ASP.Net applicaties zijn webparts “fun” Extra controls In het vorige artikel zijn we ingegaan op de basis webparts voor het muteren van gegevens in tabellen in onze database. Vaak willen we echter een aantal controls die een andere opmaak hebben en een aantal bijzonder controls tonen. In ons voorbeeld maken we een webpart waarmee we gegevens van een organisatie tonen in een detailview control en willen we daarnaast een treeview vullen met gekoppelde gegevens van deze organisatie. Hierbij maken we zoveel mogelijk gebruik van onze helper klassen. Deze klassen zorgen voor een standaard afhandeling van gezamenlijke functionaliteit, die hergebruik en stabielere code oplevert. In de eerste afbeelding is te zien hoe dit webpart eruit ziet voor de gebruiker.
De control bestaat uit een zoek- en een keuze-dialoog. Na zoeken wordt de keuzelijst gevuld, wordt hier een waarde gekozen en dan worden de detailview en de treeview gevuld. Onderstaande code toont hoe in het CreateControls-event een aantal specifieke controls aangemaakt worden. Er is hierbij gekozen voor de opzet van de controlfactory om het creëren van controls op een centrale plaats te houden. ControlFactoryHelper.CreateTextBox(this.Controls, "zoeknaam", "", 500); this.organisatie_id = ControlFactoryHelper.CreateDropDownList( this.Controls, "organisatie_id", "", 400); this.relaties = ControlFactoryHelper.CreateTreeview( this.Controls, "relaties", "Associaties"); this.detail = ControlFactoryHelper.CreateDetailsview( this.Controls, "detail", "Detail");
Zijn de controls binnen het webpart beschikbaar, dan kunnen de treeview en de detailview gevuld worden nadat er op de knop kiezen geklikt is. In onderstaande code wordt de functionaliteit getoond. protected void verwerk_kiezen(Object sender, EventArgs e) { NameValueCollection para = new NameValueCollection(); String sql = ""; DRGHelper objHelper = new DRGHelper(); para = ControlFactoryHelper.Controls2Collection( this.Controls); this.relaties.Nodes.Clear(); sql = "SELECT CONTACTPERSOON.contactpersoon_id, CONTACTPERSOON.voornaam & ' '& CONTACTPERSOON.achternaam FROM CONTACTPERSOON WHERE organisatie_id = #organisatie_id#"; objHelper.Statement2TreeView(sql, this.relaties, "Contactpersonen", para); sql = "SELECT project_id, project_naam
42
MAGAZINE
.NET C# FROM PROJECT, CONTACTPERSOON WHERE PROJECT.contactpersoon_id = CONTACTPERSOON.contactpersoon_id AND CONTACTPERSOON.organisatie_id = #organisatie_id#"; objHelper.Statement2TreeView(sql, this.relaties, "Projecten", para);
In ons voorbeeld zullen we medewerkers aan een project koppelen. Dit proces bestaat uit een aantal stappen waarbij als laatste stap projecten en medewerkers aan elkaar gekoppeld worden. In onderstaande afbeelding wordt het werkproces weergegeven.
sql = "SELECT * FROM ORGANISATIE WHERE organisatie_id = #organisatie_id# "; objHelper.Statement2DetailsView(sql, this.detail, "Overzicht", para); }
Duidelijk te zien is hoe de helper klasse ons behulpzaam is bij het inkapselen van functies die voor hergebruik in aanmerking komen. Als voorbeeld in onderstaande code wordt een treeview gevuld op basis van een SQL-statement. public void Statement2TreeView( string sql, TreeView tree, string naam, NameValueCollection colPara) { DataSet ds; TreeNode kind; sql = this.verwerkParameters(sql, colPara); ds = Statement2DataSet(sql); if (ds.Tables.Count > 0) { kind = new TreeNode(naam); foreach (DataRow row in ds.Tables[0].Rows) { kind.ChildNodes.Add(
We hebben het onszelf iets moeilijker gemaakt doordat we, als we direct op project willen zoeken, een stap moeten overslaan; in het andere geval willen we dat alleen de projecten van een organisatie getoond worden. Zodra een webpart in een ASCX-file wordt geplaatst wordt het mogelijk om een groot aantal eigenschappen in te stellen via Visual Studio. In onderstaand codevoorbeeld zijn de belangrijkste eigenschappen opgenomen voor de wizard. Door de hoeveelheid aan eigenschappen is het mogelijk om de wizard echt helemaal naar eigen inzicht te configureren.
new TreeNode(row[1].ToString(), row[0].ToString()));
Allereerst wordt een dataset aangemaakt op basis van het SQL-statement en vervolgens wordt iedere datarow toegevoegd aan de treeview. Hierbij is een aanname gedaan dat het select-statement slechts twee kolommen in de resultset heeft. Bij uitbreidingen van de library zal hier waarschijnlijk met parameters gewerkt gaan worden die een en ander afhandelen op een meer generieke wijze.
User controls In ASP.Net kunnen user controls ontwikkeld worden. Deze user controls gaan dan fungeren als een container object voor meerdere besturingselementen. Eigenlijk is een webpart niets anders dan een user control waar een aantal extra eigenschappen aan is toegevoegd; met name de koppeling met de webpart-manager en de zones zijn webpart specifiek.
Eigenlijk is een webpart niets anders dan een user control In ons voorbeeld zullen we een besturingselement implementeren dat net als de webparts een aantal aardige extra’s biedt, namelijk de wizard control. Deze control maakt het mogelijk om de gebruiker middels een aantal stappen door een complex werkproces te begeleiden.
<WizardSteps>
De elementen zijn waarschijnlijk duidelijk, maar toch een korte toelichting: • De Click events zijn ingesteld om een functie aan te roepen die voor de verdere afhandeling zorgt; • DisplaySideBar is een aardige eigenschap die het mogelijk maakt om de gebruiker te tonen waar hij of zij zich in het wizard proces bevindt. In de afbeelding op pagina 45 een voorbeeld; • ActiveStepIndex geeft aan bij welke pagina men dient te beginnen (PS: Kan iemand verzinnen waarom je niet bij de eerste pagina wilt beginnen??) Binnen de wizard worden vervolgens de pagina’s gedefinieerd in de vorm van wizard-steps. Binnen deze wizard-steps kunnen eenvoudig de gewenste controls geplaatst worden. In het codevoorbeeld hieronder is één wizard-pagina getoond in ASP.NET code.
magazine voor software development 43
.NET C# protected void Wizard1_NextButtonClick(object sender, WizardNavigationEventArgs e) { NameValueCollection para = new NameValueCollection(); DRGHelper objHelper;
Zoeken naar*
Zoek naar
objHelper = new DRGHelper(); switch( e.CurrentStepIndex ) { case 0: if(this.Organisatie_Project.Text == "Organisatie") { Wizard1.ActiveStepIndex = 1; para.Add("organisatie_naam", this.ZoekNaar.Text); objHelper.Sql2ListControl ("SELECT organisatie_id as valuecolumn, organisatie_naam as displaycolumn FROM ORGANISATIE WHERE organisatie_naam LIKE '%#organisatie_naam#%' ORDER BY 2", organisatie_id, "valuecolumn", "displaycolumn", para); } else { Wizard1.ActiveStepIndex = 2; para.Add("project_naam", this.ZoekNaar.Text); objHelper.Sql2ListControl ("SELECT project_id as valuecolumn, project_naam as displaycolumn FROM [PROJECT] WHERE project_naam LIKE '%#project_naam#%' ORDER BY 2", project_id, "valuecolumn", "displaycolumn", para); } break;
In het event is op te vragen in welke stap van de wizard we zitten. Is dit de eerste stap, dan kunnen we opvragen of de gebruiker heeft gekozen voor organisatie of project. In het eerste geval wordt de keuzelijst voor organisaties gevuld en getoond, in het andere geval wordt de project-keuzelijst gevuld. Opvallend is hierbij dat door het instellen van de activestepindex bepaald kan worden of een pagina moet worden overgeslagen. In het geval van de projectkeuze is namelijk de stap met de organisatie gegevens niet meer relevant. Op de laatste pagina van de wizard verschijnt de Voltooien-knop. Deze knop zal een aantal bewerkingen uit moeten voeren op onze database. Hierbij zijn onze helper-klasses weer relevant. In de onderstaande code een voorbeeld van deze verwerking; in de voorbeeldtoepassing is de gehele code voor deze wizard control te vinden. protected void Wizard1_FinishButtonClick(object sender, WizardNavigationEventArgs e) { DRGHelper objHelper = new DRGHelper(); NameValueCollection para = new NameValueCollection(); String sql =
Zoals te zien is zijn niet alleen controls binnen een wizard-step te plaatsen maar ook allerlei opmaakelementen. In dit voorbeeld is een tabel opgenomen die ervoor zorgt dat de elementen binnen een pagina mooi uitlijnen. Daarnaast kunnen extra controls toegevoegd worden, zoals de validatie-controls van ASP.Net.
In de workflow hebben we ons ten doel gesteld dat als er voor een project gekozen wordt de vraag omtrent de organisatie wordt overgeslagen. Dit dient in de programmacode van de control afgehandeld te worden. Onderstaande code toont hoe dit geïmplementeerd wordt in het event voor de Volgende-button in de wizard.
44
MAGAZINE
for (int i = 0; i < medewerker_id.Items.Count; i++) { if (medewerker_id.Items[i].Selected) {
voorbeeldtoepassing met een uitwerking van bovengenoemde controls. Zie de SDN-website of de website www.dla-os.nl (onder artikelen) voor deze source-code. •
Door gebruik te maken van de helper-klasse wordt het eenvoudig mogelijk om een collectie met naam/waarde combinaties te vullen en vervolgens een SQL-statement naar de database te sturen op een standaard manier op basis van de helper-klasse. Samenvatting In dit artikel is ingegaan op een aantal extra mogelijkheden van webparts. Webparts zijn niet alleen te gebruiken voor standaard controls, ook eigen combinaties binnen een webpart zijn eenvoudig mogelijk. Door de opzet van onze helper-klassen kunnen we eenvoudig eigen opmaak gebruiken en toch gebruik blijven maken van standaard functies in onze helper-klassen. Daarnaast is het binnen webparts goed mogelijk om user controls te definiëren. Dit biedt extra mogelijkheden om de opmaak en volgorde van controls binnen de webpart helemaal naar eigen inzicht in te richten. In dit artikel hebben we als voorbeeld een wizard ontwikkeld bestaande uit een aantal stappen. Bij dit artikel hoort een
Bert is als software architect werkzaam binnen het maatschap FreeIT, een maatschap van ICT professionals. Bert heeft een voorliefde voor Model Driven Development en het genereren van software. Zo heeft hij CASE tools ontwikkeld in Visual Objects als DLArchitect en DLA Work in Process. Er zijn freeware versies van deze tools beschikbaar op de dla-os website. Bert heeft een weblog op www.dla-os.nl.
UX
TIP:
Silverlight Tips of the Day Mike Snow is een senior SDET Lead bij het Web Tools Team van Microsoft en houdt zich onder andere bezig met Silverlight tools. Op zijn blog plaatst hij regelmatig “Tips of the Day” over Silverlight en gaming die je kunnen helpen bij het game programmeren. Het Blog is te vinden op http://silverlight.net/ blogs/msnow/.
Advertentie Sybase / iAnywhere
MAGAZINE
DELPHI
Cary Jensen
Introduction to Language Integrated Query with Delphi Prism: Part 1 Language Integrated Query, or LINQ (pronounced link), is a declarative programming language developed by Microsoft for the .NET framework. In a declarative programming language you specify what result you want to achieve without needing to specify how those results are produced. SQL SELECT statements are an example of declarative programming. With a given SQL statement you specify what data you want returned. It is up to the SQL engine to determine how that data is derived.
With a given SQL statement you specify what data you want returned Traditional Delphi, on the other hand, is categorized as imperative programming. When writing Delphi code, you generally describe in detailed terms how you want the result obtained. You do this using control structures, expressions, and explicit calls to functions and procedures. This article is designed to provide you with a general overview of using LINQ with Delphi Prism, the latest .NET development tool from Embarcadero Technologies and RemObjects. For a more detailed discussion of LINQ and its related topics, refer to the latest version of the .NET framework SDK.
Turning our attention to the general case of LINQ, LINQ queries can be divided into three basic parts. These are: • A queryable source of data • A query definition • Query execution The following sections describe each of these parts in greater detail. The code samples shown, and the example Delphi Prism project that appears in the accompanying screenshots, is called LINQExamples, and can be downloaded from the SDN-site through the online version of this article. Queryable Sources of Data In strictly .NET terms, a queryable source of data is any object that implements the generic IEnumerable interface, which includes List and Dictionary. In Delphi Prism, this also includes any collection that is declared as a sequence, as well as arrays (both fixed and dynamic). Some of the LINQ technologies, such as LINQ to DataSet and LINQ to XML, are implemented through LINQ providers, which are support classes that enable LINQ operations. These providers usually include special methods that provide access to an IEnumerable reference based on an object that otherwise does not support this interface. For example, LINQ to DataSet provides the AsEnumerable extension method to the DataTable class. The following variable declaration defines a simple queryable data source. In this case, the data source is a sequence. var seq: sequence of Integer := [1,2,3,4,5,6,7,8,9];
Query Definitions using Query Syntax A query definition consists of two parts, a query variable and a query expression. Furthermore, query expressions are defined using one of two techniques. The first is called query syntax, and the second is called method syntax. This section describes query syntax. Method syntax is described later in this article.
Overview of LINQ Language Integrated Query comes in a variety of different flavors, depending on the version of the .NET framework you are compiling against. For example, there is LINQ to Objects, LINQ to DataSets, LINQ to SQL, and LINQ to XML. Each of these technologies, which were introduced in .NET 3.0, provides classes you can use in conjunction with LINQ to work with objects in various domains of the .NET framework.
Query expressions are defined using a combination of one or more LINQ statements, comparison operators, extension methods, and value references. At a minimum, a LINQ query includes a ‘from’clause, which defines an alias and a data source. Consider the following code segment:
In addition, some of the major new features that are emerging in .NET also are LINQ related. One example is LINQ to Entities, which is part of the Entity Framework. The Entity Framework is one of the latest database technologies being promoted by Microsoft.
The first line of code defines the data source, and the second line defines the query. The query in this case selects all items from the sequence numbers, as can be seen in the following figure (the code that populates the list box has not been shown yet).
46
MAGAZINE
var numbers: sequence of Integer := [1,2,3,4,5,6,7,8,9]; var myquery := from c in numbers select c;
DELPHI
expressions are discussed briefly later in this article). Finally, similar to SQL, LINQ queries can include subqueries.
Defining a query and executing a query are two distinct steps (though as you will learn shortly, Delphi Prism provides a mechanism for combining these operations)
Fig. 1: The LINQExamples project The ‘c’ in the preceding query is an alias, and it is used to reference items in the numbers sequence. The ‘select’-part of the query, which contrary to SQL standards, appears at the end of the statement, defines what is returned by the query. In this case, the query returns each of the items in the sequence. The preceding query really just creates another sequence that is no different from the one that it queried. Most LINQ queries, by comparison, either select subsets of the data, perform data transformations, or other similar operations. The following query includes a ‘where’-clause that limits the myquery sequence to those numbers between 4 and 8, inclusively. As you can see, the alias is essential for performing this operation.
Executing LINQ Queries Defining a query and executing a query are two distinct steps (though as you will learn shortly, Delphi Prism provides a mechanism for combining these operations). Specifically, the query defined in the preceding section identifies what the query will do, but does not execute the query. In LINQ this is referred to as deferred execution. You cause the query to be executed by iterating over it using a for each loop. Each execution of the loop brings back another instance of whatever the query returns. For example, if you used a for each loop to iterate over the query shown in the preceding example, each iteration of the loop would return a different instance of the anonymous type. The following is the entire code sequence (short as it is), which shows how the Value members of the returned objects are assigned to the list box (which is name ResultList in this example).
var numbers: sequence of Integer := [1,2,3,4,5,6,7,8,9];
ResultsList.Items.Clear;
var myquery := from c in numbers where
var numbers: sequence of Integer := [1,2,3,4,5,6,7,8,9];
(c >= 4) and (c <= 8) select c;
var myquery := from c in numbers select new class(Value := c * c);
In both queries shown so far in this section, the select-clause could have been omitted. Specifically, if what we are returning is exactly the same type of item as in the sequence, we can omit the select-clause altogether. In other words, omitting the select clause is somewhat similar to a SELECT * clause in a SQL query. The following query, which does not have a select-clause, produces the same result as the preceding one.
for each m in myquery do ResultsList.Items.Add(m.Value.ToString);
When written to the list box, this query result looks like the following.
var numbers: sequence of Integer := [1,2,3,4,5,6,7,8,9]; var myquery := from c in numbers where (c >= 4) and (c <= 8);
The select clause in LINQ is required when you are querying objects with one or more members, and want to only return a subset of its members, or want to return transformed data. For example, the following query returns a sequence of objects whose values are equal to the square of the original values. The objects returned by the select clause, in this case, are anonymous types. (An anonymous type of an object is never explicitly declared, one of many advanced language features supported by Delphi Prism.) var numbers: sequence of Integer := [1,2,3,4,5,6,7,8,9]; var myquery := from c in numbers
Fig. 2: Data from an anonymous type returns from a LINQ query
select new class(Value := c * c);
LINQ queries can be relatively complicated. In addition to the from, where, and select keywords shown here, Delphi Prism also supports the following LINQ operations: order by, group by, join, with, take, skip, reverse, and distinct. In addition, LINQ queries can make use of lambda expressions, both in the where and select parts (Lambda
Similar to the alias in the query definition, the for each loop defines a local variable that holds the value of the item that is returned in each iteration. In the preceding code, this variable is named m. In most cases, Delphi Prism uses type inference to determine the type of this variable (which is fortunate in this case, since we used an anonymous type).
magazine voor software development 47
DELPHI
Not all LINQ queries use deferred execution. To cause the results of the query to retrieve immediately, you can use either the IEnumerable methods ToList or ToArray. An example of this is shown in the following code. var numbers: sequence of Integer := [1,2,3,4,5,6,7,8,9]; var MyResults: Array of Integer := (from c in numbers where (c >= 4) and (c <= 8)).ToArray(); for each val in MyResults do ResultsList.Items.Add(val.ToString);
Similarly, if you use any of the grouping operations, such Average, Count, First, and so forth, the query is executed immediately. This is demonstrated in the following code. var numbers: sequence of Integer := [1,2,3,4,5,6,7,8,9]; var MyResults: Double := (from c in numbers).Average();
var Customers := GetCustomers; var query := Customers.Where(Customer -> Customer.Age > 10). Where(Customer -> Customer.Age < 40). Where(Customer -> Customer.Active);
Lambda Expressions Lambda expressions are anonymous functions that can accept zero or more parameters, and typically return a value. The parameters appear on the left-hand side of the lambda operator, and are enclosed in parentheses (unless there are zero or one parameters, in which case the parentheses are optional).
Lambda expressions are anonymous functions that can accept zero or more parameters, and typically return a value
MessageBox.Show(MyResults.ToString);
LINQ Query Method Syntax As mentioned earlier, LINQ query expressions can be defined using both query syntax and method syntax. Method syntax is implemented through extension methods on the System.Linq.Enumerable class, and corresponds roughly to the LINQ query syntax operators. The truth is that when you use query syntax in your query expressions, the compiler converts these into method syntax. Another way to put this is that any query that you can write using query syntax can also be defined using method syntax. Interestingly, the opposite is not true. Specifically, there are some queries that you must define using method syntax, as there is no equivalent in query syntax. Unlike in query syntax, there are no aliases in method syntax. Instead, the methods are called on your IEnumerable object directly, using dot notation. These operations are typically performed in a single expression. In other words, using dot notation, method syntax sometimes includes a number of method calls, implemented through a chain of calls. This is demonstrated in the following code segment, which produces a query identical to the first one listed in the preceding section “Query Definitions using Query Syntax.” var myquery := numbers.where(c -> c >= 4). where(c -> c <= 8).orderby(c -> c).select(c -> c);
NB: The preceding statement is a single statement, even though it has been wrapped onto two lines due to the limits of the column space in this article. Fortunately, this wrapped form can still be compiled by Delphi since its compiler is designed to ignore white space. Also, the arguments of the where, orderby, and select clauses are lambda expressions. Lambda expressions are discussed in the following section. As with the query expressions, the select method call here is not necessary, since there is no transformation being performed on the returned items. The syntax is a little different when your sequence contains more complex data. This can be seen from the following code, which is the method syntax version of the code segment described in the following section “LINQ to Objects.”
48
MAGAZINE
The parameters, if present, are being followed by the lambda operator, which is the -> character sequence in Delphi Prism (C# uses => as the lambda operator). When reading a lambda expression aloud, the lambda operator is spoken “goes to.” As a result, the first expression in the preceding query expression is read “customer goes to customer dot age greater than 10.” The expression on the right-hand side of the lambda operator includes either an expression or a statement. This is the value that is returned by the lambda expression (if the statement is an expression lambda). It is possible, however, to include an expression block that does not resolve to an expression. These lambdas are called statement lambdas. Statement lambdas should not be used in method calls when using method syntax. In most cases, lambda expressions take advantage of the compiler’s ability to infer the type of the input parameters. Summary This article has provided you with a brief introduction to the composition of language integrated queries. In part 2 of this article, numerous examples of LINQ queries are shown, including technologies such as LINQ to Objects, LINQ to DataSets, and LINQ to XML. •
Cary Jensen Cary Jensen is President of Jensen Data Systems, Inc., a training and consulting company that won the 2002 and 2003 Delphi Informant Readers Choice Awards for Best Training. He is an award-winning author of 20 books, including Advantage Database Server: A Developers Guide (2007, Sybase), Building Kylix Applications (2001, Osborne/McGraw-Hill), JBuilder Essentials (1998, Osborne/ McGraw-Hill), and Delphi in Depth (1996, Osborne/McGraw-Hill). For information about onsite training or consulting services, you can contact Cary at [email protected] or visit his web site at www.JensenDataSystems.com.
GENERAL
Interesting Things: Creative Plumbing For as long as I’ve been in this business, I’ve heard a lot of managers, architects and other non-coders compare software development to construction. In this metaphor the architect creates the design and hands it over to the contractor, who does the work and the creative part ends with the architect handing over the design to the builders. Now doesn’t that sound like a software development project to you? City planning and blueprints There is a lot of terminology in software development originating from this metaphor. Never heard an architect talking about the systems landscape, or that architecture is like city planning? And where did you think the term architecture itself originates from? Or the fact that we are building software? And aren’t we still discussing software construction and layered architectures? Did you know that in SAP projects people still refer to the design as a blueprint? At first sight this metaphor makes sense. However, what has worried me about this metaphor over the past twenty years is that the creative part ends after the design is delivered by the architect. No pretty sight for developers eyes. In construction, this seems to be true. It’s the main reason construction is done by low educated workers against equally low wages. Ignoring creativity Converting this metaphor to software development, as a consequence, when the software architecture and design is done well, coding could be done elsewhere in the world by low educated people much cheaper than we are. This is where I highly dislike the construction metaphor. It considers coding as non-creative and repetitive work. It simply ignores creativity. Moreover, it also surpasses the fact that, as agile projects prove, the quality of software improves at high pace when there’s regular contact with the customer during development, much like the idea that during construction of your new house you regularly visit the construction site and pass comments to the contractor. So this is where it all comes down to: the idea that an architect sits at his desk and actually designs the software at his desk, after having consulted the customer. This is where creativity ends, and building the software begins, according to plan. From this crippled metaphor traditional methodologists have fantasized the living daylights out of us, poor developers. We were made to believe that software development is a craft rather than creative work. If the architecture is set out correctly and presented nicely, building the software is low skilled, repetitive work. And hence, it can easily be outsourced, off shored and done by low educated third world software developers. So if all this would be true, and construction is a good metaphor for software development, why is it not all projects are delivered successfully, on-time, and on-budget? Watching plumbers I’ll tell you why. Last week my new house was delivered by my contractor. Since then, all of a sudden I’m surrounded with lots of activity in and around the house. This activity falls in the fore mentioned category of low skilled, non-creative work. The house is crowded with plumbers, carpenters, friends with DIY capacities, gardeners. Some of them build closets, others construct my bathroom, whilst others lay floors or grass. And me? I’m constructing an attic in the garage with
a friend. When I was watching our plumbers reconstruct my bathroom, it occurred to me that after twenty years in this business, I finally saw the light. Having opposed the house building metaphor for software development for so long, it’s time to see things through. Time for a little re-construction. The plumbers in my bathroom, both in their early fifties actually quite enjoy their work, as I enjoyed constructing an attic in my garage. They were singing and making (lousy) jokes. Why is it, I wondered, that these middle aged, low educated construction workers whistle and make jokes during this work, that they’ve probably been doing since they left high school. Why?
I’ll tell you. It’s creative work. They try to make the water taps fit in the wall of my bathroom. It’s a puzzle. Drill bits and holes in my wall, fitting the taps again. It’s even fun watching. It’s actually quite like I would address software development. Build it, try it out, test it, and possibly refactor the code. The plumbers actually work quite agile. During the process they ask me where I want the tap, where the shower should be, or they propose suggestions on the tiling plan. Although the bathroom isn’t finished yet, I’m quite sure it will work out fine. Applying patterns And moreover, the plumbers work is bound by the bathroom architecture we’ve laid out, and even more by patterns, just like developing software is bound by software architecture and by design patterns. The plumbers know how high the shower regularly sits on the wall, how many centimeters they will have to drill in the wall to make the tap fit, just like I will use some implementation of a model-viewcontroller to implement the user interface, and domain driven design to construct the domain layer of my application. And you know what, to round up my argument, they make errors too, just like we software developers do. And yes, they make repairs, like we do. It’s a bit like refactoring. They try to create a solution, but if it doesn’t work, they’ll fix it, and try something else. Just like I would refactor my code, if it doesn’t perform. But most of all, there’s one simple thing that binds us software developers to gardeners, plumbers, and carpenters. So much for the construction metaphor. No doubt about it. Just like software construction. It’s creative work. Don’t let anybody ever tell you otherwise. Sander Hoogendoorn blog.sanderhoogendoorn.org •
magazine voor software development 49
DATABASES
Bert Dingemans
Sparse Columns in SQL Server 2008 Objectpersistentie eenvoudig gemaakt Inleiding Bij het toepassen van objectoriëntatie in een ontwikkeltraject waarbij de domeinobjecten als klasse geïmplementeerd worden, speelt het probleem van object-relationele mappings. Vooral het ophalen en wegschrijven van gegevens is lastig, omdat er steeds een vertaalslag gemaakt moet worden van de representatie in de database naar de representatie in de applicatie. Het wordt nog lastiger op het moment dat men overerving gaat toepassen in de klassestructuur. Een voorbeeld van het probleem
Bij het opslaan van de gegevens in de database zullen kolommen gedefinieerd moeten worden zonder dat zij ooit gevuld worden. Bijkomend probleem is dat bij een kleine wijziging in het objectmodel, dit doorgevoerd moet worden in het relationele model in de database. Een soortgelijk probleem doet zich voor bij de implementatie van condities in werkprocessen. Het implementeren van vertakkingen kent een soortgelijk patroon. Sinds SQL Server 2008 is het mogelijk om zgn. ‘sparse columns’ te definiëren. In dit artikel ga ik in op de mogelijkheden van deze functionaliteit voor object-relational mappings. Stapsgewijs geef ik uitbreidingen aan op de werkwijze in de vorm van SQL statements, te beginnen bij inserts en updates en eindigend bij stored procedures die in één enkele stap een lijst van objectdefinities implementeren. Waar nodig wordt de resultaatset getoond bij een opdracht. Sparse columns Sparse columns worden gedefinieerd voor gegevens in de database die niet altijd ingevuld worden (b.v. voor de vertakkingen zoals hierboven beschreven). In onderstaand commando ziet u hoe een tabel met sparse columns gedefinieerd wordt en een tweetal statements: CREATE TABLE [dbo].[Person]( [Id] [int] IDENTITY(1,1) NOT NULL,
wordt gegeven in onderstaande afbeelding.
[status] [varchar](50) NOT NULL, [Name] [varchar](100)SPARSE [Birthdate] [date] SPARSE
) ON [PRIMARY] GO UPDATE Person SET Birthdate = GETDATE()-(45*365) WHERE Id = 1; SELECT * FROM Person;
Fig. 1: Overerving in de klassestructuur Het probleem dat hier ontstaat, is dat men bij het vertalen naar een tabel in een relationele database 6 kolommen nodig heeft terwijl er altijd slechts 4 ingevuld worden. Onderstaande tabel geeft een strokendiagram dat dit weergeeft. Object Voornaam Achternaam
Beroep
1 2
Ontwikkelaar 3500
Jan Piet
50
Jansen Van Dijk
MAGAZINE
Maandsal. Klantpas Krediet
400120 1200
In het Create table statement worden de sparse columns gewoon gedefinieerd als de andere kolommen met een toevoeging van het trefwoord sparse. Daarnaast moet de kolom gedefinieerd worden als NULL wat logisch is omdat de gegevens gebaseerd zijn op een vertakking.
Sparse columns worden opgeslagen als kolom van het type XML Vervolgens kunnen de gegevens in de sparse columns gewoon
DATABASES
gewijzigd en opgevraagd worden met de standaard SQL commando’s. Dat laatste is bijzonder, want fysiek worden de gegevens van de sparse columns helemaal niet in gewone kolommen opgeslagen maar in een kolom van het type XML. Dit maken we zichtbaar met de olgende twee commando’s. CREATE TABLE [dbo].[Person](
lichten we er twee uit die laten zien dat SQL Server 2008 extra functionaliteiten biedt. In de eerste stored procedure wordt als parameter een XML-string gebruikt om de nieuwe waarden van een rij te bewerken. CREATE PROCEDURE [dbo].[Person_bewerken_XML](
Het blijkt dat nu bij een * wildcard niet meer de sparse columns getoond worden maar alleen de fysieke kolommen. Verder is te zien hoe de sparse columns in een XML-kolom worden opgeslagen als een XML-string. Wel is het mogelijk om de sparse columns te muteren en of op te vragen door ze op te nemen in de specificatie, zoals hieronder: UPDATE Person SET allxml = 'Bert Dingemans1962-09-21 Wildforster 37 Ede' WHERE Id = 1 GO
De opzet is eenvoudig en bestaat uit een enkel statement dat de update uitvoert op basis van de primaire sleutel in de tabel; de status is een extra kolom die aangeeft welke methode als laatste is uitgevoerd op het object. Dit gegeven is relevant in onze situatie van objectpersistentie. Voordeel van het werken met een XML-string in plaats van met de afzonderlijke sparse columns is dat we ongestraft in onze applicatie elementen aan ons objectmodel kunnen toevoegen of muteren zonder dat dit gevolgen heeft voor de werking van onze stored procedure. Ook als we de gegevens willen opvragen in de toepassing voor het vullen van ons objectmodel, kunnen we dat opnieuw doen op basis van de XML-kolom. Alleen bij rapportages en het bevragen van ons model voor specifieke objecten maken we gebruik van de sparse columns in de SELECT- of WHERE-component van een statement.
SQL Server 2008 kent parametertables Nadeel van deze opzet is dat je nog steeds per object naar de database moet om een bewerking uit te voeren op de tabellen in de database. Sinds SQL Server 2008 is dat niet meer nodig, het is nu nl. mogelijk om met parameter-tables te werken. Dat is een soort parameter-collection die je als read-only waarde kunt meegeven aan de stored procedure. Hiertoe dient een type gedefinieerd te worden en deze moet voor de aanroep van de stored procedure gevuld worden met de gewenste waarden. In het codevoorbeeld zie je de definitie en vervolgens 1 stored procedure aanroep die 3 rijen wijzigt in de tabel.
SELECT Name, Address, Place, Birthdate FROM Person
CREATE TYPE dla_parametertable
WHERE year(birthdate) < 1965
AS TABLE (id int
Name Bert Dingemans
Address Wildforster 37
Place Ede
Birthdate 1962-09-21
, p_xml xml , p_status varchar(50) ); GO
Het voorbeeld toont dat het mogelijk is om met een update statement de XML-kolom te muteren en vervolgens met de sparse columns de gegevens op te vragen en omgekeerd. Dit biedt hele interessante mogelijkheden voor object persistentie en het werken met stored procedures zoals je zult zien in de volgende paragraaf.
CREATE PROCEDURE [dbo].[Person_aanmaken_valuepara]( @p_xml dla_parametertable readonly ) AS BEGIN
Stored procedures Stored procedures in SQL Server hebben o.a. als voordeel dat zij meerdere opdrachten tegelijkertijd op de database kunnen uitvoeren. Met name in onze situatie is dat wenselijk. In het voorbeeldscript is een aantal voorbeeld stored procedures opgenomen. In dit artikel
DELETE FROM [Person]; INSERT INTO [Person] ([allxml], status )
magazine voor software development 51
DATABASES
Uitbreidingen van de werkwijze zijn natuurlijk denkbaar zoals het daadwerkelijk bijwerken van rijen in plaats van een delete en insert. Voorbeelden hiervan zijn te vinden op mijn website, waar ik werk aan een Object Relational Mapper in Vulcan.Net.
SELECT p_xml, p_status FROM @p_xml END GO declare @table dla_parametertable; insert into @table values (1, 'Bert Dingemans1962-09-21 Rhodosdreef 154 Utrecht', 'Person_aanmaken_para') , (2, 'Anneke Hubert1965-10-15 Rhodosdreef 154
Samenvatting Met de komst van SQL Server 2008 wordt een aantal nieuwe concepten geïntroduceerd die het mogelijk maken om op eenvoudige wijze Object Relational Mappers te introduceren. In het bijzonder sparse columns, XML columns en parameter tables worden in dit artikel uitgewerkt en toegelicht. Bij dit artikel is een voorbeeldscript opgenomen waarin extra voorbeelden zijn opgenomen en de source code als compleet script is uitgewerkt. •
Het voordeel van deze werkwijze zal duidelijk zijn: aan de client-side van de toepassing wordt dit statement opgebouwd en vervolgens is er slechts 1 aanroep naar de database die vervolgens een refresh uitvoert op de objecten die meegegeven worden aan de stored procedure aanroep.
Bert is als software architect werkzaam binnen de maatschap FreeIT, een maatschap van ICT professionals. Bert heeft een voorliefde voor Model Driven Development en het genereren van software. Zo heeft hij CASE tools ontwikkeld in Visual Objects als DLArchitect en DLA Work in Process. Er zijn freeware versies van deze tools beschikbaar op de dla-os website. Bert heeft een weblog op www.dla-os.nl.
Advertentie 4DotNet
52
MAGAZINE
GENERAL
Freek Leemhuis en Maarten Metz
De Kick Ass Development Reeks
Je bent als software ontwikkelaar continu bezig om je allerlei gereedschappen en technieken eigen te maken. De vaardigheid om snel en effectief te leren is misschien wel de belangrijkste die je als ontwikkelaar hebt. Het belangrijkste gereedschap dat je hierbij gebruikt is je brein. Maar maak je daar wel optimaal gebruik van? In de Kick Ass Development reeks zullen we je aan de hand van modellen en praktijkvoorbeelden inzichten en tips geven om je brein te refactoren voor optimaal rendement. In het eerste deel zullen we vooral leerstijlen en leerhulpmiddelen behandelen. We zullen in verdere artikelen aandacht besteden aan kennisgebieden die voor software ontwikkelaars interessant zijn. Bovendien zullen we adviseren hoe je het beste je brein in kunt zetten bij het oplossen van problemen.
Misschien heb je wel eens samengewerkt met iemand die complexe taken fluitend uitvoert. Wanneer je hem of haar aan het werk ziet, lijkt het allemaal makkelijk. In no-time voegen dit soort types nieuwe talen en frameworks toe aan hun toch al indrukwekkende gereedschapskist. Misschien denk je dat dit soort personen meer talent hebben, en dat jij nooit zo goed zult worden. In de meeste gevallen heb je het mis.
“Greatness doesn’t come from DNA but from practice and perseverance honed over decades.” (Geoff Colvin) Een totaal gebrek aan talent kan het moeilijk maken om echt goed te worden op een bepaald gebied. Over het algemeen geldt echter dat niet talent, maar focus, oefening en toewijding bepalende factoren zijn bij het ontwikkelen van deskundigheid. Mozart was op 4 jarige leeftijd al een muzikaal wonderkind, maar ook hij begon pas 13 jaar later muziek van wereldklasse te produceren.
Kick Ass development - part 1: developing a developer De halfwaarde tijd van (vooral technologische) kennis neemt steeds sneller af. Technologische ontwikkelingen en globalisering zorgen ervoor dat je relatief veel moet doen om ‘bij te blijven’. Als beginnend programmeur heb je tijdens je studie misschien een paar honderd regels code geproduceerd. Als starter word je vervolgens aan het werk gezet in een omvangrijk software project, waarbij de complexiteit een veelvoud is van alles waar je tijdens je studie mee bent geconfronteerd. Gaandeweg kom je erachter hoe complex software-ontwikkeling eigenlijk is. Je kennis en expertise schieten vaak tekort om meteen een goede oplossing voor problemen te kiezen. Misschien denk je “Wijsheid komt met de jaren, en als ik wat meer ervaring heb word ik vanzelf beter”. Dit is waar, maar slechts tot op zekere hoogte. Ervaring is nuttig, maar zegt echter niets over de kwaliteit die iemand kan leveren. Iemand kan jaren ervaring hebben met VB zonder zich te bekommeren om de basisprincipes van Object Oriëntatie. Als iemand 5 jaar ervaring heeft met een bepaalde techniek, is hij of zij dan een expert? Of heeft iemand dan 5 keer een jaar dezelfde ervaring op verschillende projecten?
Talent is dus geen voorwaarde om bij de besten te horen. Hard werken is dat wel. Geduld ook; in zijn boek ‘Talent is overrated’ betoogt Geoff Colvin dat om echt deskundig te worden, op welk terrein dan ook, meestal zo’n tien jaar gemoeid is. Niet talent, maar de juiste combinatie van hard en effectief werken kunnen enorme verbeteringen opleveren. Onderzoeken beweren dat de productiefste ontwikkelaars vele malen productiever zijn dan de minst productieven. Voorzichtige inschattingen hebben het dan over een factor 5, terwijl ook een factor van maar liefst 28 is aangetoond (zie o.a. ‘Code complete’ van McConnel en ‘Facts and Fallacies of Software Engineering’ van Glass). Stel je voor: een ontwikkelaar die 28 webapplicaties oplevert in de tijd dat een andere ontwikkelaar er 1 oplevert! Voor je werkgever reden genoeg om deze raspaardjes te koesteren – al zullen ze deze waarschijnlijk niet 28 keer zoveel betalen.
magazine voor software development 53
GENERAL
Leerstijlen en persoonlijkheden De meeste leertheorieën erkennen dat mensen verschillende leerstijlen hanteren. Je kunt bijvoorbeeld onderscheid maken in visuele of auditieve leerstijlen. Sommige mensen vinden het prettig om te leren door te luisteren naar podcasts, terwijl anderen hierbij afhaken omdat ze een visuele component missen. In het boek 'Experiential Learning: Experience As The Source Of Learning And Development' (1984) beschrijft Kolb een cyclus die gevolgd wordt bij leren (zie figuur 1).
In de software-industrie ben je vaak aangewezen op zelfstudie. Het helpt dan om te weten wat je ‘standaard’ stijl is. Ben je bijvoorbeeld een denker, dan ben je misschien geneigd een boek over design patterns van A tot Z door te lezen. Je hebt dan nog niet ervaren wat nu eigenlijk de problemen zijn die deze patterns oplossen, en als je er niet actief mee experimenteert zul je het geleerde niet lang onthouden. In dat geval zou je bijvoorbeeld een hands-on workshop kunnen gebruiken om concrete ervaring op te doen. Of stel dat je een framework wilt doorgronden. Je zou er boeken over kunnen lezen, of de complete API door kunnen werken. Een andere mogelijkheid is om het framework meteen toe te passen in een project en gaandeweg te leren wat de voor- en nadelen zijn (met alle mogelijke refactoring vandien). Weer een andere mogelijkheid is om automatische testen te schrijven en op het framework uit te proberen. Schrijf testen totdat alle gewenste functionaliteit getest is en je begrijpt hoe het werkt. Wat je dan hebt is uitvoerbare documentatie op maat. Veel compacter en betrouwbaarder dan welke handleiding ook en als testset ook uitvoerbaar op nieuwe versies van het framework waardoor je aan de falende testen meteen ziet welke functionaliteit veranderd is. En terwijl je testen schrijft en uitvoert doe je daadwerkelijke ervaring op met het gebruik van het framework zonder de projectcode te vervuilen. Bovendien doorloop je alle fasen van het model van Kolb.
Weet dus wat je eigen leerstijl is Fig. 1: De leercirkel van Kolb Als je iets meemaakt (concrete ervaring) is het belangrijk de ervaringen te overdenken en te reflecteren over wat de ervaring betekent (reflectieve observatie). Vervolgens moet je deze losse overdenkingen in een theorie integreren (abstracte conceptualisatie). Je kunt dan bedenken hoe je deze theorie een volgende keer bij een soortgelijke gebeurtenis toe kunt passen. Het toepassen van deze nieuwe inzichten (actief experimenteren) leidt dan weer tot nieuwe ervaringen. Onbewust leer je al op deze manier en doorloop je automatisch de 4 fasen. Toch is het handig om daar bewust mee bezig te zijn zodat je het proces kunt optimaliseren. Het is namelijk sterk afhankelijk van de persoon op welke fase van de cyclus de nadruk wordt gelegd. Kolb onderscheidt de volgende vier leerstijlen: • Divergerend: de dromer Je bekijkt doorgaans dingen vanuit verschillende perspectieven. Je kiest er eerder voor om te kijken dan om meteen tot actie over te gaan en je gebruikt je verbeelding om tot oplossingen te komen. Je hebt een brede belangstelling, houdt ervan veel informatie te verzamelen en bent sociaal aangelegd. • Assimilerend: de denker Je hebt een voorkeur voor precieze, logische benaderingen. Je houdt er van informatie te organiseren in overkoepelende concepten. Je leert vaak goed door duidelijke uitleg en achtergrond te krijgen, terwijl je aan praktische ervaring minder behoefte hebt. Je hanteert liever een meer academische benadering en bent minder gericht op je sociale omgeving. • Convergerend: de beslisser Je bent goed in het toepassen van opgedane kennis om problemen op te lossen. Je bent praktisch ingesteld en richt je meer op techniek dan op je sociale omgeving. Je bent zeer praktisch ingesteld en houdt van experimenteren. • Accommoderend: de doener In accommoderende stijl ga je meer intuïtief te werk. Je maakt graag gebruik van analyses van anderen om hier in de praktijk mee te experimenteren. Je bloeit op als er actie en initiatief wordt verlangd en werkt graag in teams om doelgericht aan resultaten te werken.
54
MAGAZINE
Weet dus wat je eigen leerstijl is, en zorg dat je ook voldoende aandacht geeft aan de fasen die bij die stijl minder aan bod komen. Metacognitie: monitor je leerproces Metacognitie kan helpen bij het volgen van een effectief leertraject. Hierbij zijn twee processen belangrijk: het monitoren van voortgang van het leren, en het aanpassen van strategieën als de werkwijze niet het gewenste resultaat oplevert. Je leert dus, en tegelijkertijd monitor je de voortgang en stuurt eventueel bij als de gevolgde strategie niet het gewenste resultaat oplevert. Dit kan worden toegepast door doelen expliciet te maken en resultaatgericht te werk te gaan, bijvoorbeeld door leerdoelen SMART te maken (zie einde artikel). Wanneer je specificeert wat de stappen zijn die je wilt maken, hoe je voortgang meet, en wanneer je bepaalde doelen wilt bereiken, wordt het ook makkelijker om zelfregulatie toe te passen. Je kunt tijdens het leertraject beslissen of de leermethode(n) die je hebt gehanteerd effectief zijn of niet. Zet je brein in de turbo stand
Toen de X286 PC uitkwam was deze veel sneller dan de eerdere IBM machines. Dit nieuwe type computer had een turbo knop die de PC sneller maakte. Veel mensen vroegen zich af waarom die knop er zat - waarom zou je die knop niet altijd op ‘aan’ zetten? (bepaalde oudere software was alleen geschikt voor langzame PC’s en werkte daarom alleen als de knop op ‘uit’ stond). Omdat gebruikers niet precies wisten waar de knop voor diende stond deze vaak uit waardoor de machine niet optimaal presteerde. Ons brein heeft verschillende niveaus van activiteit, en verschillende situaties of benaderingen kunnen ervoor zorgen dat ons brein in een vergelijkbare turbostand schiet.
GENERAL
Ter illustratie het volgende waargebeurde verhaal: “Een kennis paste enkele jaren geleden software voor een fabrieksbesturing aan, laadde de code naar de PLC (programmable logic controller ), activeerde het nieuwe programma en merkte tot zijn schrik dat het vrijwel direct muisstil werd in de fabriekshal om hem heen. Alle fabrieksgeluiden van pompen, motoren en centrifuges waren plotseling gedoofd. De stilte duurde niet lang. Al snel kwamen operators de besturingsruimte in rennen en vroegen wat er gebeurd was. Wat bleek: hij had in de code een verwijzing gemaakt naar een label dat niet bestond. Een 'goto' naar een ongedefinieerde plek. De instructie had de PLC volledig laten crashen en alle outputs die aan de PLC hingen vielen terug naar hun veilige stand. Het schoonmaken van alle leidingen en machines, maar ook het productieverlies dat geleden werd door deze vergissing kostte veel tijd en geld. Je kunt je voorstellen dat zo’n gebeurtenis je brein in turbostand zet en dat je zo’n fout niet snel meer zal maken.” Dit is een extreem voorbeeld, maar voor alle leermomenten geldt dat ze meer effect hebben als ze een emotionele component bevatten. Je herinnert je iets beter wanneer het verrassend is, of uitdagend. Je herinnert je niet hoe je over straat liep en er niets gebeurde. Je herinnert je wel dat je over straat liep en er iets bijzonders gebeurde. Je neemt heel veel waar terwijl je over straat loopt, maar slaat niet alles op. De opslagcapaciteit van je brein is beperkt en daarom ben je zeer goed uitgerust om alles wat niet belangrijk is te negeren. Er moet dus iets gebeuren, iets wat tegen de verwachting van je brein ingaat, om te zorgen dat het gebeurde opgeslagen en onthouden wordt. Je wilt bijvoorbeeld een nieuwe programmeertaal onder de knie krijgen, maar hebt er in het dagelijkse werk (nog) niet mee te maken. In de avonduren op de zolderkamertje buig je maar weer eens over dat aangeschafte boek. Vaak is de stof droog, en is het moeilijk om je aandacht er bij te houden. Hoe kun je het brein, dat zo goed is in het negeren van onbelangrijke dingen, overtuigen dat de stof belangrijk is? Ten eerste kun je de omgeving waarin je leert wijzigen: in plaats van op je zolderkamertje weg te kwijnen kun je ook proberen om met vrienden of collega’s samen een studiegroep op te zetten. In de developer community worden vaak veel activiteiten georganiseerd zoals Special Interest Group(SIG) meetings, code camps en coding dojos. Deze activiteiten bieden je de mogelijkheid om samen met anderen te experimenteren met nieuwe technieken. Je moet hiervoor vaak wel meer uit je ‘comfort zone’ treden, en dat kan best moeilijk zijn. Als je die stap maakt zul je echter merken dat je in dergelijke situaties veel meer leert dan in je eentje. De situatie brengt allerlei emoties met zich mee. Je moet je verlegenheid misschien overwinnen, maar vaak is het gezellig, soms uitdagend, maar in alle gevallen komt er veel meer emotie aan te pas dan op je zolderkamertje. En je brein zal – in de turbo stand – dingen beter onthouden.
ren en je brein in de turbo stand zetten. Mocht je toch in je eentje door droge leerstof heen moeten worstelen, dan helpt de PQ4R methode (zie einde artikel) om het studiemateriaal beter te onthouden. Traditionele studie Traditionele studie zoals academische - of certificeringstrajecten leveren vooral declaratieve kennis (‘codified knowledge’) op: weten dat iets zo is, ook wel feitenkennis genoemd. Om deze om te zetten naar procedurele kennis (‘tacit knowledge’) is ervaring nodig. Om in programmeertermen te spreken: de ervaring fungeert als de compiler die declaratieve kennis omzet in procedurele. Zoals ook het model van Kolb aantoont is alleen abstracte conceptualisatie niet afdoende. Dat gezegd hebbende zijn er wel kenmerken aan te wijzen waar effectief leermateriaal aan voldoet. • Goede boeken zijn gecomprimeerde leerervaringen. Ze laten je zien wat belangrijke aandachtsgebieden zijn, en plaatsen kennis in een relevante context. • Goede boeken laten ook zien hoe de kennis in praktijksituaties kan worden toegepast. Het gaat erom dat procedurele kennis uit de praktijk in principes worden gevat. • Goede boeken laten je patronen zien die je zelf door ervaring al kent, maar die je nooit bewust herkende of verwoord had. Iemand kan tientallen boeken lezen over design patterns zonder ze zelf ooit toegepast te hebben. Alle patternkennis van zo'n persoon is dan declaratief. Het is niet ondenkbaar dat zo'n persoon met al deze declaratieve kennis op zak een leraar kan zijn voor iemand die zich wil verbeteren op het gebied van design patterns. Praktijkproblemen gaan vaak echter verder dan een ‘student registration system’ of een ‘ATM’ waardoor zo’n leraar in de praktijk waarschijnlijk snel door de mand valt. Bij traditionele studiemethoden wordt de procedurele kennis van de expert omgezet naar declaratieve kennis (boeken/leerprogramma’s). Vervolgens moet de leerling deze declaratieve kennis door ervaring omzetten naar procedurele kennis. In figuur 2 wordt dat weergegeven door de rode pijlen.
Niet alle leermomenten zul je met anderen delen, dus wat kun je in die situaties doen om je brein toch in de turbo stand te krijgen? Visualiseren is een uitstekende manier om meer delen van je brein te activeren. Het maken van bijvoorbeeld een mindmap is hiervoor een uitstekende manier. Gebruik hiervoor geen mindmap software, maar gewoon papier en pen – de fysieke actie van tekenen gecombineerd met het denken over de abstracties die je probeert weer te geven zorgt ervoor dat je brein in de turbo stand zal schieten. Plaatjes zijn makkelijker te onthouden dan woorden, en studies tonen aan dat kennisoverdracht in visuele vorm tot 89% effectiever is dan alleen tekst. Doe wat je leuk vindt! Wanneer je bijvoorbeeld een muziekfanaat bent, maak dan een programma dat MP3tjes toont en afspeelt. De positieve emotie van het bezig zijn met iets wat je boeit zal je stimule-
Fig. 2: Kennisoverdracht (vrij naar ‘Leidinggeven aan professionals? Niet doen!’ van Mathieu Weggeman) Michael Polanyi zegt over procedurele kennis: “We know more than we can tell” - het is voor experts vaak moeilijk om de procedurele kennis die ze bezitten om te zetten naar een declaratieve vorm. Een illustratief voorbeeld hiervan is de ontwikkeling van een broodbakmachine voor thuisgebruik door Matsushita. Bij de initiële ontwikkeling van dit apparaat kregen ze maar geen goed resultaat. Ze lieten ten einde raad een engineer in dienst treden bij een meester broodbakker om de geheimen van het bereiden te achterhalen. De engineer
magazine voor software development 55
GENERAL
keek de kunst van het kneden af (het deeg werd gedraaid en uitgerekt), en schreef al doende zijn bevindingen op. De broodbakker kon zijn kunde niet goed onder woorden brengen, en er was iemand nodig die de kunst afkeek om de kennis te om te zetten in declaratieve vorm. Ook het proces van het omzetten van declaratieve kennis naar procedurele is moeizaam: er moet eerst ervaring worden opgedaan (en fouten worden gemaakt) voordat de kennis goed kan worden toegepast. Voor dit soort studietrajecten is de directe interactie tussen student en leraar (auteur) vaak miniem, en de student gebruikt één van de belangrijkste talenten van zijn brein niet: het vermogen te leren door imitatie. Monkey see, monkey do! De mens heeft zich evolutionair gedurende vele eeuwen ontwikkeld door te leren door imitatie, en ons brein is als gevolg daarvan heel goed uitgerust om op deze manier te leren. Pas sinds we schrift gebruiken (en eigenlijk vooral sinds de uitvinding van de boekdrukkunst) zijn we op grote schaal overgestapt naar bestudering van abstracte, declaratieve kennis. De methode waarvoor we het best zijn uitgerust wordt echter vaak verwaarloosd bij dit soort leertrajecten; het aantal 1 op 1 leermomenten, waarbij de expert direct in actie kan worden geobserveerd, is meestal beperkt. In situaties waar dat wel gebeurt, kan er directe overdracht van procedurele kennis plaatsvinden. In figuur 2 is dat pad door de groene pijl aangegeven. De figuur maakt duidelijk dat het delen van kennis via de rode pijlen veel omslachtiger, langduriger en arbeidsintensiever is dan ‘onderlangs’ kennis te delen door te socialiseren. Juist voor het delen van snel verouderende kennis pleit Mathieu Weggeman in zijn boek ‘Leidinggeven aan professionals? Niet doen!’ voor socialiseren in meester-gezel relaties. Voor basiskennis, die doorgaans een lage ontwikkelingssnelheid heeft, kan de lange weg zinvol zijn. Technieken als pair programming en het uitvoeren van code reviews kunnen het aantal van dit soort overdrachtsmomenten vergroten. Ook de stroming die software engineering als een ambacht ziet (‘Software Craftmanship’) promoot deze benadering door het vormen van meester-gezel werkverhoudingen. De kenniskloof tussen leraar en leerling moet hierbij niet te groot zijn, omdat de kans bestaat dat de leerling dan het grootste deel van de tijd geen idee heeft waarom de leraar de dingen doet die hij doet. ‘Learn to walk before you can run’. Voor elk soort leerervaring geldt dat ze pas echt tot meesterschap leidt wanneer opgedane kennis ook daadwerkelijk in de praktijk kan worden gebracht. Idealiter kun je als software-ontwikkelaar op het kennisgebied van je interesse werk vinden. Mocht dit niet het geval zijn, overweeg dan om via een hobbyproject en/of open source project ervaring op te doen om je declaratieve kennis te ‘compileren’ tot procedurele kennis. Je zult het snelst leren als je kunt samenwerken met vakgenoten die net iets verder zijn dan jij. Of zoals Robert Fripp, een zeer bekende jazz-gitarist, zijn leeradvies verwoordt: ‘zorg dat je de slechtste muzikant bent in een band’. Zorg dus waar mogelijk dat je samenwerkt met mensen die beter zijn dan jezelf. Van beginner naar expert De eerste stap in het opdoen van expertise is het bewust worden van je eigen vaardigheid, of gebrek hieraan. Abraham Maslow schrijft een 4-staps leerproces zoals weergegeven in het kader. Deze bewustwording kan plaatsvinden als gevolg van zelfreflectie, maar bijvoorbeeld ook door een onafhankelijke meting (toets) of door terugkoppeling van vakgenoten. Wees er wel op bedacht dat mensen de neiging hebben om hun eigen competentieniveau te overschatten. Pas wanneer je hogere niveaus bereikt, begin je te beseffen hoeveel je eigenlijk niet weet. Als beginner op een kennisgebied moet je dan ook niet proberen meteen de hele context te begrijpen.
56
MAGAZINE
Beginners need unambiguous, context-free rules (Andy Hunt) Wanneer je bijvoorbeeld met je auto een nieuwe route moet afleggen, en je bent niet bekend met de omgeving, dan zul je er voor kiezen blind op je navigatiesysteem of wegenkaart te vertrouwen. Echter, als je de omgeving goed kent, en je kent de verkeersdrukte op bepaalde knooppunten dan zul je hier en daar van de voorgestelde route afwijken. Om het optimale resultaat te bereiken (niet de kortste, maar de snelste route) neem je de context mee. Als je al verschillende programmeertalen beheerst dan is een overzicht van de syntax en belangrijkste features van een nieuwe taal voldoende om de taal onder de knie te krijgen. De ervaring met andere programmeertalen geeft je voldoende context om snel inzicht te krijgen in de nieuwe taal. Bij het leren van je eerste programmeertaal zul je eerst meer low-level moeten beginnen om uiteindelijk - door experimentatie en ervaring - te komen tot een high-level begrip. Tenslotte Voor ontwikkelaars is het noodzakelijk te blijven leren. Technieken komen en gaan, en het C# van vandaag is het COBOL van morgen. Het loont daarom om te leren hoe je moet leren. Metacognitie en de Kolb leerstijlen bieden handvaten voor het optimaliseren van het leerproces. Leren door imitatie gaat de mens zeer goed af, zorg daarom dat je zoveel mogelijk samenwerkt met mensen die meer expertise hebben dan jezelf. Houdt rekening met het tien jaar principe: je moet een vak vaak vele jaren beoefenen voordat je echt een expert wordt. Talent is daarbij van ondergeschikt belang – oefening, toewijding en plezier zijn veel belangrijker.
An investment in knowledge always pays the best interest. (Benjamin Franklin)
Het SMART principe Volgens het SMART principe kun je doelstellingen expliciet en controleerbaar maken: • Specifiek - Het specificeren van een doel (wie, wat, waar, wanneer, waarom) leidt tot een preciezere doelstelling waardoor het makkelijker wordt er invulling aan te geven; • Meetbaar - Specificeer hoe je vorderingen meet, en wanneer een doel bereikt is; • Acceptabel - Je kunt doelen hebben die in het begin onbereikbaar lijken. Vorm daarom doelen die wel binnen bereik liggen, waarna je met kleine stappen richting het uiteindelijke doel beweegt; • Realistisch - Is het doel haalbaar? Kost het niet te veel inspanning, of gaat het ten koste van andere dingen? • Tijd gebonden – Specificeer wanneer je met de activiteiten begint, en wanneer je denkt klaar te zijn.
GENERAL
De PQ4R methode Heb je het gevoel dat dit allemaal te veel tijd kost? Misschien dat deze quote je motiveert: 'If you don't have time to do it right, when will you have time to do it over? Preview - oriënteer je vooraf.
Door bijvoorbeeld eerst de inhoudsopgave door te nemen creëer je een context waarbinnen de specifieke deelgebieden kunnen worden geplaatst.
Questions - stel vragen
Stel zo veel mogelijk vragen over het onderwerp voordat je gaat lezen.
Read - lees
Lees zorgvuldig en probeer antwoorden te vinden op de vragen die je gesteld hebt. De antwoorden leiden vaak weer tot nieuwe vragen waardoor een dieper niveau van tekstverwerking ontstaat.
Probeer voorbeelden te bedenken en verbanden Reflect - denk er over te leggen met kennis die je al hebt of die je ook na nog wilt opdoen.
Recite - vertel het in je eigen woorden
Nadat je een flink stuk gelezen hebt, probeer je in eigen bewoording voor te dragen wat je geleerd hebt. Als dit niet goed lukt, keer je weer terug naar de tekst en probeer je de gaten in je geheugen op te vullen.
Review - recapituleer
Nadat je het hoofdstuk gelezen hebt probeer je de belangrijkste punten terug te halen. Zorg ervoor dat de vragen die je gesteld hebt allemaal naar tevredenheid beantwoord zijn.
Maslow's 4 stadia van leren Onbewust bekwaam gedrag komt tot uiting in termen als ‘intuïtie’, ‘Fingerspitzengefühl’ en ‘gut feeling’. Deze termen verwijzen allemaal naar het - vaak onbewust - toepassen van patroonherkenning waardoor een expert snel problemen kan doorgronden en een kwalitatief goede beslissing kan nemen. Stadium
Kenmerken
1 Onbewust onbekwaam
Je realiseert je niet dat je bekwaamheid tekort schiet of dat je gedrag niet effectief is
2 Bewust onbekwaam
Je wordt je bewust van tekortkomingen en gaat actief leren hoe het beter kan
3 Bewust bekwaam
Je past het nieuw aangeleerde bewust en met succes toe
4 Onbewust bekwaam
Het effectieve gedrag gaat automatisch, is vanzelfsprekend
Freek Leemhuis Freek Leemhuis werkt als sinds 1996 met het Microsoft development platform. Sinds 2006 werkt hij als consultant voor Logica in rollen als Developer en Software Architect. Hij heeft een bijzondere belangstelling voor onderwerpen als OO, Domain Driven Design, Agile technieken en kwaliteit van software. In zijn vrije tijd zoekt hij via Devnology (http://Devnology.nl) gelijkgestemden om kennis en ervaring uit te wisselen. Volg zijn blog op http://freekleemhuis.com.
Maarten Metz Maarten Metz werk als software ontwikkelaar voor de Java competence van Logica. Hij heeft 10 jaar werkervaring in bit-, byte-, procedureel- en objectgeoriënteerde omgevingen. Zijn interesses gaan uit naar software design, - construction, quality, - testing en de psychologie van software engineering. Hij leest legio boeken en artikelen over voornoemde onderwerpen en hoopt ooit nog eens het software equivalent van Pink Floyd's 'Dark side of the moon', Monet's 'Kathedraal van Rouen' en Tolstoj's 'Anna Karenina' te creëren.
UX
TIP:
Silverlight Blog summary page Check out http://www.netvibes.com/rboarman#Silverlight
ASP.NET onder de Motorkap: Ben jij al AJAX Supporter? Ik woon in de buurt van Amsterdam en dat betekent wel haast automatisch dat Ajax mijn club is. Helaas heb ik de laatste jaren weinig reden om daar trots op te zijn. Toch ben ik een echte AJAX supporter, hoewel ik eerlijk moet zijn dat ik het dan niet meer over voetbal heb, maar over Asynchronous JavaScript And XML. Als je als ASP.NET ontwikkelaar nog niet naar AJAX hebt gekeken, dan wordt het hoog tijd dat je dat gaat doen. AJAX maakt de gebruikerservaring rijker en maakt het programmeren (vaak) makkelijker. Vooral als je gebruik maakt van alles wat er al op het web te vinden is om met AJAX te werken en dan met name van de ASP.NET AJAX Control Toolkit, waarvan in mei versie 3 uitgekomen is. De Control Toolkit is een open source project dat vanuit Microsoft gestuurd wordt, maar waar in principe iedereen aan kan bijdragen of zelf aanpassingen kan doen. Je kunt de Control Toolkit in actie zien op http://www.asp.net/ajax/AjaxControlToolkit/Samples/ en daar kun je ook downloads, tutorials en video’s vinden. Omdat de Control Toolkit open source is, kun je inspiratie opdoen voor je eigen controls en zien hoe je volgens best practices AJAX kunt gebruiken. En die best practices zijn nodig, want je kunt AJAX op allerlei manieren misbruiken. Hoe het niet moet… ASP.NET AJAX bestaat eigenlijk uit een aantal onderdelen. Het belangrijkste onderdeel is een JavaScript bibliotheek die het veel eenvoudiger maakt om bepaalde functionaliteit te verzorgen in de browser en waarmee je web services kunt aanroepen op de server. Daarnaast is er een server deel voor het aanbieden van web services als onderdeel van een pagina en de UpdatePanel control. Die laatste lijkt heel handig. Je kunt namelijk op een pagina regionen aanmaken die zich los van de rest van de pagina kunnen verversen. Voor eenvoudige scenario’s waarbij bandbreedte en responstijd allemaal niet zo belangrijk zijn, is dit nog best handig, want je kunt er snel resultaat mee bereiken. Ook is het erg handig voor prototypes en demo’s, want man, wat loopt dat lekker op een lokale machine! De koude douche komt als je complexe pagina’s met de UpdatePanel control hebt gemaakt en deze “in het wild” gaat gebruiken. Dan blijkt het toch allemaal niet zo lekker te lopen, want de UpdatePanel control is eigenlijk een heel dom ding. Wanneer een UpdatePanel ververst moet worden, wordt een volledige PostBack gedaan. Op de server wordt dan ook de volledige pagina uitgevoerd alsof er sprake is van een PostBack. Daarna kijkt ASP.NET AJAX welke stukken van de pagina daadwerkelijk veranderd zijn en stuurt dat terug naar de browser, die daar de UpdatePanel control(s) bijwerkt. Heel vernuftig, maar een totaal verkeerde benadering, want dit betekent enorm veel overhead in het netwerkverkeer en onnodige belasting van de server. Hoe het wel moet Het grote voordeel van AJAX is dat je web services aan kunt roepen die op de server draaien en dat je het resultaat daarvan in de browser kunt verwerken. Wil je bijvoorbeeld een DropDownList vullen met gegevens aan de hand van de keuze uit een andere DropDownList,
58
MAGAZINE
dan kun je de betreffende gegevens ophalen met een webservice en dan met wat JavaScript de DropDownList vullen. Dit werkt sneller en efficiënter dan een UpdatePanel waarin je de DropDownList zet die je wilt aanpassen. Ten eerste doe je geen volledige postback, maar gaan alleen die gegevens over de lijn die daadwerkelijk nodig zijn. Ten tweede is de code om de DropDownList te vullen waarschijnlijk efficiënter. En ten derde kun je voor de verwerking van de data gebruik maken van de kracht van de client computer. Het is natuurlijk wel wat meer werk om het zelf te doen, maar de voordelen maken het de moeite waard. Overigens hoef je voor dit voorbeeld niets zelf te doen, want de CascadingDropDown uit de Control Toolkit is specifiek gemaakt voor dit scenario.
AJAX zelf implementeren kost iets meer werk, maar de voordelen maken het de moeite waard Quick wins Zonder dat je al erg diep in de technologie hoeft te duiken biedt de Control Toolkit een aantal controls die je heel snel kunt gebruiken en die al enorme voordelen bieden. De eerder genoemde CascadingDropDown is daar een voorbeeld van, maar nog makkelijker en sneller zijn de verschillende “control extenders” waarmee je bestaande controls uit kunt breiden om bepaalde dingen te doen. Met de PasswordStrength control extender bijvoorbeeld kun je een TextBox uitbreiden om de sterkte van een wachtwoord te controleren. Dit kan visueel op verschillende manieren. De FilteredTextBox en MaskedEdit kun je aan een TextBox koppelen zodat gebruikers alleen bepaalde karakters of een bepaald patroon (bijvoorbeeld een telefoonnummer) in kunnen voeren. In tegenstelling tot de ASP.NET validator controls, gaat dit proactief aangezien de gebruiker geen ongeldige invoer kan geven. Met een validator control kan dat wel, maar blokkeert de validator de PostBack. Vanaf versie 3.0 bevat de Control Toolkit ook een HTMLEditor control die je zou kunnen gebruiken als standaard in plaats van een van de commerciële controls die hiervoor beschikbaar zijn in de markt. Kortom, je doet er goed aan om snel eens te kijken naar de Control Toolkit en er je voordeel mee te doen. Op naar snellere en intuïtieve webinterfaces! Michiel van Otegem [email protected] •
ARCHITECTURE
Marion Verwey en Nienke van de Brink
Requirements Management voor Software Architecten Inleiding Veel projecten gaan, ondanks een enthousiaste start, nooit in productie. Herkenbaar? Bij dergelijke projecten zijn vaak de requirements niet goed van kwaliteit, niet consistent met elkaar of ontbreken volledig. Tijdens het ontwikkelen van interne standaarden hebben wij uit onze ervaringen best practices gedestilleerd. In dit artikel delen we een aantal van deze best practices. We kijken naar de voordelen voor een project en geven handvatten om deze best practices toe te passen. Hang je Project Vision ingelijst aan de muur In veel projecten blijkt het Vision-document achteraf bezien een verspilling van tijd; niet omdat je het document niet nodig had, maar omdat je het vaak vergat te gebruiken. In het begin van het project heb je veel aandacht aan het opstellen en afstemmen van het Visiondocument met de klant besteed, maar daarna heb je de technische uitwerkingen en beslissingen in het project niet meer genomen op basis van de oorspronkelijk beschreven needs, maar op basis van de inschatting van een projectmedewerker.
Wij vinden het belangrijk dat ieder teamlid het Vision-document kent. Het Vision-document beschrijft immers waarom het project gestart is (problem statement, needs) en welke functionele oplossing hiervoor gekozen is (features, functionele context). Het Vision-document bevat echter nog niet de gedetailleerde uitwerking van de oplossing in systeemrequirements (Use Cases en Supplementary Requirements). Wanneer alle teamleden het waarom en wat van een project duidelijk voor ogen hebben, voorkomt dit wantrouwen tussen business en IT – wantrouwen omdat de IT-er vindt dat de business niet duidelijk aangeeft wat ze wil en de business vindt dat de IT-er altijd denkt in onmogelijkheden in plaats van mogelijkheden. Door de IT-er deelgenoot te maken van het probleem dat opgelost moet worden, kan hij ook proactief meedenken en aangeven wat een alternatieve oplossing kan zijn voor een slecht realiseerbare systeemrequirement. Daarnaast helpt het Vision-document bij het snel bepalen van de oplossing bij wijzigingen doordat het Vision-document de needs en features bevat en de bijbehorende stakeholders (zie ook de best practice over traceability) . Bovendien is het eenvoudiger om de scope van het project te bewaken wanneer alle teamleden het Vision-document kennen. Iedereen weet wat de oorspronkelijk afgesproken scope is, en waarom deze zo is bepaald. Onbewuste afwijkingen van de scope vanwege onbekendheid met de doelstellingen van het project komen zo minder voor.
Fig. 2: Het-Vision document bevat zowel het waarom als het wat van een product en geeft daarmee een goed functioneel overzicht van het project.
Fig. 1: Zorg dat alle projectleden (de essentie van) het Visiondocument kennen. Hoewel het misschien vreemd lijkt om een samenvatting van het Vision-document werkelijk aan de muur te hangen, kan dit in complexe projecten soms werkelijk helpen.
Betrek de juiste stakeholders Het is belangrijk om je requirements samen met de juiste stakeholders te verzamelen en af te stemmen. Maar hoe bepaal je wie de juiste stakeholders zijn? Je wilt niet met te veel zogenaamde stakeholders om tafel zitten, want dan eindig je met Poolse landdagen waarbij geen besluit genomen wordt. Je wilt echter ook niet aan het eind van je project ontdekken dat b.v. de beheerafdeling je product niet accepteert
magazine voor software development 59
ARCHITECTURE
omdat het niet aan hun beheerrequirements voldoet. De juiste stakeholder is iemand die: • Direct geraakt wordt door de uitkomsten van het project, en daarbij ook in staat is de consequenties van de besluiten voor het eindproduct te overzien; • Besluitvaardig is en hierbij ook mandaat heeft om besluiten over de functionaliteit van het product te nemen; • Communicatief is (dus ook afstemt met zijn achterban) en mee wil werken aan de ontwikkeling van dit product. Wanneer je onvoldoende, of verkeerde, stakeholders bij je project betrekt zal dit er altijd toe leiden dat het product uiteindelijk niet geaccepteerd wordt. Betrek dus de juiste stakeholders, en leg de stakeholders en hun verantwoordelijkheden ook vast in het Visiondocument. SMARTificeer de requirements Met het smartificeren van requirements bedoelen we het SMART opschrijven van de requirements. Hiermee voorkom je dat bijvoorbeeld de tester een requirement anders interpreteert dan de bouwer, die op zijn beurt weer een andere interpretatie heeft dan de functionaliteit die de requirements-specifier voor ogen had.
SMART staat voor Specifiek, Meetbaar, Accepteerbaar, Realiseerbaar/Realistisch, Tijdgebonden/Testbaar/Traceerbaar SMART staat voor Specifiek, Meetbaar, Accepteerbaar, Realiseerbaar/Realistisch, Tijdgebonden/Testbaar/Traceerbaar. Een voorbeeld van een niet functionele requirement die niet SMART is, luidt: “De performance van het systeem moet goed zijn.” Want wat is goed? De ene gebruiker vindt het al goed, als gegevens binnen 5 seconden op het scherm staan, terwijl de andere gebruiker het heel vervelend vindt als hij langer dan één seconde op een antwoord moet wachten. Een SMARTere specificatie van deze requirement is: “Het tonen van gegevens op het scherm mag in 80% van de gevallen niet langer dan 1,5 sec. duren.” Deze requirement is nu specifiek, omdat het spreekt over het tonen van de gegevens op het scherm; meetbaar en testbaar omdat het in 80% van de gevallen niet langer dan 1,5 seconde mag duren; accepteerbaar omdat duidelijk is als meer dan 20% er langer dan 1,5 seconde overdoet de requirement niet meer voldoet, en realiseerbaar omdat binnen de voor deze opdracht gebruikte technologieën dit een gangbare performance-eis is. Bereik overeenstemming over de requirements Stakeholders vertegenwoordigen allemaal een andere doelgroep dus hebben ze allemaal een ander beeld van het op te lossen probleem en daarom ook verschillende wensen voor de oplossing. Zorg dus dat je alle requirements met alle stakeholders afstemt en eventuele conflicterende requirements aanpast, met ieders instemming. Een goed goedkeuringsproces is hiervoor noodzakelijk. Ons advies hiervoor is simpel: gebruik workshops. Verzamel je requirements in een workshop, zodat alle stakeholders elkaars requirements kennen en erkennen, je de verwachtingen van al je stakeholders kunt managen en je conflicterende requirements direct kunt benoemen en aanpassen – de eindgebruiker heeft hierbij in principe het laatste woord. Alle stakeholders zijn met deze werkwijze volledig in de besluitvorming meegenomen, waardoor toekomstige wijzigingen ook eenvoudiger geaccepteerd worden.
60
MAGAZINE
Naast acceptatie van je stakeholders moeten ook alle projectteamleden de requirements begrijpen. Testers en designers zijn bovendien vaak kritische reviewers die vanuit hun rol goed de maakbaarheid en SMART-heid van requirements kunnen bevragen. Door het projectteam op tijd bij de review van de requirements te betrekken voorkom je rework in een latere fase, zorg je voor een eenvoudigere overdracht binnen het team en bereik je de optimale oplossing voor de klant.
Testers en designers zijn bovendien vaak kritische reviewers die vanuit hun rol goed de maakbaarheid en SMART-heid van requirements kunnen bevragen Testers en designers kunnen opmerkingen hebben die te maken hebben met de maakbaarheid en SMART-heid van requirements. Denk hierbij weer aan het voorbeeld van de performance van een systeem: “Het tonen van gegevens op het scherm mag in 80% van de gevallen niet langer dan 1,5 sec. duren.” Zoals eerder beschreven kan dit een goede requirement zijn. Echter, in een complexe technische omgeving waarin het systeem verschillende services moet aanroepen, kan dit lastig realiseerbaar zijn. De designer zal je hier dan ook op wijzen. De tester zal je bovendien vragen of er maxima gelden voor de overige 20%, en of het systeem een foutmelding moet tonen als het over deze maximumgrens heengaat. Na overleg met tester en designer kan deze requirement aangepast worden naar: “Het tonen van gegevens op het scherm mag in 80% van de gevallen niet langer dan 2 sec. duren. Wanneer het tonen van de gegevens langer dan 2 sec. duurt, moet de gebruiker een (beheerbare) foutmelding getoond worden.” Daag je klant uit om niet alles als must have te classificeren Als je met de klant praat dan merk je al gauw dat hij eigenlijk alles wil. Ook de briljante ideeën die jij ze aan de hand doet willen ze graag hebben. Echter, als je alle requirements zonder prioritering of classificering gaat uitvoeren, dan merk je in het algemeen dat het allemaal wel erg veel, lastig en duur wordt. Daarom is het ook in het belang van de klant, dat hij een onderscheid maakt tussen de ‘must haves’ en de ander ‘haves’ in het MOSCOW-model (Must Have, Should Have, Could Have, Want to Have). Een manier om de klant uit te dagen om dit onderscheid te maken is door hem de vraag te stellen: “Wat gebeurt er als we dit niet in het systeem gaan ondersteunen?”
“Wat gebeurt er als we dit niet in het systeem gaan ondersteunen?” Ook bij het prioriteren is het belangrijk om de software-architect of designer erbij te betrekken. Door de klant feedback te geven over de maakbaarheid van een requirement maak je de klant duidelijk dat sommige requirements erg duur zijn. Deze kosten-baten afweging maakt het voor de klant eenvoudiger om een requirement te ‘downgraden’. Het omgekeerde kan natuurlijk ook het geval zijn. Soms is een requirement voor een klant minder belangrijk, maar geeft de software architect aan dat het geen extra moeite kost om deze tegelijk met een ‘must have’ requirement te realiseren. Door deze requirement dan direct te koppelen aan deze ‘must have’, kun je een voor het project zo efficiënt mogelijke planning maken.
ARCHITECTURE
Een voorbeeld van een requirement die door middel van het stellen van de vraag “Wat gebeurt er als we dit niet in het systeem gaan ondersteunen?” van een ‘must have’ naar een ‘should have’ zou kunnen verhuizen is de volgende: “Het systeem moet context-gevoelige helpfunctionaliteit kunnen aanbieden.” Als je vervolgens de klant vraagt wat er gebeurt als er wel helpfunctionaliteit beschikbaar is, maar niet context gevoelig, dan antwoordt de klant vaak dat het op zich niet zoveel uitmaakt, maar dat de gebruiker alleen iets langer moet zoeken om de goede help-ondersteuning te vinden. In dit concrete voorbeeld verandert bij veel projecten deze ‘must have’ vaak naar een ‘could have’ of ‘should have’. Daarnaast moet je wel een nieuwe requirement toevoegen: “Het systeem moet helpfunctionaliteit aanbieden.”
Een voorbeeld van een wijziging die goed doorgevoerd moet worden is: “Gedurende de test wordt een fout gevonden, die te herleiden blijkt te zijn tot een fout in de requirements.” Als gevolg hiervan moeten de requirements dus gewijzigd worden. Als je niet openstaat voor deze wijziging, betekent dit dat er uiteindelijk een foutief systeem wordt opgeleverd.
Manage de verwachtingen, zowel van de klant als van jezelf Net als bij het Vision-document is verwachtingsmanagement een activiteit waar je vaak in de eerste fase van het project op focust om het daarna te vergeten. Wat je je daarbij vaak niet realiseert, is dat een project een levende organisatie is, waarin bijna continu kleine wijzigingen aan de scope worden aangebracht. Wanneer je deze kleine wijzigingen niet duidelijk communiceert, zal het eindresultaat vaak een teleurstelling zijn voor de klant. Minstens net zo belangrijk is het om ook je eigen verwachtingen te managen. Jij verwacht namelijk ook iets van de klant, bijvoorbeeld medewerkers die een bijdrage aan het project leveren, maar ook interface-specificaties of reviewactiviteiten. Wanneer je deze verwachtingen niet van te voren uitspreekt, zal de klant niet altijd aan jouw verwachtingen voldoen, waardoor het project vertraging kan oplopen.
Als voorbeeld van de bijdrage die de software architect kan leveren aan het opstellen van niet-functionele requirements, kun je weer denken aan het voorbeeld van de performance requirement: “Het tonen van gegevens op het scherm mag in 80% van de gevallen niet langer dan 1,5 sec. duren.” Het is wel leuk als je opschrijft dat de gegevens altijd binnen 1,5 seconde op het scherm getoond worden, maar dit moet natuurlijk wel haalbaar zijn. De software architect kan in dit geval heel goed aangeven wat binnen de gebruikte architectuur haalbare performance-eisen zijn. Hij kan de grenzen zetten en vervolgens kun je in overleg met de klant de uiteindelijke performance-eisen bepalen waardoor de uiteindelijke requirement er als volgt uit kan zien: “ Het tonen van gegevens op het scherm mag in 80% van de gevallen niet langer dan 1,5 sec. duren, wanneer geen externe services gebruikt worden en niet langer dan 2,5 sec. duren wanneer externe services gebruikt worden.”
Voor het managen van klantverwachtingen kun je bijvoorbeeld denken aan ons eerdere voorbeeld over de contextgevoelige helpfunctionaliteit. Op het moment dat je besluit om dit terug te brengen tot een ‘gewone’ helpfunctionaliteit moeten alle stakeholders op de hoogte worden gesteld. Als je dit nalaat, is het voor de gebruikers wel een fikse teleurstelling op het moment dat het systeem in productie gaat.
Veranderingen gedurende het project zijn onvermijdbaar Sta open voor wijzigingen Wij komen regelmatig klanten tegen die huiverig zijn voor wijzigingen omdat ze bang zijn dat deze je project lastig bestuurbaar maken. Natuurlijk is de hoeveelheid wijzigingen een maat voor de stabiliteit van je project, maar dit betekent niet dat je alle wijzigingen moet weren. Veranderingen gedurende het project zijn onvermijdbaar. Door deze wijzigingen te verwachten en een goed proces in te richten om deze te behandelen kun je je project ook met wijzigingen bestuurbaar houden. Stem het change-proces ook af met al je stakeholders, zodat iedereen weet hoe een wijziging behandeld wordt en wie daarover mag besluiten. Een aantal aanbevelingen voor een goed change-proces zijn: • Zorg dat je expliciet vastlegt wat je met een wijziging doet (uitvoeren, afwijzen of uitstellen); • Geef aan wanneer uitgestelde wijzigingen wel opgepakt worden; • Zorg dat de in de wijziging beschreven oplossing in lijn is met het Vision-document; wijzigingen mogen alleen goedgekeurd worden als deze bijdragen aan de needs die beschreven zijn in het Visiondocument; • Als de wijziging een functionele wijziging betreft, volg dan hetzelfde afstemmingsproces als voor alle andere requirements, dus betrek alle relevante stakeholders; • Zorg dat voor een wijziging niet alleen de code, maar ook de documentatie wordt bijgewerkt.
Gebruik de (kennis van de) software architect Bij het opstellen van niet-functionele requirements is vaak ook gedegen technische kennis nodig. Een requirement specifier heeft deze technische kennis vaak niet, maar de software architect natuurlijk wel. Hij kan je dan ook helpen bij het opstellen van goede en haalbare nietfunctionele requirements.
Traceer requirements … maar niet oneindig Het is belangrijk om requirements van hoog naar laag niveau traceerbaar te maken. Zo kun je detecteren of alle hoog niveau requirements daadwerkelijk verder uitgewerkt zijn. Met andere woorden: dekken de requirements de needs en features? Je wilt immers voorkomen dat de klant om iets heeft gevraagd, maar dat je die high level requirements niet hebt vertaald naar systeemrequirements (scope gap) of dat je een leuke systeemrequirement hebt bedacht die geen aansluiting heeft op de bovenliggende requirements; hiervan heeft de klant dan eerder aangegeven daar geen behoefte aan te hebben (scope creep). Daarnaast kun je eenvoudig een impactanalyse voor een wijziging uitvoeren. Denk bijvoorbeeld aan een wijziging die later in het project om technische redenen moet worden doorgevoerd. De bijbehorende documentatie moet ook op basis hiervan worden aangepast. Wanneer alle requirements van hoog naar laag getraceerd worden, kun je snel inzichtelijk maken welke documentatie aangepast moet worden. Wanneer je deze traces niet hebt aangebracht, moet je veel uitzoeken en is het risico groot dat je dit, omwille van de tijd, of gewoon omdat het saai werk is, niet meer doet. De documentatie die je dan later aan de beheerafdeling oplevert is niet meer in sync met het werkelijk ontwikkelde product, waardoor het werk voor de beheerafdeling erg lastig wordt. Hoe meer verschillende soorten requirements er zijn, des te meer mogelijkheden er zijn om de requirements te tracen, maar bedenk bij iedere vorm van tracing goed waarom je de tracing aanlegt en wat de toegevoegde waarde van de tracing is.
Het is daarom heel belangrijk om bij het begin van het traject te bepalen wat je gaat tracen en met behulp van welke tooling je dit ondersteunt magazine voor software development 61
ARCHITECTURE
Marion Verwey
Het nadeel van te veel tracen is dat het veel en saai werk is, waardoor ook in de praktijk blijkt dat je deze traces niet goed onderhoudt. Het nadeel van te weinig tracen is dat je onvoldoende profiteert van de toegevoegde waarde van tracing waardoor het werk de moeite niet waard is.
Marion Verwey is een zeer ervaren system analist bij Capgemini. Naast haar werkzaamheden als system analist bij diverse klanten is zij ook vakgroepleider van de requirements vakgroep binnen de sector public. Zij is bereikbaar via marion. [email protected].
Het is daarom heel belangrijk om bij het begin van het traject te bepalen wat je gaat tracen en met behulp van welke tooling je dit ondersteunt. Als je wel eens hebt geprobeerd meer dan 1000 requirements in Excel vast te leggen, dan weet je dat je dan het overzicht over alle requirements snel kwijt bent. Maar als je een uitgebreide requirements management tool gebruikt voor een klein project met honderd requirements levert juist dat veel overhead op en voldoet Excel prima. We adviseren daarnaast ook om vanaf het begin van het project traces vast te leggen; achteraf aanbrengen van tracing is veel en vooral ook saai werk. In figuur 3 geven we een traceability model dat wij aanbevelen, met traces tussen alle requirements typen en naar de belangrijkste van requirements afgeleide deliverables.
Nienke van den Brink Nienke van den Brink een zeer ervaren system analist bij Capgemini. Naast haar werkzaamheden als system analist bij diverse klanten is zij ook vakgroepleider van de requirements vakgroep binnen de sector Financial services. Ze is bereikbaar via Nienke.vanden.Brink@ capgemini.nl.
UX Fig 3: Aanbevolen traceability model: dit model tracet alle requirementstypen naar elkaar zonder hierbij in veel detail te gaan en legt een trace naar de belangrijkste van de requirements afgeleide technische en test deliverables Conclusie Het opstellen van requirements is een lastig vakgebied en in de praktijk blijkt vaak dat je toch belangrijke zaken uit het oog verliest. In dit artikel hebben we vanuit onze praktijkervaringen een aantal best practices beschreven waarvan wij hebben gemerkt dat iedereen deze in de praktijk – bewust of onbewust – wel eens vergeet. Kort samengevat komen de best practices er op neer dat je vanaf het begin van je project serieus met het opstellen van requirements om moet gaan en gedurende het project deze als leidraad bij alle besluiten moet gebruiken; daarnaast is het erg belangrijk om de juiste personen bij je requirements te betrekken, zowel de stakeholders voor afstemming van wat ze willen, als software architecten, designers en testers voor de maakbaarheid en testbaarheid van je requirements. We hopen duidelijk te hebben gemaakt waarom het belangrijk is om deze best practices altijd toe te passen, en we hebben hierbij een aantal handvatten voor het toepassen gegeven. Ben je geïnteresseerd in de achtergronden van deze best practices, of de uitbreidingen die de auteurs hiermee op RUP hebben gedaan, neem dan contact op met een van de auteurs. •
62
MAGAZINE
TIP:
Silverlight Tutorial Blogs Er zijn een groot aantal Silverlight tutorials beschikbaar. Onderstaande lijst is van actieve blogs die regelmatig bijgehouden worden. Er zijn goede tutorials te vinden plus de source code zodat je gelijk zelf aan de slag kunt. • • • • • • • • • • • • • • •
Andy Beauliue: http://www.andybeaulieu.com/ Home/tabid/67/Default.aspx Brad Adams: http://blogs.msdn.com/brada/default.aspx Chris Hay: http://www.screencast.com/users/chrishayuk DotNet Curry: http://www.dotnetcurry.com/ Jesse Liberty: http://silverlight.net/blogs/jesseliberty/ Joe Stegman: http://blogs.msdn.com/jstegman/default.aspx Nikola Mihaylov: http://blogs.msdn.com/nikola/default.aspx Page Brooks: http://pagebrooks.com/ Pete Brown: http://community.irritatedvowel.com/blogs/ pete_browns_blog/default.aspx Shawn Wildermuth: http://wildermuth.com/ Silverlight Learning Resources: http://silverlight.net/learn/ SilverlightShow: http://www.silverlightshow.net/ Terence Tsang: http://www.shinedraw.com/ Tim Heuer: http://timheuer.com/blog/ Timmy Kokke: http://geekswithblogs.net/tkokke/ Default.aspx
Advertentie Sogeti
.NET
Laurens Ruijtenberg
F#: Een Functionele Oplossing binnen een Imperatieve Wereld Naast C# en Visual Basic gaat Microsoft ook F# meeleveren in Visual Studio 2010. F# is een functionele taal, zoals je kon lezen in SDN Magazine #99. Dit betekent dat waarden en functies gelijk aan elkaar zijn. Het is bijvoorbeeld mogelijk om functies als variabele mee te geven, opnieuw te definiëren of er bewerkingen op uit te voeren. Daarnaast zijn variabelen en functies binnen F# standaard ‘immutable’. Zodra een waarde of functie is toegewezen mag deze niet meer veranderen, wat weer voordelen biedt bij multithreaded programmeren. Dit heet ook wel stateless programmeren en is in puur functionele talen de manier om met variabelen om te gaan. F# is geen puur functionele taal maar een multi-paradigma taal. Daardoor ondersteunt het zowel functionele als imperatieve code. Talen als Visual Basic en C# zijn voorbeelden van imperatieve talen. Binnen imperatieve talen vertel je stap-voor-stap wat de applicatie moet doen. Dit kan bijvoorbeeld het doorlopen van een lijst zijn. In een functionele taal beschrijf je wat je als eindresultaat verwacht zonder dat je exact specificeert wat er moet gebeuren om dat resultaat te bereiken. Dat laat je over aan de compiler en een eventueel achterliggend framework. De toenemende populariteit van multicore processoren is een van de oorzaken dat software in toenemende mate complexer wordt; er zijn immers extra mechanismes vereist voor multithreading. In dit artikel wordt ingegaan op een aantal concepten binnen F# die helpen bij het omgaan met de eerder genoemde problemen. Dit doen we aan de hand van functiecompositie, datatypes en asynchrone workflows. Compositie van functies Het gebruik van functiecomposities is binnen functionele talen een bekende manier om complexiteit te beheersen. Met deze samenstellingen kun je losse basisfuncties definiëren die samen herbruikbare onderdelen vormen voor nieuwe complexe functies. Verwar dit niet met het simpelweg aanroepen van functies vanuit een hoofdfunctie. Binnen F# is dit een rekenkundige bewerking en daardoor ook erg eenvoudig om te realiseren. Met behulp van de ComposeLeft (<<) en ComposeRight ( >>) operators kan je eenvoudig nieuwe functies samenstellen.
64
MAGAZINE
let SumPrices (PriceList : list<decimal>) = List.sum PriceList let IncludeTax OrderSum = OrderSum * 1.19M let SumAndIncludeTax = SumPrices >> IncludeTax let Prices : list<decimal> = [10.99M; 21.50M; 45.99M;] let result = SumAndIncludeTax Prices
Listing 1: Een kort voorbeeld van functiecompositie Als voorbeeld combineren we de functies SumPrices(x) en IncludeTax(x), wat resulteert in functie SumAndIncludeTax(x). In figuur 1 kun je zien dat beide functies een keten vormen. De uitvoer van Sum Prices(x) is de invoer van IncludeTax(x). Met andere woorden: SumAndIncludeTax(x) is gelijk aan IncludeTax(SumPrices(x)).
De exacte specificatie van het eindresultaat laat je over aan de compiler Zodoende is het belangrijk dat alle datatypes in de keten met elkaar overeenkomen om type mismatches te voorkomen. De F# compiler controleert dit door de functietypes van alle functies met elkaar te vergelijken. Een functietype is een korte beschrijving van de in- en uitvoertypes. Dit noteren we als ‘invoertype -> uitvoertype’. In figuur 1 staat de functie IncludeTax(x) beschreven. Deze accepteert en resulteert in een decimal, dus is het bijbehorende functietype ‘decimal -> decimal’. Het functietype van de resulterende samengestelde functie bestaat uit het parametertype van de eerste functie en het uitvoertype van de laatste functie.
Fig. 1: Schematische weergave van een samengestelde functie
De F# compiler vergelijkt alle functietypes van alle functies met elkaar
.NET C# Datatypes Een veelgebruikte manier om complexiteit en compositie te beheersen binnen software zijn de concepten achter object georiënteerd softwareontwerp. Daarom ondersteunt F# types en classes, wat niet gebruikelijk is binnen functionele talen, maar dit biedt binnen F# enorme voordelen. Een F# class kan bijvoorbeeld eenvoudig toegevoegd worden aan een bestaande C# applicatie. Daarnaast kan F# code hierdoor ook gebruik maken van het .NET framework en kan het door deze voordelen dienst doen als een verrijking bovenop bestaande applicaties. Door alle multithreaded code te verplaatsen naar een F# class kunnen complexe mechanismes in het C# deels worden vermeden.
Variabelen zijn immutable
Standaard types kun je vergelijken met structs in C#. Classes heten binnen F# concrete types. Beide specificeer je met het ‘type’-keyword. type Person = { Name : string; Age : int Weight : int
Fig. 2: Intellisense weergave van de Person class
}
Listing 2: Een F# type Zodra je haakjes noteert achter de naam van een type, is dit een class (zie listing 3).
Binnen een functionele taal moet elke variabele altijd direct toegewezen worden. Omdat dit bij properties niet altijd mogelijk is, kun je dit gedrag beïnvloeden door het DefaultValue attribuut te specificeren bij de betreffende variabele. Het gedrag van classes en properties past eigenlijk niet binnen de denkwijze van pure functionele talen, maar F# biedt in deze gevallen het beste van beide werelden.
namespace Demo type Person() = [] val mutable private _name : string [] val mutable private _age : int [] val mutable private _weight : int member public obj.Name with get() = obj._name and set(value : string) = obj._name <- value member public obj.Age with get() = obj._age and set(value : int) = obj._age <- value member public obj.Weight with get() = obj._weight and set(value : int) = obj._weight <- value
Als een member geen parameters bevat, dan beschouwt F# dit als een read-only property en een member met parameters is een method. Indien je binnen properties zowel get als set wilt ondersteunen, kun je de keywords ‘with’ en ‘and’ koppelen aan de bijbehorende property. member public obj.Weight with get() = _weight and set(value : int) = _weight <- value
Listing 4: Een property member Multithreading en asynchrone workflows In traditionele talen kan multithreaded programmeren problematisch zijn, omdat je als ontwikkelaar rekening moet gaan houden met alle bijbehorende administratie op de achtergrond. Je moet een aantal mechanismes inbouwen om te voorkomen dat problemen als deadlocks en racing conditions optreden, zoals monitors, mutexes en asynchrone methoden. Als je F# applicaties stateless ontwikkelt en
Listing 3: Een F# ‘concrete type’ / class
Deadlocks kunnen ontstaan als je niet stateless ontwikkelt of mutable variabelen gebruikt
In het bovenstaande voorbeeld zie je drie variabelen (_name, _age en _weight), die zichtbaar zijn binnen de klasse door het private keyword. Wat opvalt, is het gebruik van het ‘mutable’ keyword. Zoals in de inleiding is beschreven, zijn alle variabelen binnen F# standaard immutable. Met het mutable keyword kan de inhoud van een variabele aangepast worden. Dat is in het geval van classes belangrijk, omdat deze vaak afhankelijk zijn van een variërende state. Een property is daar een goed voorbeeld van omdat de achterliggende waarde gedurende de uitvoer van het programma vaak kan veranderen. Indien je dus de setter van een property wilt definiëren, zul je de achterliggende waarde als mutable moeten opgeven.
gebruik maakt van immutable variabelen, zul je hier niet zo snel tegenaan lopen. Dit dwingt je namelijk om bij elke bewerking een nieuwe instantie van de betreffende variabele aan te maken. Hierop kun je vervolgens de gewenste bewerking uitvoeren (zie figuur 3). Indien je toch mutable variabelen gebruikt, zijn deze voordelen niet meer van toepassing. Je kunt jezelf echter afvragen waarom die nodig zouden zijn, gezien de voordelen die immutable variabelen binnen F# bieden. F# bevat asynchrone workflows om parallelle verwerking te ondersteunen. Daar moet je wel het één en ander voor doen. Eerst moet de parallel uit te voeren code gedefinieerd worden met behulp van het async type. Je mag meerdere async types definiëren die elk een
member public obj.IsNameEqual othername = obj._name = othername
magazine voor software development 65
.NET C# In het bovenstaande voorbeeld vermenigvuldigt de functie DoSqr x met zichzelf en schrijft het resultaat naar de console. Door de functie het type async te laten retourneren, geef je aan dat deze functionaliteit onderdeel is van de asynchronous workflow. Zodra de applicatie wordt uitgevoerd zie je dat de volgorde van de output willekeurig is, wat aangeeft dat de code parallel is uitgevoerd. Conclusie In dit artikel zie je slechts een paar voorbeelden van de mogelijkheden die F# biedt. Hierdoor kan de ontwikkelaar zich meer concentreren op het implementeren van bijvoorbeeld business rules, zonder dat deze zich hoeft te bekommeren om de bijbehorende achtergrondadministratie van bijvoorbeeld threads. Asynchronous workflows zijn een goed voorbeeld hiervan. Daarnaast biedt F# interessante mogelijkheden op het gebied van compositie, zoals samengestelde functies, de volledige ondersteuning van classes en de mogelijkheid om F# projecten op te nemen in bestaande C# solutions. Dit alles moet leiden tot overzichtelijke, compactere en schaalbare applicaties, die gemakkelijk te implementeren zijn binnen bestaande software. Fig. 3: Omgang met immutable state andere taak op zich nemen. Je kunt het vergelijken met het maken van een parallelle batchjob. Je hoeft jezelf niet bezig te houden met het maken van threads, want dat doet het F# framework allemaal voor je op de achtergrond. Zodra alle taken gedefinieerd zijn, geef je alle async types door aan Async.Parallel. Deze retourneert een collectie die uitgevoerd wordt met Async.Run. Listing 5 geeft aan hoe je async types definieert en uitvoert. let x = async { return 10 + 20 } let y = async { return 20 + 30 }
Links • Microsoft F# Developer Center: http://msdn.microsoft.com/enus/fsharp/default.aspx • Channel 9 F# videos: http://channel9.msdn.com/tags/FSharp/ • Don Syme’s weblog on the F# language and related topics: http://blogs.msdn.com/dsyme/ •
Laurens Ruijtenberg
let asyncCollection = Async.Parallel( [x; y] ) Async.Run (asyncCollection)
Listing 5: Eenvoudige parallele uitvoer Omdat binnen functionele talen een collectie ook mag bestaan uit functies, kun je deze ook doorgeven aan Async.Parallel. Dit kan bijvoorbeeld een for-loop instructie zijn die asynchroon een list verwerkt.
Laurens Ruijtenberg is solution developer bij Avanade, de wereldwijde joint-venture tussen Microsoft en Accenture. Voor vragen of opmerkingen is hij te bereiken via [email protected]
open System let DoSqr(x) = async { let sqr = x * x do Console.WriteLine(sqr.ToString()) } let result = Async.Run (Async.Parallel [ for i in 1 .. 100 -> DoSqr(i) ])
Listing 6: Parallele verwerking van een for-loop
Fig. 4: De output van de asynchroon uitgevoerde for-loop
66
MAGAZINE
19 & 20 oktober a.s. SDN Conference 35 sprekers 100 sessies Schrijf nu in!
UX
Fons Sonnemans
Windows Sidebar Gadgets Ontwikkelen met
Silverlight 2.0 Gadgets zijn mini-applicaties die draaien binnen je Live.com homepage, Windows Live Space of Windows Sidebar. De Windows Sidebar is een balk aan de rechterkant van je desktop waarop je gadgets kunt plaatsen waardoor je snel toegang hebt tot je favoriete applicaties en informatie (zie figuur 1). Deze mini-applicaties zijn eigenlijk kleine HTML-pagina’s waarin de logica meestal in JavaScript geprogrammeerd is. Deze kun je ook in Silverlight 2.0 programmeren. In dit artikel leg ik uit hoe dit moet door een eenvoudige ‘Hello World’ voorbeeld gadget te bouwen. Om Windows Sidebar gadgets te gebruiken heb je Windows Vista of Windows 7 nodig. Silverlight 2.0 Silverlight is een browser plugin die gebruikt kan worden door verschillende browsers op verschillende besturingssystemen. De plugin zorgt ervoor dat je eenvoudig animaties, video en audio kunt gebruiken in je browser. Silverlight bevat een subset van de functionaliteit van Windows Presentation Foundation (WPF). Dit betekent dat je kunt programmeren in je favoriete programmeertaal, in mijn geval C#. Deze oplossing is zeer krachtig en moet dan ook gezien worden als een alternatief voor Flash en het daarbij horende ActionScript. Silverlight applicaties draaien in een browser, dus op de client PC. De applicaties worden via een