1. Woord Vooraf Mijn stage bij Deceuninck was geen gewone stage in de zin van een groot project dat werd uitge‐ voerd in opdracht voor mijn stagebedrijf. In plaats daarvan bestond mijn stageopdracht uit allemaal kleinere research‐opdrachten die elk op hun eigen manier een meerwaarde hebben voor Deceunin‐ ck. De grootste nadruk in mijn stage lag op research en pas daarna kwam het opstellen van kleine implementatie(s) van de oplossing(en). En daarmee bedoel ik dan niet alleen het uitzoeken hoe bepaalde dingen werken, maar ook of bepaalde oplossingen implementeerbaar zijn. Als gevolg hiervan, zijn de meeste onderdelen van mijn stageverslag dan ook geen grote uitgewerkte oplossingen. Het zijn hoofdzakelijk basisoplossingen waarmee men kan zien wat er wel en niet mogelijk is. In die zin was mijn stage veeleer een boeiende zoektocht in plaats van louter een analyse‐ en programmeeropdracht. Ik zou in dit woord vooraf dan ook graag een paar mensen willen bedanken die een grote rol hebben gespeeld in de begeleiding van mijn stage. Zonder hen zou het onmogelijk zijn geweest deze tot een goed einde te brengen. Eerst en vooral zou ik mijn stagementoren Bernard Vander Beken, Nick Sabbe en Sam Goutsmit willen bedanken voor de hulp die ze mij geboden hebben niet alleen tijdens de projecten maar ook om mij aan te passen aan het bedrijf en de algemene bedrijfssfeer. Ook zou ik mijn stagebegeleider Dirk Vandycke willen bedanken voor de tijd die hij in de coaching van mijn stage heeft gestoken. Als laatste zou ik ook mijn stagegever Filip Levrou, het bedrijf Deceuninck en de Hogeschool West‐ Vlaanderen willen bedanken voor de kans die ze me hebben geboden om deze ervaring mee te maken.
Arnout Logghe
Stageverslag
Pagina 3 van 172
2. Samenvatting Mijn stage bij Deceuninck bestond vooral uit research‐opdrachten en het oplossen van specifieke problemen. Sommige stageplaatsen spitsen zich toe op één grote analyse‐ en programmeeropdracht rond een centraal thema. Mijn opdracht daarentegen was veel ruimer: Onderzoeken of er oplossingen bestaan voor een aantal kleinere problemen en zo ja, welke mogelijke alternatieven in de praktijk het beste zijn. Als gevolg hiervan, zijn de meeste onderdelen van mijn stageverslag dan ook geen grote uitgewerkte oplossingen. Het zijn hoofdzakelijk basisoplossingen waarmee men kan zien wat er wel en niet mogelijk is. Een uitzondering hierop is het project van de ‘Russian Documents’ en het project van ‘Selenium’. Deze waren in tegenstelling tot de andere wel projecten waarvan een goed uitgewerkte oplossing verwacht werd. De reden hiervoor is dat het project van de ‘Russian Documents’ waarschijnlijk gebruikt zal worden in de productie en dat ‘Selenium’ op de IT‐afdeling intensief gebruikt zal worden als manier van automated testing. Af en toe leverde een onderzoek niet echt resultaat op, maar ook deze doodlopende sporen zijn het vermelden waard omdat ze informatie opleveren over wat er wel of juist niet mogelijk is. Een aantal van de opdrachten die ik kreeg, kan gegroepeerd worden en op die manier krijgen we volgende 4 grote categorieën:
Uitdenken dataflow Deze groep bestaat uit complexe opdrachten waar het uitdenken van de dataflow het belangrijkste is. Dit vereist uiteraard dat men goed moet weten wat er precies gevraagd wordt door de eindge‐ bruiker. Soms was dit echter niet het geval, het gevolg is dan ook dat er bij deze projecten soms meerdere dataflows werden uitgedacht en dus ook meerdere oplossingen bekomen. Het gedetailleerd uitwerken was dan ook meestal bijkomend van aard. Het belangrijkste was de dataflow en het onderzoek of deze effectief in een praktische oplossing kon omgezet worden. • TravelRequests • Russian Documents
Automated Testing voor webapplicaties Hier draait alles rond het automatisch uittesten van webapplicaties. Het doel is om het beste testprogramma te zoeken, dit dan te implementeren in een project en dat dan te laten draaien op de Arnout Logghe
Stageverslag
Pagina 4 van 172
buildserver van Deceuninck zodat men direct kan zien wat er fout loopt. Deze opdracht is opge‐ bouwd uit 3 subopdrachten: • • •
Zoeken van het beste Testing Framework Uittesten van een project van mijn bedrijf met Selenium Integreren van dit project met Buildserver
Research in .NET In deze 3de categorie zitten alle opdrachten die te maken hebben met research in .NET. • •
LINQ Conversie van VS2005‐2008 en verschil ertussen
Andere opdrachten Hierin vallen alle andere opdrachten die niet direct aan een groot project kunnen gelinkt worden. • •
Gridview CodeSmith
Arnout Logghe
Stageverslag
Pagina 5 van 172
3. Verklarende woordenlijst
3.1. Bedrijfsspecifiek Compounding Onder compounding wordt het proces verstaan waarbij je PVC – poeder met een 10 à 15 – tal producten mengt. Dit proces is noodzakelijk, omdat PVC – poeder in zuivere vorm niet voor extrusie geschikt is. Compounding is het proces waar je PVC‐poeder met een 10 à 15 – tal producten mengt. Dit is nodig omdat PVC‐poeder in zuivere vorm niet voor extrusie geschikt is.
Extrusie Extrusie is een continue verwerkingsmethode voor de productie van PVC‐profielen. Via een schroef in een verwarmde cilinder wordt de compound gehomogeniseerd, in plastische toestand gebracht en voorgestuwd naar de spuitmond van de procesmatrijs, waar het profiel zijn gewenste vorm aan‐ neemt. Daarna krijgt het profiel in de vormmatrijs zijn definitieve vorm. Eenvoudiger gezegd: een vloeibare PVC‐grondstof wordt door een vormplaat geperst en daarna afgekoeld.
Matrijs De matrijs is de vorm waarin vloeibare PVC‐grondstof wordt gespoten waardoor het eindprodukt(de uiteindelijke PVC‐profielen) een gewenste vorm aannemen en door afkoeling vast worden.
PVC PVC staat voor PolyVinylChloride en is een grondstof die vaak gebruikt wordt in de industrie ter vervanging van hout en beton. PVC is een grondstof die heel gemakkelijk te bewerken is, maar die wel schadelijk is voor het milieu.
Veredeling
Arnout Logghe
Stageverslag
Pagina 6 van 172
Profielen kunnen volledig naar wens van de klant worden afgewerkt. Deceuninck beschikt over verschillende technieken om het uitgebreid kleurengamma gestalte te geven. De profielen kunnen bekleefd worden met een UV–bestendige folie met houtstructuur. Bedrukking van verschillende motieven en kleuren wordt dan weer voornamelijk toegepast op decoratieve profielen voor binnenhuisinrichting. Zo hebben we de afwerkingstechniek Decoroc coating. Deze zorgt voor een matte, satijnachtige look. De korrelige structuur voelt prettig aan en is duurzaam en onderhouds‐ vriendelijk. Een andere techniek bestaat erin om de kleur aan de massa toe te voegen: de kleur van het profiel wordt dan bepaald door de pigmenten die aan de grondstof werden toegevoegd. Veredeling is dus in feite het afwerken van profielen met folie, bedrukking en coating.
3.2. IT specifiek ASP Asp of Active Server Pages is een technologie van Microsoft, waarmee we dynamische webpagina’s en websites kunnen aanmaken. Met dynamisch wordt bedoeld dat de webpagina’s iedere keer opnieuw zullen worden opgebouwd wanneer ze worden opgevraagd door een gebruiker. Het tegenovergestelde van dynamische webpagina’s zijn statistische webpagina’s zoals HTML.
Builden of compilen Het omzetten van broncode naar uitvoerbare code. Het gaat hier in feite om Intermediate Language, een soort van tussenstation tussen broncode en machinetaal die door .NET in runtime uitgevoerd kan worden.
Buildserver Zorgt ervoor dat alle geselecteerde toepassingen regelmatig opnieuw worden gecompileerd. Zo worden alle veranderingen die in de loop van de dag gebeuren aan de applicaties op het netwerk automatisch gecontroleerd op fouten. Bij Deceuninck gebruikt men CruiseControl.NET als buildser‐ ver. Deze gaat op zoek in SourceSafe naar de laatste versie van de applicaties.
CodeSmith CodeSmith is een freeware Code generator die werkt op basis van een sjabloon en gebruikt wordt om grote stukken code automatisch te laten genereren. Een voorbeeld hiervan is het aanmaken van klassen op basis van tabellen uit een SQL‐database. CodeSmith Templates gebruiken een syntaxis die bijna identiek is aan deze van ASP.NET.
Componentart
Arnout Logghe
Stageverslag
Pagina 7 van 172
Componentart is een bedrijf dat extra componenten levert voor het .NET framework. Het gaat hier over componenten met extra functionaliteit. Zo bestaat er een gridview waarin men een treeview kan steken, een combobox met meerdere kolommen enz…
CruiseControl.NET Software die gebruikt wordt om een buildserver op het netwerk te zetten.
Gridview Component van .NET waarmee een lijst van objecten op een grafische manier kan voorgesteld worden.
HTML Hypertext Markup Language is een taal voor de opmaak van documenten. HTML wordt vooral gebruikt op het Internet om webpagina’s te maken.
Imacros Programma voor geautomatiseerd testen dat in de browser werkt, en waarmee men websites en webapplicaties kan uittesten.
InfoPath Office programma waarmee men op een grafische manier XML‐bestanden kan aanpassen of aanmaken.
LINQ LINQ (.NET Language‐Integrated Query) is een component uit het .NET framework 3.5 en definieert een set van query ‐operators die men kan gebruiken om query’s en filtering toe te passen op data uit arrays, enumerables, klassen, XML en relationele database.
Selenium Arnout Logghe
Stageverslag
Pagina 8 van 172
Programma voor geautomatiseerd testen, waarmee men een applicatie of website grondig kan testen. Selenium werkt op 2 manieren. Remote Control: dit werkt via het ingeven van commando’s IDE: dit werkt in de browser met recording.
SharePoint Met Windows SharePoint Services kan je websites maken, informatie delen en documenten laten samenwerken. Het kan goed worden geïntegreerd met clienttoepassingen, waaronder het Microsoft Office system, biedt aanvullende functionaliteit en dient als een platform voor de ontwikkeling van toepassingen.
Spysmith Programma voor geautomatiseerd testen, waarmee men websites of webgebaseerde toepassingen kan testen. Met Spysmith kan men op objectniveau gaan kijken wat er allemaal verandert of gebeurt.
TestComplete Programma voor geautomatiseerd testen, waarmee men niet alleen applicaties of websites, maar ook services en Windows zelf kan testen. De testen van TestComplete worden opgeslaan als scripts die we dan kunnen uitvoeren.
Unittests Unittesten is een methode om softwaremodules of stukjes broncode (units) afzonderlijk te testen op een correcte werking. Bij Unittesten zal voor iedere unit een aparte test ontwikkeld worden.
Watir & FireWatir Programma voor geautomatiseerd testen dat gebruik maakt van Ruby.
XML Extensible Markup Language is een standaard voor het definiëren van formele markup‐talen voor de representatie van gestructureerde gegevens in de vorm van platte tekst. Dit gebeurt op een manier die leesbaar is voor het systeem én voor de mens . XML is in feite een bepaalde manier waarmee we gegevens gestructureerd kunnen vastleggen. Met XML kunnen we niet alleen gegevens opslaan, maar deze ook verzenden over het Internet. Arnout Logghe
Voor akkoord verklaard ......................................................................................................... 79
Arnout Logghe
Stageverslag
Pagina 11 van 172
5. Inleiding
5.1. Deceuninck: voorstelling van het bedrijf Groep Deceuninck, wereldwijd producent van kunststofraamsystemen en –bouwprofielen, is actief in de bouwindustrie en gespecialiseerd in compounding, matrijzenbouw, ontwerp, ontwikkeling, extrusie, veredeling en spuitgieten van kunststofraamsystemen, ‐profielen en ‐dichtingen en houtcomposiettoepassingen. Het bedrijf is het best bekend om het ontwerp en de productie van kunststofraam‐ en deursystemen en kunststofbouwprofielen voor toepassingen zoals ramen, deuren, luiken, rolluiken, muurbekledingen, lamellenplafonds. De omzet van Deceuninck bedraagt jaarlijks ongeveer 650 miljoen euro en het bedrijf telt wereldwijd ongeveer 3000 personeelsleden. Vooral in Europa is Deceuninck zeer sterk aanwezig, het bedrijf is dan ook marktleider in enkele Europese landen. De maatschappelijke zetel en het coördinatiecen‐ trum bevinden zich in België. De oorsprong van het bedrijf gaat terug tot het jaar 1937. Benari Deceuninck, vader van de huidige bestuurders, startte een klein bedrijf in Beveren‐Roeselare voor de productie van allerlei knopen, riemen, enz… van plastic materiaal. In de jaren '60 opteert de firma voor een nieuwe richting in de kunststofproductie: extrusie van PVC‐korrels voor de productie van profielen voor de bouwnijverheid (aanvankelijk profielen voor rolluiken, later raamprofielen) . Ondertussen is Deceuninck uitgegroeid tot een internationaal bedrijf dat actief is in meer dan 75 landen, met meer dan 35 filialen wereldwijd, van Noord‐Amerika en Europa tot en met Azië.
fig 1: Kaart met de verscheidene filialen van Deceuninck. Er zijn meerdere vestigingen per vlag
Arnout Logghe
Stageverslag
Pagina 12 van 172
fig 2: De structuur van de groep Deceuninck
5.2. Deceuninck en ICT De volledige ICT‐structuur binnen Deceuninck is gecentraliseerd in de hoofdzetel in Hooglede. ICT is een afdeling van Deceuninck die centraal de volgende activiteiten aanstuurt: •
Ontwikkeling en implementatie van alle IT‐applicaties. De IT‐afdeling maakt en onderhoudt de software voor de Deceuninck groep.
Arnout Logghe
Stageverslag
Pagina 13 van 172
•
Beheer van het Deceuninck netwerk (WAN) : De afdeling zorgt ook voor het netwerk, servers en pc’s binnen de groep.
•
Support: mensen kunnen vanuit elk filiaal met hun IT‐problemen hier terecht.
Fig 3: De administratieve vestiging van Deceuninck in Hooglede‐Gits
De ICT‐strategie binnen Deceuninck is gebaseerd op drie belangrijke pijlers: •
Platformbenadering: iedereen gebruikt dezelfde software per businessproject.
•
Best‐ of breed principe: De keuze van software wordt gebaseerd op de aangeboden functio‐ naliteit. De integratie met andere softwarepakketten wordt als minder belangrijk be‐ schouwd.
•
Consolidatie van alle applicatie‐infrastructuur biedt voordelen naar schaalbaarheid, TCO en fouttolerantie. Vermits het aantal softwarepakketten en het gebruik ervan toeneemt, stelt zich wel meer en meer de vraag naar integratie tussen al deze verschillende paketten.
De IT‐afdeling in Hooglede‐Gits is dan weer opgedeeld in 4 afdelingen: •
SAP
•
AS400
•
Helpdesk + systeembeheerders
•
Web‐development
Tijdens mijn stage werd ik ingeschakeld in de afdeling web‐development. Dit team staat in voor de verschillende websites en webapplicaties van Deceuninck en bestaat uit 3 personen: Nick Sabbe, Bernard Vander Beken en Sam Goutsmit.
Arnout Logghe
Stageverslag
Pagina 14 van 172
6. De Stageopdracht(en) 6.1. Travel Requests 6.1.1. Doelstelling van het probleem Een gebruiker moet reisaanvragen kunnen invullen op basis van een zelfgemaakt InfoPath Template. Deze aanvragen worden dan op SharePoint gezet, en moeten op de één of andere manier in een lijst terechtkomen waar men ze kan sorteren, filteren, enz.… De Template moet worden aangemaakt op basis van een gegeven view van de reisaanvragen; dit is een Excel‐bestand dat door het bedrijf wordt verstrekt. Ook belangrijk is dat alleen de gebruiker die het document heeft aangemaakt en de beheerder, het XML‐document kunnen zien en aanpassen. Mijn opdracht: • • • • •
maak op basis van de gegeven view van de reisaanvragen, een XML‐bestand aan; maak een Template op basis van dat XML‐bestand; zorg dat gebruikers op de een of andere manier d.m.v. deze Template, XML‐bestanden kun‐ nen genereren; zorg dat die XML‐bestanden ergens in een list(bv databank) terechtkomen, waar ze kunnen worden gesorteerd/gefilterd, enz.……; zorg ervoor dat de juiste machtigingen worden ingesteld en wel zodanig dat o alleen de gebruiker die het XML‐document op SharePoint heeft gezet en de beheer‐ der het XML‐document kunnen aanpassen; o er ook beheerders zijn die Templates kunnen beschikbaar stellen aan andere gebrui‐ kers en waarbij de XML‐documenten in hun map moeten terechtkomen.
Arnout Logghe
Stageverslag
Pagina 15 van 172
6.1.2. Eerste Oplossing: Hieronder staat een schematische voorstelling van de Dataflow van mijn oplossing(zie fig 1).
Sharepoint
Travel Requests
TemplatesInfoPath
3. In de travel request map, kunnen we nu sorteren/filteren/xml omzetten naar Excel, enz...
2. De template word ingevuld door de gebruiker, en wanneer op submit wordt geklikt, opgeslaan in sharepoint 1. We zenden de template Travel Request door via mail.
MailBox Fig 1: DataFlow van de eerste oplossing voor het probleem van de resiaanvragen
De oplossing houdt dus in dat er een InfoPath Template wordt aangemaakt. Deze Template moet dan worden opgeslaan op SharePoint, in de map TemplateInfoPath. Wanneer deze Template moet worden ingevuld, wordt ze via mail doorgezonden naar de gebruikers. Deze kunnen de Template invullen en dan op submit klikken. Het XML‐bestand met de ingevulde data wordt dan opgeslaan in de map Travel Requests op SharePoint. Hierop kunnen dan verschillende bewerkingen worden uitgevoerd zoals sorteren, filteren, XML omzetten naar Excel enz.…. Hoe dit alles precies in zijn werk gaat, heb ik hieronder uitgewerkt:
Arnout Logghe
Stageverslag
Pagina 16 van 172
• • • • • • • • •
aanmaken van een XML‐bestand; aanmaken van een InfoPath Template; aanmaken van een Form Library; instellen destination van de Template; de Template doorzenden via mail; de gebruiker vult de Template in en klikt op submit; bewerkingen in SharePoint; converteren naar Excel; instellen van de beveiliging;
Aanmaken XMLbestand Via het bedrijf krijgt men een Excel‐bestand met reisaanvragen(zie fig 2).
Fig 2: Excel‐bestand waarop mijn XML moest gebaseerd zijn.
Per aanvraag, zijn er dus eerst enkele reisaanvraagspecifieke datavelden zoals de supervisor’s name, final destination enz… Daarnaast is er ook de data van de tickets (zoals treintickets en vliegtuigtickets). Ook moeten hotelboekingen en huurauto’s opgeven worden. Er kunnen meerdere trein‐ en vliegtickets, hotel‐ en autoboekingen zijn per reisaanvraag. Er is ook rekening gehouden met andere economische alternatieven. Arnout Logghe
Stageverslag
Pagina 17 van 172
Wanneer we op basis hiervan een XML‐bestand TravelRequest.XML maken ziet dat er als volgt uit: <Surname>Bill Buckram <SupervisorsName> <MotivationFinalPlaning> <Time><Time><Time>< Rate><Time><PaymentOptions>
Er is dus een travelrequestheader, waarin de gegevens zoals de supervisors name, final destination, enz.…. staan.
Arnout Logghe
Stageverslag
Pagina 18 van 172
Daarnaast is er ook de travelrequestdetail, waar men de verdere gegevens van de tickets en boekingen kan vinden. Op het einde zijn er dan ook nog enkele reisaanvraagdetails, zoals bijvoorbeeld de transactionfee.
Aanmaken InfoPath Template Nadat het XML‐bestand is aangemaakt, wordt op basis hiervan een InfoPath Template aangemaakt. Een InfoPath Template is in feite een XML Template waarmee ook de grafische visualisatie van het in te vullen of aan te passen XML‐bestand kan worden ingesteld. Dit is ideaal om ervoor te zorgen dat een dergelijk bestand op een gebruikersvriendelijke manier kan worden aangemaakt of gewijzigd op basis van een bestaande XML‐structuur. Voor het aanmaken van een InfoPath Template, zie bijlage 1: Aanmaken InfoPath Template Het eindresultaat ziet er als volgt uit (zie fig 3).
Fig 3: Printscreen van het uiteindelijke InfoPath form
De rentalcars, remarks en rentalcaralternatives zijn repeating values. Hetzelfde geldt voor de overeenkomstige velden van airtickets, traintickets en hotel reservations. Ook travellers is een repeating value. Alle kosten van de tickets en de boekingen worden afzonderlijk opgeteld en het resultaat komt in het veld Total Rate. Dit veld wordt eerst aangemaakt in InfoPath.
Arnout Logghe
Stageverslag
Pagina 19 van 172
Deze Total Rates worden op hun beurt weer opgeteld en de som komt in het veld Total estimated travel cost: sum(TotaalTrainTickets) + sum(TotaalAirTickets) + sum(TotaalReservations) + sum(TotaalRentalCars). Idem voor de more economic travel costs, alleen deze keer met de Total rates van de alternatives.
Aanmaken Form Library Nu kan men op basis van de InfoPath Template een eigen Form Library Travel Requests aanmaken. Wanneer de gebruiker dan de Template invult en doorzendt, komen de gegenereerde XML‐ bestanden in deze Form Library terecht. Aanmaken van een InfoPath Template zie bijlage 2: Aanmaken Form Library Op SharePoint is nu een nieuwe Form Library map Travel Requests aangemaakt. De XML‐bestanden worden gegroepeerd per maand en jaar door per maand en jaar een submap aan te maken. Tevens moet ervoor gezorgd worden dat de nieuwe XML‐bestanden allemaal in Travel Requests toekomen en dat de administrator van deze site, de XML‐bestanden in de juiste map kan gaan zetten. De Form Library ziet er nu als volgt uit (zie fig 4)
Fig 4: Aangemaakte Form Library.
Het probleem is dat men de XML‐bestanden in de Standard View niet naar een andere map kan slepen. Er bestaan echter nog andere views waaruit men kan kiezen, zoals de Explorer View. Deze lijkt op Windows verkenner en hier kan men wel XML‐bestanden naar andere mappen slepen.
Arnout Logghe
Stageverslag
Pagina 20 van 172
Instellen destination in Template en deze op SharePoint zetten Wanneer er op submit geklikt wordt, moet het formulier naar de juist aangemaakte Form Library op SharePoint worden verzonden. Hiervoor opent men de Template in design mode en kiest men – tools – submit options (zie fig 5).
Fig 5: Instellen van de submit options.
Eerst wordt “allow users to submit this form” en “send form data to a single destination” aangevinkt en de SharePoint document library wordt gekozen. Daarna wordt de Data Connection Wizard doorlopen waarin je onder andere kan opgeven naar welke document library de XML‐bestanden moeten worden gezonden en onder welke naam de XML‐bestanden moeten worden bewaard (zie fig 6)
Arnout Logghe
Stageverslag
Pagina 21 van 172
Fig 6: Data Connection Wizard.
We vullen hier het pad in van ons juist aangemaakt Form Library. Als bestandsnaam, nemen we een concatenatie van: “TravelRequest”, de datum van vandaag, “Destination” en de waarde finaldestina‐ tion uit onze Template. Op deze manier kunnen we heel gemakkelijk per XML‐bestand zien, wanneer het op de SharePoint werd gezet en wat de bestemming van de reisaanvraag is. Dit is heel handig, wanneer we de Travel Requestss willen groeperen per maand per jaar of per bestemming. Bij het invullen van de Template komt nu het gegenereerde XML‐document toe in de juiste map op SharePoint. Nu moeten we alleen de Template zelf nog in de juiste map op SharePoint zetten namelijk in de map TemplatesInfoPath.
Template doorzenden via mail Wanneer nu iemand een nieuwe reisaanvraag wil invullen, zenden we deze Template door naar de gebruiker met een link via mail (zie fig 7).
Arnout Logghe
Stageverslag
Pagina 22 van 172
Fig 7: Doorzenden van de Template via mail.
Wanneer de gebruiker op deze link klikt, wordt er een lege Template geopend, en kan de gebruiker deze Template invullen.
Gebruiker vult de Template in en klikt op submit Nu kan het lege InfoPath Template ingevuld worden. Bij het klikken op submit, wordt dit doorgestuurd naar de SharePoint Form Library Travel Requests, en komt er een melding dat het formulier succesvol verzonden is( zie fig 8).
Fig 8: Boodschap dat het formulier successvol is gesubmit.
Arnout Logghe
Stageverslag
Pagina 23 van 172
Bewerkingen in SharePoint Wanneer men nu naar SharePoint gaat, ziet men dat de gegenereerde XML‐bestanden erin staan (zie fig 9).
Fig 9: XML‐bestanden in SharePoint.
Men kan nu een aantal bewerkingen uitvoeren op deze map zoals filteren, door op de filter te klikken bovenaan (je kunt per kolom een filter instellen). Daarnaast kan men ook sorteren, XML‐bestanden aanpassen, maar ook XML‐forms met elkaar mergen. Om XML‐forms met elkaar te mergen, moet men naar de Merge Forms View gaan, hierin kan men dan aanvinken welke forms allemaal moeten worden gemerged (zie fig 10).
Arnout Logghe
Stageverslag
Pagina 24 van 172
Fig 10: Merge Forms View.
Wanneer we bijvoorbeeld 2 Travel Requests mergen, krijgen we volgend resultaat (zie fig 11).
Fig 11: twee forms die gemerged zijn.
Arnout Logghe
Stageverslag
Pagina 25 van 172
We kunnen echter geen queries over meerdere submappen uitvoeren. Wel kunnen we de mapin‐ houd van de Form Library en de onderliggende mappen naar Excel of Access converteren, en daar bewerkingen uitvoeren. Voor het converteren van de mapinhoud van de aangemaakte Form Library naar Excel, zie bijlage 5: converteren van de mapinhoud van een Form Library naar Excel Nu kan men wel sorteren en filteren op mappen en hun submappen, in Excel weliswaar (zie fig 12).
Fig 12: mapinhoud van een Form Library dat is geconverteerd naar Excel.
Instellen beveiliging Nu moet de beveiliging nog zodanig worden ingesteld, dat alleen de beheerders en de gebruikers die een XML‐bestand op SharePoint hebben gezet, dit XML‐document kunnen aanpassen. Op een eenvoudige wijze kan je op een bepaalde map, voor een bepaalde user bepaalde machtigin‐ gen instellen(zie fig 13).
Arnout Logghe
Stageverslag
Pagina 26 van 172
Fig 13: Instellen van een user machtiging op een map.
Men kan instellen dat alle forms die toekomen automatisch op pending state ingesteld worden. Zo zijn ze niet zichtbaar voor iedereen, maar enkel voor diegene die ze gemaakt heeft, en voor de beheerders natuurlijk. Later kan men ze dan wel of niet goedkeuren (zie fig 14).
Fig 14: Alle items moeten standaard als pending ingesteld worden.
Arnout Logghe
Stageverslag
Pagina 27 van 172
Wanneer men nu alles in de General View gaat bekijken, zijn ze allemaal onzichtbaar. Wanneer men in de My Submissions View gaan, ziet men alleen diegene die men zelf heeft toegevoegd.
Probleem: •
Deze oplossing kan gemakkelijk omzeild worden. In de Explorer View ziet men wel alle forms en kunnen ze ook allemaal aangepast worden door iedere gebruiker.
6.1.3. Tweede Oplossing Om het veiligheidsprobleem in de vorige oplossing te vermijden, zal er dus een nieuw systeem bedacht moeten worden. In deze oplossing wordt een Travel Requests Template op SharePoint beschikbaar gesteld via een portal. De eindgebruiker kan nu via dit portal de Template openen en invullen. Daarna kan hij de Template doorzenden naar de coördinator via mail. Deze bekijkt de ingevulde Template, en geeft enkele opties op voor de reis (je hebt volgende vluchten, je hebt volgende hotel boekingen, enz...) en zendt dan de aangepaste Template terug naar de eindgebruiker. Die bekijkt de Template, kiest 1 van de opties die gegeven werden en stuurt het dan opnieuw naar de coördinator. Deze voegt er nu een referentienummer aan toe en plaatst het eindresultaat op SharePoint. De coördinator zendt ook een mail ter confirmatie naar de eindgebruiker. Op deze manier moet de gebruiker nooit naar de Travel Requests map op SharePoint gaan, maar moet hij alleen maar naar het portal van de Template gaan. En dus is er geen probleem meer qua beveiliging. De vereisten van wat de Template moet kunnen, zijn ondertussen wel aangepast: •
•
per treinreis/vlucht, enz.…. bestaan er meerdere alternatieven. In de huidige Template heb‐ ben we maar 1 alternatieve lijst(met verschillende alternatieven eronder). We hebben echter meerdere alternatieve lijsten nodig (bv. een alternatieve lijst van vluchten vanuit Zaventem, Charleroi, enz…..); ook moeten we kunnen selecteren welk alternatief we nemen.
We moeten dus de volgende dingen aanpassen aan de vorige oplossing om deze werkende te krijgen: • • •
aanmaken van een nieuwe InfoPath Template; aanmaken van een Form Library; opslaan van het ingevulde Template(XML‐bestand) op SharePoint door de coördinator.
Aanmaken nieuwe InfoPath Template We zullen een nieuwe Template moeten aanmaken, waarin de gestelde vereisten wel mogelijk zijn, namelijk de Template ReisaanvragenUltiem.xsn(zie fig 15).
Arnout Logghe
Stageverslag
Pagina 28 van 172
We moeten dus zorgen, dat we meerdere alternatieve combinaties kunnen opstellen bij een bepaalde boeking of tickets (meerdere alternatieve vliegreizen bijvoorbeeld). We moeten dan door middel van een combobox één van deze alternatieven selecteren. Daarnaast moeten we ook een Reference Number kunnen opgeven.
Fig 15: Aangepast InfoPath Template.
Wat we eerst en vooral moeten aanpassen, is onze Data Source. We moeten ervoor zorgen dat trainticketalternative én ticket repeating values zijn. Trainticketalternative is dan een groepering van een lijst van alternatieve treintickets. Per trainticketalternative gaan we ook een nummer toevoegen. Via dit nummer kunnen we gemakkelijk naar een bepaald trainticketalternative verwijzen. Hiervoor voegen we zelf het veld my:test toe aan trainticketalternative. We voegen ook nog een ander veld toe, my:Alternativetrains, en slepen dit als combobox in het formulier. Nu gaan we ervoor zorgen, dat deze combobox een lijst bevat van alle nummers van de alternatieve treintickets zodat men gemakkelijk een alternatief kan kiezen. Hiervoor moeten we de properties van de combobox aanpassen (zie fig 16).
Arnout Logghe
Stageverslag
Pagina 29 van 172
Fig 16: Instellen properties van de combobox Alternativetrains.
We gaan hier de listbox entries zetten op “Look up values in the form’s Data Source”. Dit betekent dat de waarden in de combobox opgehaald worden uit de Data Source van het formulier. Bij entries selecteren we dan de trainticketalternative en bij values de nummers van deze trainticketalternative, namelijk het veld my:test. Nu moeten we alleen nog maar een nieuw veld toevoegen, my:referencenumber, en dit als textbox in ons formulier slepen. Onze Template is nu aangepast aan de nieuwe vereisten waaraan het moest voldoen. Wanneer we nu de Template invullen met 2 alternatieve vluchten naar Moskou ‐ 1 via Leipzig, en 1 via Berlijn ‐ kunnen we dan één van deze alternatieven kiezen (zie fig 17).
Arnout Logghe
Stageverslag
Pagina 30 van 172
Fig 17: Ingevulde Template.
Het reference number bovenaan kunnen we nu ook met een integer waarde invullen.
Opslaan van ingevuld Template(XMLbestand) op SharePoint door de coördinator Wanneer de Template juist is ingevuld, de gebruiker de opgegeven opties heeft geselecteerd en het reference number is toegevoegd, moet de coördinator het XML‐bestand nog op SharePoint zetten. Hij kan dit doen door naar de map Travel Requests op SharePoint te gaan en dan te kiezen voor upload form. Er verschijnt een textbox, waar we naam en pad van het formulier kunnen ingeven, en een knop browse, waarmee we naar het InfoPath formulier kunnen zoeken. Wanneer we een formulier hebben ingegeven, klikken we op save en close, en wordt het formulier toegevoegd aan de map Travel Requests.
6.1.4. Conclusie Ik heb voor dit project 2 dataflows en dus 2 oplossingen uitgedacht.
Arnout Logghe
Stageverslag
Pagina 31 van 172
De eerste oplossing genereert XML‐bestanden op basis van een InfoPath Template en data die de gebruiker ingeeft. Het resultaat hiervan wordt dan op SharePoint opgeslaan. Deze oplossing was echter door een tekort aan beveiliging binnen SharePoint 2003 geen optimale oplossing. Als repons hierop werd oplossing 2 uitgedokterd. Deze is wel optimaal, omdat hier de beveiliging van SharePoint niet langer een issue is. De gebruiker moet namelijk niet langer SharePoint aanspreken, alleen de coördinator moet dit nog doen.
Arnout Logghe
Stageverslag
Pagina 32 van 172
6.2. Gridview sorting probleem 6.2.1. Doelstelling Bij Deceuninck werkt men in Visual Studio met speciale controls, ‘Componentart controls’ genoemd. Deze tools zorgen voor extra functionaliteit en mogelijkheden. Zo is er bijvoorbeeld een Compo‐ nentart grid waarin we gemakkelijk een bepaalde Treeview kunnen tonen. Of een combobox met zoekfunctie die alle items toont die een bepaald stuk bevatten … In feite werken die Componentart componenten net zoals de componenten die we al kennen in Visual Studio(een Componentart grid werkt net zoals een Standaard gridview), alleen hebben ze meer functionaliteit. Er is echter een probleem met één van de Componentart controls, meerbepaald met een Compo‐ nentart grid die in één van de projecten van mijn stagebedrijf wordt gebruikt. Omdat ik zelf geen aanpassingen mag uitvoeren in de broncode van applicaties die bij Deceuninck actief gebruikt worden, heb ik het probleem geschetst op basis van een zelfgemaakt project met eigen data. In dat project heb ik een Componentart grid met 3 kolommen (zie fig 1): • • •
de kolom naam die de voornaam van een persoon bevat; de kolom familienaam die de familienaam van een persoon bevat; de kolom image die aangeeft of de persoon tot het rode of groene team behoort.
Fig 1: de gridview die gebruikt wordt bij Deceuninck.
Het probleem zit in de sortering, wanneer we vb willen sorteren op naam, worden de personen juist gesorteerd op naam met Arnout Logghe als eerste, dan Bert Vanhuyse enz…. Alleen worden de images niet mee gesorteerd, ze blijven gewoon staan zoals ze stonden (zie fig 2).
Arnout Logghe
Stageverslag
Pagina 33 van 172
Fig 2: De gridview gesorteerd op naam.
De data komt uit een XML‐bestand dat gekoppeld wordt aan de datagrid. Dit XML‐bestand bevat personen, die elk een naam, familienaam, soort en buttonwaarde hebben:
<Soort>1 ArnoutLogghe <Button>0
In deze gridview gaan we de tabellen hardcoded instellen:
Zoals je kunt zien, wordt er aan de kolom soort een ServerTemplate gekoppeld: Arnout Logghe
Stageverslag
Pagina 34 van 172
Dit is een lege hyperlink, maar bij het laden van de pagina wordt er ‐ op basis van de waarde die in de kolom soort terechtkomt ‐ een rood of groen vlaggetje in deze hyperlink geplaatst: for(int i = 0; i < Grid1.Items.Count;i++) { string naam = Grid1.Items[i]["Naam"].ToString(); HyperLink link = (HyperLink)(ComponentartUtility.GetGridTemplateControl(Grid1, "Soort", "imageHyperLink", i)); int soort = int.Parse(Grid1.Items[i]["Soort"].ToString()); switch (soort) { case 1: link.ImageUrl = "../images/flag_green.gif"; break; case 2: link.ImageUrl = "../images/flag_red.gif"; break; default: link.ImageUrl = "../images/flag_none.gif"; break; } }
Componentartutility is een C# project in de Solution waarbinnen er gewerkt wordt, dat enkele methods bevat die we in meerdere projecten kunnen gebruiken. Een van deze methods is getgridTemplatecontrol. Deze method worsdt opgeroepen met de variabe‐ len grid, kolom, Template en rij en geeft een control terug van deze grid, kolom, Template en rij. Uiteindelijk moet het XML‐bestand nog aan het grid worden gekoppeld. Dit gebeurt in de method buildgrid, die in de page_load wordt opgeroepen:
public void BuildGrid() { DataSet ds = new DataSet(); XMLDataSource source = new XMLDataSource(); ds.ReadXML(@"D:\SOURCESAFE\Spikes\StageArnoutLogghe\Stage.ArnoutLogghe.Grid vie
zoek een manier om de images toch mee te sorteren, wanneer er gesorteerd wordt op naam of familienaam; zorg ervoor dat er naar een nieuwe pagina wordt gegaan wanneer er op een image wordt geklikt. Aan deze pagina wordt meegegeven op welke image we hebben geklikt door middel van de griditemindex.
6.2.2. Oplossingen om de vlaggetjes mee te sorteren: De optimale oplossing zou eruit bestaan om alles wat betreft de sortering client‐side te laten gebeuren. Het sorteren op zich in de gridview gebeurt immers ook client‐side. Er werden verschillende oplossingen uitgedacht en geïmplementeerd om dit probleem op te lossen, de ene al beter werkbaar dan de andere. De meest optimale oplossing staat als laatste vermeld; dit is ook de oplossing die door mijn stagebedrijf zal gebruikt worden.
Door middel van een client Template, in plaats van een server Template Met behulp van een asp image We hebben eerst geprobeerd om via een client Template een asp:hyperlink in te voegen:
In de gridcolumn van soort, gaan we dan gaan verwijzen naar een client Template, in plaats van een server Template:
Arnout Logghe
Stageverslag
Pagina 36 van 172
We krijgen dan als resultaat een grid zonder vlaggetjes(zie fig 3)
Fig 3: Het grid zonder vlaggetjes.
We gaan proberen het asp image, client‐side op te roepen: GridServerTemplateContainer control = (GridServerTemplateContainer)Grid1.Controls[0]; LiteralControl mage = (LiteralControl)control.Controls[0];
Problemen •
wanneer we de applicatie laten lopen verschijnen er geen images. Î Waarschijnlijk kan het programma de asp hyperlink niet client‐side valideren (omdat het een server‐side control is die in een client Template zit).
Met behulp van een HTML image In plaats van een asp‐object kunnen we ook een HTML‐object gebruiken, de clientTemplate wordt dan:
Wanneer we dit uitvoeren, krijgen we een gridview met alleen maar gele vlaggetjes(zie fig 4).
Arnout Logghe
Stageverslag
Pagina 37 van 172
Fig 4: Een gridview met alleen maar gele vlaggetjes.
De applicatie plaatst dus wel images in de juiste kolom
Problemen •
We kunnen de images niet server‐side aanspreken. Î Het programma herkent de images niet, waarschijnlijk omdat er alleen een client‐side referentie naar de images bestaat. We kunnen deze images dus niet server‐side veranderen. Wanneer we dit proberen krijgen we een null waarde.
Door middel van ConditionalFormat We gaan aan de code van ons grid, een ConditionalFormat toevoegen. Deze ConditionalFormat moet ervoor zorgen dat aan een bepaalde rij het juiste vlaggetje toegekend wordt. Dit gebeurt door te verwijzen naar een Template. De code van de ConditionalFormat:
We plaatsen hier dus een cliëntfilter op het data‐item soort. De ConditionalFormat gaat alle rijen in onze gridview doorlopen: wanneer soort de waarde 1 heeft in een rij gaan we aan die rij de css redrow koppelen, bij waarde 2 de css bluerow.
Arnout Logghe
Stageverslag
Pagina 38 van 172
De css klassen redrow en bluerow, zetten de achtergrondkleur van een rij respectievelijk op rood of blauw.
Problemen •
We kunnen in de ConditionalFormat alleen verwijzen naar een css‐class, niet naar een Tem‐ plate.
Door middel van een callback serverside Een andere oplossing bestaat in het gebruiken van een callback om de server client‐side aan te spreken wanneer het grid gesorteerd wordt. Op dat moment wordt deze callback client‐side opgeroepen. Hiervoor wordt een clientevent aangemaakt dat bij oproeping de on_sort function uitvoert: <SortChange EventHandler="On_Sort" />
Die on_sort function roept callback aan en geeft als argumenten de kolom mee waarop gesorteerd is en ook of dit stijgend of dalend gebeurt. <script language="Javascript" type="text/Javascript"> //
Om een callback client‐side te kunnen oproepen, moeten we de gridview in een callback tag plaatsen:
Deze callback gaat dan server‐side de method on_callback oproepen, en geeft enkele callback event arguments mee. Wij hebben hierin de kolom en de manier van sorteren opgeslaan: protected void on_callback(object sender, CallBackEventArgs e) { BuildGrid(); headingtekst = e.Parameters[0].ToString(); descending = e.Parameters[1].ToString(); string naam = headingtekst.Substring(1, headingtekst.Length-1); Boolean ja;
Arnout Logghe
Stageverslag
Pagina 39 van 172
Grid1.Levels[0].IndicatedSortColumn = naam; Grid1.Levels[0].IndicatedSortDirection = descending; if (descending == "0") { ja = false; } else { ja = true; } Grid1.Items.Sort(Grid1.Levels[0].Columns[naam], ja); //ModifyGrid(); Grid1.RenderControl(e.Output); }
De method on_callback gaat eerst het grid gaan builden (=koppelen van de datasource). Dan haalt het de argumenten op die aangeven hoe en op welke kolom er gesorteerd werd. Deze worden in variabelen geplaatst. Nu kunnen we het grid gaan sorteren op basis van deze variabelen. Daarna wordt het opnieuw opgebouwd met de functie rendercontrol. Wanneer we ons programma nu laten lopen, worden de vlaggetjes meegesorteerd. Deze oplossing is echter nog niet ideaal omdat een en ander nog server‐side gebeurt.
Door gebruik te maken van < % %> Vervolgens hebben we geprobeerd om met behulp van < % %> alleen nog client‐side te werken met behulp van een client Template. We gaan in de < % %> gaan kijken naar de waarde van soort . Op basis daarvan wordt de juiste HTML‐image gekoppeld. <% if(Container.DataItem.GetMember("Soort").Value == 1){%> <% }else{%> <%}%>
Problemen We moeten hier op de één of andere manier naar de waarde van soort kunnen verwijzen. Alleen werkt data‐item hier niet; en ook op andere manieren lukt het niet.
Arnout Logghe
Stageverslag
Pagina 40 van 172
Door gebruik te maken van ## ## De callback server‐side methode werkte wel, maar was niet ideaal, omdat er zowel server‐side als client‐side geprogrammeerd moest worden om te kunnen sorteren. We weten dat we met een client Template wel in staat zijn een image in de juiste kolom te plaatsen, we kunnen er alleen niet server‐side naar verwijzen. Daarom wordt de volgende oplossing bedacht : In de clientTemplate wordt de juiste image gekop‐ peld aan de kolom soort door gebruik te maken van ## ## en if. Eerst gaan we het XML‐bestand zodanig aanpassen dat er in soort niet 1 of 2 staat, maar de naam van de image(flag_green.gif, flag_red.gif). Wanneer dat gebeurd is, kunnen we als volgt naar de image verwijzen : ../Images/## DataItem.GetMember('Soort').Value ##
Deze oplossing werkt ook zoals gevraagd werd, maar op deze manier kunnen we wel volledig client‐ side sorteren!!!
Problemen Deze oplossing is nog niet ideaal: het zal niet werken wanneer we om de één of andere reden toch willen werken met 1 of 2 als waardes van soort. De waarde van soort, moet hier immers de naam van de vlag zijn!
6.2.3. Optimale Oplossing: Wat we ook kunnen doen, is binnen de ## ## tag, naar een functie verwijzen. Op deze wijze zullen we dan op basis van een waarde, de juiste image teruggeven. Onze clientTemplate wordt nu:
We gaan hier verwijzen naar een zelf gemaakte functie BepaalPad, we geven hieraan de waarde 1 of 2 mee(de waarden uit ons XML‐bestand), en de functie geeft de juiste image terug. De functie BepaalPad:
De functie krijgt dus een bepaalde waarde: bij 1 geeft hij een groen vlaggetje terug, anders geeft hij een rood vlaggetje terug. Deze oplossing werkt en is ideaal, de waarde van soort hoeft nu niet meer de naam van de image te zijn die we moeten afbeelden, en alles werkt client‐side. We zijn niet meer van de server afhankelijk om te kunnen sorteren en alle vlaggetjes worden mooi meegesorteerd. (zie fig 5, 6, 7 en 8).
Fig 5: Beginscherm.
Fig 6: Stijgend gesorteerd op naam.
Arnout Logghe
Stageverslag
Pagina 42 van 172
Fig 7: Dalend gesorteerd op naam.
Fig 8: Stijgend gesorteerd op familienaam.
6.2.4. Oplossingen voor het klikken op een image: Wanneer we op de image van een bepaalde rij klikken, moet er een nieuwe pagina geopend worden. Deze pagina bevat in de titelbalk de waarde van de image waarop we geklikt hebben. Dit moet gebeuren op basis van de griditemindex. Eerst gaan we ervoor zorgen, dat bij het klikken op een asp button, er server‐side verwezen wordt naar een nieuwe pagina. (met als parameter de naam van die rij). Daarna gaan we ervoor zorgen dat dit gebeurt wanneer we klikken op een image met behulp van de griditemindex. (We hebben hier dan als parameter niet de naam van de rij, maar de value van soort). Hiervoor maken we een nieuwe lege pagina aan: ToonDetails.aspx. Eerst maken we de button wijzig client‐side aan. Hiervoor maken we een nieuwe kolom button aan in ons grid. Daaraan wordt dan een serverTemplate gekoppeld:
Arnout Logghe
Stageverslag
Pagina 43 van 172
Die serverTemplate krijgt de naam ButtonWijzig:
De method modifygrid haalt de wijzigbutton server‐side op en kent er een PostBackUrl er aan toe. In deze url wordt verwezen naar de pagina ToonDetails.aspx en als parameter krijgt hij de de value naam mee van de rij. public void ModifyGrid() { if (Grid1.DataSource != null) { string naam = Grid1.Items[i]["Naam"].ToString(); Button button = (Button)(ComponentartUtility.GetGridTemplateControl(Grid1, "Button", "ButtonWijzigen", i)); string URL = "~/Pages/ToonDetails.aspx?naam=" + naam; button.PostBackUrl = URL; } } }
Dit werkt: wanneer we nu op de button wijzig klikken van de rij Arnout, zal ToonDetails.aspx?Arnout geopend worden (zie fig 9).
Fig 9: Wanneer we geklikt hebben op een wijzig knop, opent hij een nieuw form met in het pad de naam van de persoon van die rij.
In plaats van op een button te klikken, zou je eigenlijk op de image moeten kunnen klikken en daarbij gebruik maken van de griditemindex. Om dit probleem op te lossen is het volgende uitgeprobeerd: •
document.getElementById(Grid1).SelectedIndex; Î Werkt niet, we kunnen niet de selectedindex van het grid nemen. De selectedIndex blijft op ‐1 staan, ook als we een dataitem selecteren.
Arnout Logghe
Stageverslag
Pagina 44 van 172
• • •
•
string selecteditem = Grid1.SelectedItems[0].Items[0].ToString(); Î Werkt niet, We krijgen een null waarde, ook wanneer we een dataitem selecteren. we zijn gaan kijken naar de inhoud van Request.Headers; Î Hierin vinden we niets bruikbaars we zijn gaan kijken naar de inhoud van Request.Form; Î Hierin vinden we ook niets bruikbaars, wanneer we in request.form het grid proberen aan te spreken, krijgen we een null waarde! Request.ParamsÎ Ook hierin vinden we niets bruikbaars.
Problemen •
We kunnen server‐side niet bepalen op welke image in ons grid we client‐side hebben ge‐ klikt.
•
We kunnen niet aan de griditemindex geraken. We kunnen echter wel kijken naar de source code van onze pagina (zie fig 10).
Fig 10: De source code van onze pagina.
De data van ons grid, bevat wel 1, Arnout, logghe, enz… Maar we kunnen de index van iedere rij niet zomaar vinden. Heeft iedere rij in een ComponentartGrid wel een index?
Arnout Logghe
Stageverslag
Pagina 45 van 172
Hier zit ik vast; ik heb het probleem voorgelegd aan mijn stagementors, en ook zij weten niet direct een oplossing. Daarenboven kwamen er andere projecten met een hogere prioriteit en heb ik dit project niet verder kunnen afwerken. Jammer …
6.2.5. Conclusie Er bestaat inderdaad een optimale oplossing om ervoor te zorgen dat het grid kan gesorteerd worden, ook als er bepaalde zaken client‐side worden gegenereerd, zoals bv images. De beste oplossing maakt gebruik van ## ## en verwijst naar een functie die het juiste pad van het image teruggeeft op basis van een meegegeven waarde. We hebben echter geen oplossing gevonden waarmee we door de griditemindex kunnen bijhouden op welk image we hebben geklikt. Noch mijn stagementoren, noch ikzelf wist hier een goede oplossing voor. Bovendien kregen we toen te kampen met tijdsgebrek ….
Arnout Logghe
Stageverslag
Pagina 46 van 172
6.3. Automated Testing Deceuninck wil graag een programma dat automatisch de projecten van het bedrijf kan uittesten. Mijn opdracht was dan ook als volgt: Op zoek gaan naar automatische testprogramma’s en onder‐ zoeken welk testprogramma het best geschikt zou zijn voor mijn stagebedrijf. Eens die beslissing genomen, gaan we dit testprogramma dan wat dieper bekijken waarbij we ook een bestaand project van Deceuninck gaan testen. Later gaan we dan dit project op de buildserver plaatsen, en kijken of de tests nog altijd blijven draaien bij het builden.
6.3.1. Eerste subproject : Zoeken van het beste Testing Framework Doelstelling Het doel is om enkele automated testing programma’s te gaan uittesten, en te onderzoeken welk testprogramma het best geschikt zou zijn voor Deceuninck. Het meest gechikte testprogramma wordt daarna diepgaander bekeken en uitgetest. Er zijn 5 verschillende testprogramma’s die in aanmerking komen: • • • • •
Alle testprogramma’s moeten voldoen aan volgende vereisten: • • •
Google Search testing, uittesten van een ASP.NET applicatie zonder Javascript, uittesten van een ASP.NET applicatie met Ajax & Javascript & Componentart.
De ASP.NET applicatie zonder Javascript: Deze applicatie zit heel eenvoudig in elkaar: we hebben enkele asp‐textboxen waar we een naam en voornaam dienen in te vullen, een combobox voor de leeftijd en een radiobuttonlist waarin we de bestemming van onze vakantie kunnen ingeven. Wanneer er op submit geklikt wordt, gaan we via response.Redirect() naar een resultaatpagina waar de ingevulde data wordt getoond (zie fig 1).
Arnout Logghe
Stageverslag
Pagina 47 van 172
Fig 1: Het beginscherm van de ASP.NET applicatie zonder Javascript
De ASP.NET applicatie met Ajax & Javascript & Componentart: Deze applicatie is iets complexer dan de vorige: de combobox is nu een Componentart combobox, er is ook een Ajax timer onderaan en wat achterliggende Javascript die ervoor zorgt dat bij het klikken op submit, een resultaat pagina wordt geopend, waar alle ingevulde data wordt getoond (zie fig 2).
Fig 2: Beginscherm van de ASP.NET applicatie met Ajax, Javascript en Componentart
De testprogramma’s Watir/Firewatir Arnout Logghe
Stageverslag
Pagina 48 van 172
Watir is een open source programma dat toelaat om automatische tests op te stellen. Daarbij wordt gebruik gemaakt van Ruby om deze tests uit te voeren. Watir programma’s worden dan ook opgeslaan onder de extensie .rb. Watir gebruikt browsers op dezelfde manier als gewone gebruikers, namelijk door erin te klikken, tekst te schrijven, enz.….
Google Search Google search werkt goed, zowel in Internet Explorer als in Mozilla Firefox. Om te werken met Mozilla Firefox in plaats van Internet Explorer, moeten we alleen maar bovenaan require firewatir toevoegen en het stukje code van “ie = Watir::IE.new” aanpassen tot “ie = FireWatir::Mozilla Firefox.new “. De code: Require "watir" test_site = "http://www.google.com" ie = Watir::IE.new puts "Beginning of test: Google search." puts " Step 1: go to the test site: " + test_site ie.goto test_site puts " Step 2: enter 'pickaxe' in the search text field." ie.text_field(:name, "q").set "pickaxe" # "q" is the name of the search field puts " Step 3: click the 'Google Search' button." ie.button(:name, "btnG").click # "btnG" is the name of the Search button puts " Expected Result:" puts " A Google page with results should be shown. 'Programming Ruby' should be high on the list." puts " Actual Result:" if ie.text.include? "Programming Ruby" puts " Test Passed. Found the test string: 'Programming Ruby'. Actual Results match Expected Results." else puts " Test Failed! Could not find: 'Programming Ruby'." end puts "End of test: Google search." sleep 100
ASP.NET applicatie zonder Javascript Ook dit werkt zoals het hoort. Het enige probleem, is dat watir/firewatir soms bepaalde objecten niet herkent. Een radiobuttonlist wordt bv niet herkend, en we moeten dan werken met ie.radio. Watir kan ook remote werken, we hoeven hiervoor enkel het pad: “test_site = http://dpitest/stagearnout/” aan te passen. De code: require "watir" test_site = "http://localhost:1349/Default.aspx"
Arnout Logghe
Stageverslag
Pagina 49 van 172
ie = Watir::IE.new puts " Step 1: go to the test site: " + test_site ie.goto test_site puts " Step 2: enter VoorNaam in the voornaam text field." ie.text_field(:name, "txtVoornaam").set "Arnout" # "txtVoorNaam" is the name of the voornaam field puts " Step 3: enter Naam in the voornaam text field." ie.text_field(:name, "txtNaam").set "Logghe" # txtNaam" is the name of the naam field puts " Step 4: select age in the select list." ie.select_list(:name, "DropDownList1").set("19-24") # DropDownList1" is the name of the select list puts " Step 5: select value in the buttonlist." ie.radio(:name, "RadioButtonList1").set ie.radio(:value, "Turkije").set # Turkije" is the value of the value in the buttonlist we have to click puts " Step 6: click the 'Verzenden' button." ie.button(:name, "btnVerzenden").click # "btnVerzenden" is the name of the Verzenden button puts " Going to page Resultaten.aspx:" puts " A Resultpage will be shown with the result of the given data." if ie.text.include? "Arnout" puts " Given input Arnout found" else puts " Test Failed! Given input Arnout not found" end if ie.text.include? "Logghe" puts " Given input Logghe found" else puts " Test Failed! Given input Logghe not found" end if ie.text.include? "19-24" puts " Given input 19-24 found" else puts " Test Failed! Given input 19-24 not found" end if ie.text.include? "Turkije" puts " Given input Turkije found" else puts " Test Failed! Given input Turkije not found" end puts "End of test: ASP.NET form." sleep 8
ASP.NET applicatie met Ajax & Javascript & Componentart Het werken met Javascript en Ajax vormt geen enkel probleem, het werken met Componentart vormt echter wel een probleem. Watir wil namelijk geen Componentart objecten herkennen. De code is eigenlijk redelijk identiek aan de vorige. Het enige dat verandert is het selecteren van de juiste waarde in de buttonlist en het selecteren van de juiste waarde uit de Componentart combo‐ box. De code: puts " Step 5: select value in the buttonlist."
Arnout Logghe
Stageverslag
Pagina 50 van 172
ie.radio(:value, "Turkije").set # Turkije" is the value in the buttonlist we have to click puts " Step 6: select interest." ie.radio(:name, "ComboBox1").set "Computer" # Combobox1" is the name of the dropdownlist we have to click
Voordelen/Nadelen Voordelen • Je kunt gemakkelijk testen in Internet Explorer met Watir en in Mozilla Firefox met Firewatir • Je kunt bijna alle tests uitvoeren: van het testen van Google tot het testen van applicaties die Javascript en Ajax gebruiken. • De code van de tests zit redelijk gemakkelijk in elkaar en is goed begrijpbaar. • E ris een sterke online community en er zijn ook veel voorbeelden van gebruikte code. Nadelen • We kunnen geen code laten genereren met recording, we moeten alles zelf intypen. • Watir kan geen Componentart objecten herkennen.
Spysmith Spysmith is een simpel en krachtig programma voor het testen van websites, maar ook voor het testen van een Windows gebruikersinterface en webgebaseerde toepassingen. Spysmith draait in de achtergrond en kan opgeroepen worden door een bepaalde toetsencombinatie.
Google Search Blijkbaar kunnen we geen externe websites testen, alleen maar eigen gemaakte webpagina’s. We kunnen met Spysmith wel HTML‐elementen testen, maar alleen als we gebruik maken van Internet Explorer en niet binnen Mozilla Firefox. Ook kunnen we testen op Windows objecten. Wanneer we een object of element testen, krijgen we wel alleen maar basis informatie. We kunnen concluderen dat Spysmith niet aan de vereisten voldoet die verwacht worden van een testprogramma. Het is dan ook zinloos om Spysmith verder te testen.
Imacros 6.0.4.1 Imacros is in feite een extensie voor zowel Mozilla Firefox als Internet Explorer. Imacros zorgt voor automatische testing van de browser door middel van recording. De macro’s van Imacros kunnen ook gecombineerd worden met Javascript code. De macro’s worden opgeslaan onder de extensie .iim, en worden standaard met de Imacros browser geopend.
Google Search Arnout Logghe
Stageverslag
Pagina 51 van 172
Google search werkt zeer goed. Wat handig is, is dat we hier bepaalde acties kunnen recorden en opslaan als een macro. We kunnen dan deze macro’s op gelijk welk tijdstip zowel in Internet Explorer als Mozilla Firefox terug afspelen. De gegenereerde code die we krijgen, wanneer we zoeken op ‘pickaxe’ in Google, ziet er als volgt uit: VERSION BUILD=6000502 TAB T=1 TAB CLOSEALLOTHERS URL GOTO=http://www.google.com/ SIZE X=859 Y=661 TAG POS=1 TYPE=INPUT:TEXT FORM=NAME:f ATTR=NAME:q CONTENT=pickaxe TAG POS=1 TYPE=INPUT:SUBMIT FORM=NAME:f ATTR=NAME:btnG
ASP.NET applicatie zonder Javascript Ook hier kunnen we uiteraard via recording weer code laten genereren. Wanneer we dat doen, krijgen we volgende code: VERSION BUILD=6020130 RECORDER=FX TAB T=1 URL GOTO=http://localhost:1349/Default.aspx TAG POS=1 TYPE=INPUT:TEXT FORM=NAME:form1 ATTR=ID:txtVoornaam CONTENT=ArnoutE TAG POS=1 TYPE=INPUT:TEXT FORM=NAME:form1 ATTR=ID:txtNaam CONTENT=Logghe TAG POS=1 TYPE=TD ATTR=TXT:Turkije TAG POS=1 TYPE=SELECT FORM=NAME:form1 ATTR=ID:DropDownList1 CONTENT=$19-24 TAG POS=1 TYPE=INPUT:RADIO FORM=NAME:form1 ATTR=ID:RadioButtonList1_1 TAG POS=1 TYPE=INPUT:SUBMIT FORM=NAME:form1 ATTR=ID:btnVerzenden&&VALUE:Verzenden TAG POS=1 TYPE=INPUT:TEXT FORM=NAME:form1 ATTR=ID:txtVoornaam CONTENT=ArnoutE
Om remote te werken in plaats van lokaal, moeten we gewoon de URL goto aanpassen.
ASP.NET applicatie met Javascript & Componentart Wanneer we via recording de code laten genereren, krijgen we volgend resultaat: VERSION BUILD=6020130 RECORDER=FX TAB T=1 TAB CLOSEALLOTHERS URL GOTO=http://localhost:1882/Default.aspx TAG POS=1 TYPE=TD ATTR=TXT: <SP><SP><SP><SP><SP><SP><SP><SP> <SP><SP><SP><SP><SP><SP><S P><SP><SP><SP><SP><SP> <SP><SP><SP><SP><SP><SP><SP><SP><SP><SP><SP><SP>C uba <SP><SP><SP><SP><SP><SP><SP><SP><SP><SP><SP><SP> <SP><SP><SP><SP> <SP><SP><SP><SP><SP><SP><SP><SP>Turkije <SP><SP><SP><SP><SP><SP><SP><SP> <SP><SP><SP><SP> <SP><SP><SP><SP><SP><SP><SP><SP><SP><SP><SP><SP>Caraibe n <SP><SP><SP><SP><SP><SP><SP><SP><SP><SP><SP><SP> <SP><SP><SP><SP><S P><SP><SP><SP><SP><SP><SP><SP>Singapore <SP><SP><SP><SP><SP><SP><SP><SP> TAG POS=1 TYPE=SELECT FORM=NAME:form1 ATTR=ID:DropDownList1 CONTENT=$25-30
Arnout Logghe
Stageverslag
Pagina 52 van 172
TAG POS=1 TYPE=INPUT:RADIO FORM=NAME:form1 ATTR=ID:rdTurkije TAG POS=1 TYPE=INPUT:SUBMIT FORM=NAME:form1 ATTR=ID:Submit1&&VALUE:submit
Bij de uitvoering ervan zien we dat alles zonder problemen verloopt, behalve de Componentart combobox. Blijkbaar herkent Imacros geen Componentart objecten.
Voordelen/Nadelen Voordelen: • • • •
Imacros kan heel gemakkelijk gebruikt worden door zowel Internet Explorer als Mozilla Fire‐ fox, zonder dat we code moeten aanpassen. De macro’s kunnen automatisch gegenereerd worden met recording. We kunnen macro’s en Javascript combineren. Ajax werkt ook zonder problemen.
Nadelen: • •
Zelf een macro opstellen door het intypen van code of een macro aanpassen is niet evident, doordat de code soms moeilijk te begrijpen is. Imacros herkent geen Componentart objecten.
TestComplete TestComplete is een programma voor het automatisch testen van software development projecten. Daarnaast kan men ook Windows onderdelen, Java, browsers, enz… testen. TestComplete werkt een beetje zoals de andere programma’s in het .NET framework. Tests uivoeren doen we op d volgende manier: eerst maken we een project aan, daarna nieuwe scripts en tenslotte plaatsen we de testcode in deze scripts.
Google Search We hebben in TestComplete de mogelijkheid om code in een script te genereren met recording. We gaan naar ons script, klikken op record en typen in Google dan ‘pickaxe’ in. Nu is er een nieuwe function Test1 aangemaakt in ons script, met volgende code: function Test1() { var w1; w1 = Sys["Process"]("Mozilla Firefox")["Page"]("http://www.google.be/Mozilla Firefox?client=Mozilla Firefoxa&rls=org.mozilla:en-US:official"); w1["Form"]("f")["Table"]("frame")["Cell"](2, 0)["Table"](0)["Cell"](0, 1)["Table"](0)["Cell"](0, 1)["Table"](1)["Cell"](0, 0)["Textbox"]("sf")["Keys"]("pickaxe[Enter]"); //Please wait until download completes: "http://www.google.be/search?client=Mozilla Firefox-a&rls=org.mozilla%3AenUS%3Aofficial&channel=s&hl=nl&q=pickaxe&meta=&btnG=Google+zoeken" w1["Wait"]();
Arnout Logghe
Stageverslag
Pagina 53 van 172
}
ASP.NET applicatie zonder Javascript We kunnen in TestComplete alle objecten gebruiken die op dat moment op onze pc lopen. Om een project te gebruiken, moeten we dus gewoon dat project in Visual Studio opstarten en kunnen het dan in TestComplete aanspreken. We kunnen op die manier ook andere Windows onderdelen aanspreken. Wanneer we op recording klikken, en onze applicatie invullen met de juiste data, genereert TestComplete de volgende code: function Test1() { var p1; var w1; var w2; var w3; p1 = Sys["Process"]("Mozilla Firefox"); p1["Window"]("MozillaUIWindowClass", "Untitled Page - Mozilla fox")["Activate"]();
Blijkbaar maakt TestComplete, net zoals andere .NET programmeertalen variabelen aan voor het opslaan van data. Een nadeel hier, is dat TestComplete ook zijn beginstatus onthoudt. Dus als bv. Windows verkenner openstond, zal TestComplete weer verwachten dat Windows Verkenner openstaat als je het script opnieuw uitvoert. Als dat niet zo is, zal hij het script niet willen uitvoeren.
ASP.NET applicatie met Javascript & Componentart Wanneer we de applicatie uitvoeren, invullen en recorden, krijgen we volgende code:
Bij TestComplete wordt de Componentart combobox wel herkend. Tot nu toe is dit het enige testprogramma waar dat gebeurt.
Voordelen/Nadelen Voordelen • • • • • • •
We kunnen code genereren met recording TestComplete werkt heel goed met .NET framework applicaties We kunnen meerdere functies in een script steken, en die gemakkelijk na elkaar uitvoeren. TestComplete onthoudt zijn beginstatus. TestComplete herkent Ajax en Javascript. Het herkent Componentart objecten. Kan met zowel Internet Explorer als Mozilla Firefox werken. We moeten hiervoor geen code aanpassen.
Nadelen
Arnout Logghe
Stageverslag
Pagina 55 van 172
• •
Hoewel we net zoals in andere programmeertalen met functies en variabelen werken, is de code wel redelijk ingewikkeld en moeilijk aan te passen. TestComplete onthoudt de beginstatus en dus alle programma’s die openstonden toen de code werd gegenereerd.
Selenium Selenium is een testprogramma voor webapplicaties. Selenium loopt direct in een browser en voert de stappen grafisch uit, we kunnen stap voor stap grafisch zien wat Selenium doet. Selenium werkt in Internet Explorer en Mozilla Firefox op Windows, Linux, Macintosh en Safari. Er bestaan 2 manieren om Selenium te gebruiken: • •
Selenium IDE is een grafische manier van werken met Selenium, we kunnen hier ook gaan recorden; Selenium Remote Control is een meer geavanceerde manier van werken, we kunnen hiermee ook Selenium Unittests aanmaken. Wel is er niet de mogelijkheid van recording.
Selenium Remote Control We gaan hier met Selenium RC gaan werken via de command prompt, de instructies worden ingegeven in het Selenium RC interactive venster. Het Selenium RC interactive mode venster wordt opgestart door volgende commando’s: Cd ”Installatiefolder van Selenium RC” java ‐jar Selenium‐server.jar –interactive
Google Search Om de stappen uit de Google search uit te voeren, gebruiken we volgende commando’s: cmd=getNewBrowserSession&1=*Mozilla Firefox&2=http://www.google.com
Dit start een nieuwe sessie met als startpagina Google en als browser Mozilla Firefox. cmd=open&1=http://www.google.com/webhp&sessionId=123178 cmd=type&1=q&2=pickaxe&sessionId=123178 cmd=click&1=btnG&sessionId=260113
Voorgaande commando’s zorgen ervoor dat Google wordt geopend en gezocht naar ‘pickaxe’. Nu moeten we alleen maar nog de browser afsluiten met: cmd=TestComplete&sessionId=123178
ASP.NET applicatie zonder Javascript
Arnout Logghe
Stageverslag
Pagina 56 van 172
Volgende commando’s moeten uitgevoerd worden, om alle stappen uit te voeren in onze applicatie: cmd=getNewBrowserSession&1=*Mozilla Firefox&2= http://localhost:1349/Default.aspx cmd=open&1= http://localhost:1349/ &sessionId=849305 cmd=type&1=txtVoornaam&2=PickAxe&sessionId=849305 cmd=type&1=txtNaam&2=Logghe&sessionId=849305 cmd=type&1=DropDownList1&2=19-24&sessionId=849305 cmd=type&1=RadioButtonList1&2=Turkije&sessionId=849305 cmd=click&1=btnVerzenden&sessionId=849305 cmd=type&1=ComboBox1&2=Computer&sessionId=849305
Dit werkt perfect. Om remote te werken in plaats van local, hoeven we alleen maar de basis‐URL aan te passen in de eerste 2 commando’s.
ASP.NET applicatie met Javascript & Componentart Om de applicatie met Ajax, Componentart en Javascript te testen, moeten volgende commando’s worden uitgevoerd: cmd=getNewBrowserSession&1=*Mozilla Firefox&2= http://localhost:1349/Default.aspx cmd=open&1= http://localhost:1349/Default.aspx&sessionId=849305 cmd=type&1=txtVoornaam&2=PickAxe&sessionId=849305 cmd=type&1=txtNaam&2=Logghe&sessionId=849305 cmd=type&1=DropDownList1&2=19-24&sessionId=849305 cmd=type&1=ComboBox1&2=Computer&sessionId=849305 cmd=click&1=btnVerzenden&sessionId=849305
Selenium wil hier echter de componenartcombobox niet invullen, hoewel hij wel “Got result OK: on session 849305” geeft!
Selenium IDE De IDE is een soort van console waarin we bepaalde Selenium scripts kunnen openen en afspelen. We kunnen ook bepaalde acties in een browser gaan recorden, waarbij dan in de console de verschillende stappen verschijnen die moeten doorlopen worden om deze acties opnieuw uit te voeren.
Google Search We kunnen Selenium IDE openen en via recording instellen dat hij code moet gaan genereren en daarna op ‘pickaxe’ in Google gaan zoeken(zie fig 3).
Arnout Logghe
Stageverslag
Pagina 57 van 172
Fig 3: Selenium IDe venster met commando’s die een google search uitvoeren.
ASP.NET applicatie zonder Javascript Ook voor een applicatie zonder Javascript kunnen we via recording code laten genereren (zie fig 4).
Fig 4: Selenium IDe venster met commando’s zonder Javascript/Ajax/Componentart.
Arnout Logghe
Stageverslag
Pagina 58 van 172
Wanneer we dit script starten, zal alles terug perfect uitgevoerd worden.
ASP.NET applicatie met Javascript & Componentart Via recording kan men testcode genereren voor de applicatie met Ajax, Javascript en Componentart (zie fig 5).
Fig 5: Selenium IDe venster met commando’s met Javascript/ajax/Componentart.
Wanneer we dit script starten, zien we dat hij de Componentart textbox niet wilt invullen. De rest voert Selenium wel goed uit.
Voordelen/Nadelen Voordelen • • • • • • •
We kunnen code genereren met recording. We hebben verschillende manieren van werken(IDE en Remote Control) We kunnen Selenium Unittests aanmaken. We kunnen meerdere testscripts na elkaar uitvoeren. Selenium herkent Ajax en Javascript. Code is makkelijk leesbaar en aanpasbaar Kan met zowel Internet Explorer als Mozilla Firefox werken, we moeten hiervoor geen code aanpassen.
Nadelen
Arnout Logghe
Stageverslag
Pagina 59 van 172
•
We kunnen niet testen op Componentart objecten(hoewel er op sommige fora op Internet beweerd wordt van wel)
Conclusie Ik denk dat we hier kunnen besluiten dat het beste testprogramma toch Selenium is, gevolgd door TestComplete. Met Selenium kunnen we op verschillende manieren werken, we kunnen gemakkelijk verschillende browsers gebruiken, we kunnen ook Unittests aanmaken en de code is goed leesbaar. Selenium heeft dus nauwelijks nadelen, alleen de Componentart objecten worden niet herkend.
Arnout Logghe
Stageverslag
Pagina 60 van 172
6.3.2. Tweede subproject: Uitdiepen van Selenium Selenium kan nu als testprogramma verder worden uitgediept. Hiervoor worden de volgende zaken behandeld: • • •
hoe kunnen we Selenium draaien op de C# runner; hoe kunnen we Selenium IDE laten lopen vanuit de cmd; hoe kunnen we Selenium laten werken met Componentart?
Hoe kunnen we Selenium draaien op de C# runner? Selenium runnen op de C# runner doen we door gebruik te maken van Selenium Unittests. Om Selenium Unittests uit te voeren, moeten we 2 zaken doen: • •
aanmaken van Selenium Unittests in Visual studio; opstarten van Selenium Remote Control in de cmd.
Aanmaken van Selenium Unittests in Visual studio Om Selenium Unittests aan te maken in Visual Studio, gaan we eerst een nieuw Testproject aanma‐ ken. Dit testproject bevat nu al enkele Unittest files. Wij gaan nu één van deze files omzetten naar een Selenium Unittest file. Om een Unittest file om te zetten naar een Selenium Unittest, gaan we eerst een referentie naar Selenium moeten toevoegen aan ons project. In de references, moeten we de dll’s uit de installatie‐ map van Selenium Remote Control aan het project toevoegen. Daarna gaan we bovenaan in onze Unittest.cs file “using Selenium;” plaatsen en de code aanpassen naar Selenium Unittest code. Deze code ziet er als volgt uit: namespace SeleniumTests { [TestFixture] public class NewTest { private ISelenium Selenium; private StringBuilder verificationErrors; [SetUp] public void SetupTest() { Selenium = new DefaultSelenium("localhost", 4444, "*Mozilla Firefox", "http://localhost:4444"); Selenium.Start(); verificationErrors = new StringBuilder(); } [TearDown]
Arnout Logghe
Stageverslag
Pagina 61 van 172
public void TeardownTest() { try { Selenium.Stop(); } catch (Exception) { // Ignore errors if unable to close the browser } Assert.AreEqual("", verificationErrors.ToString()); } [Test] public void TheNewTest() { Selenium.Open("http://www.google.com"); }
Opstarten van Selenium Remote Control in de cmd Nadat er een Selenium Unittest file is aangemaakt, moeten we de Selenium Remote Control opstarten voordat we de test kunnen uitvoeren. Hiervoor openen we de cmd en typen we Cd “Installatie map van Mozilla Firefox”. We moeten vanuit deze map Selenium Remote Control gaan opstarten, omdat Mozilla Firefox de default browser is en we de tests met Mozilla Firefox willen uitvoeren. Nu gaan we het bestand Selenium‐server.jar opstarten, zodat Selenium Remote Control gestart wordt. We doen dit met het commando: java -jar “installatie map van Selenium Remote Control”\ Seleniumserver.jar
In ons geval wordt dat: java ‐jar C:\Selenium‐remote‐control‐1.0‐beta‐1‐dist\Selenium‐remote‐control‐1.0‐beta‐1\Selenium‐ server‐1.0‐beta‐1\Selenium‐server.jar. De Selenium server is nu opgestart (zie fig 6).
Arnout Logghe
Stageverslag
Pagina 62 van 172
Fig 6: Opgestarte Java console.
Nu kunnen de Selenium Unittests uitgevoerd worden.
Hoe kunnen we Selenium IDE laten lopen vanuit de cmd? Dit kan door een Selenium TestSuite aan te maken. Selenium TestSuite moeten we laten lopen via Selenium Remote Control. In deze Selenium TestSuite gaan we dan verwijzingen plaatsen naar Selenium IDE tests. Hiervoor zijn volgende stappen nodig: • • •
aanmaken van de TestSuite; opslaan van Selenium IDE test als HTML; laten lopen van de TestSuite in Selenium Remote Control.
Aanmaken van de TestSuite De TestSuite is eigenlijk gewoon een .HTML‐bestand dat we in de installatiemap van Selenium core gaan zetten. Dit bestand bevat verwijzingen naar de testprogramma’s die moeten worden uitgevoerd en ziet er als volgt uit: <meta content="text/HTML; charset=ISO-8859-1" http-equiv="content-type"> Test Suite
Hier hebben we dus één testprogramma dat wordt uitgevoerd, namelijk “Vanuitdecmd.HTML”.
Opslaan van Selenium IDE test als HTML Wanneer we een Selenium IDE test hebben opgesteld, moet die worden opgeslaan in de Selenium core map op onze pc. Daarna moeten we de extensie veranderen naar .HTML. In dit .HTML‐bestand gaan de stappen nu als tekst in een tabel staan. Bekijk maar onderstaand voorbeeld van een dergelijk .HTML‐bestand: <meta http-equiv="Content-Type" content="text/HTML; charset=UTF-8"> Vanuitdecmd
Vanuitdecmd
open
http://localhost:1188/Default.aspx
type
txtVoornaam
Arnout
TestSuite uitvoeren in Selenium Remote Control Nu openen we de cmd, en met een aantal commando’s worden de Selenium Remote Control en TestSuite opgestart. Eerst typen we “Cd C:\Selenium‐remote‐control‐0.9.2‐dist\Selenium‐remote‐control‐0.9.2\Selenium‐ server‐0.9.2” om naar de map van Selenium Remote Control te gaan. Daarna typen we “java ‐jar Selenium‐server.jar ‐HTMLSuite "*Mozilla Firefox" "http://localhost:8080" "/path/to/folder/TestSuccessfulSuite.HTML" "/path/to/folder/result.HTML"” om de TestSuite zelf op te starten. De Selenium Remote Control wordt opgestart als een HTML‐suite, en we gaan de TestSuccessfulSuite in deze HTML‐suite opstarten op de localhost (zie fig 7).
Arnout Logghe
Stageverslag
Pagina 64 van 172
Fig 7: Opstarten van de Selenium Remote Control en de TestSuite in de cmd.
Hoe kunnen we Selenium laten werken met Componentart? Selenium wil nog altijd niet met Componentart objecten werken. Mijn opdracht is nu verder uit te zoeken of dit echt niet mogelijk is. Wanneer we Componentart objecten proberen uit te testen met Selenium, merken we dat bij sommige zaken zoals comboboxen wel lukt, maar bij andere zoals Componentart menu’s dan weer helemaal niet. We kunnen nu op 2 manieren proberen om toch Componentart menu’s met Selenium te laten werken: • •
door gebruik te maken van Searchenginefriendly; door middel van User Extensions.
Door middel van Searchenginefriendly Via Internet kwam ik te weten dat men best RenderSearchEngineStamp en RenderSearchEngineStruc‐ ture op true zet zodat er kan gezocht worden op de inhoud van een bepaald Componentart object. Met RenderSearchEngineStamp en RenderSearchEngineStructure wordt een index en een schema toegevoegd zodat zoekfuncties mogelijk worden. We gaan een eenvoudig project maken met een Componentart menu en een label. De inhoud van het Componentart menu is hard gecodeerd in de Code behind. Wanneer we op een menu‐item klikken, wordt de tekst in ons label geplaatst. Wanneer we nu met Selenium gaan testen of we op een menu‐item hebben geklikt, zien we dat hij het klikken op het menu‐item nu wel bij de andere commando’s plaatst in Selenium IDE. Wanneer we dit uitvoeren, krijgen we echter een foutmelding dat hij het menu‐item niet vindt (zie fig 8). Dit werkt dus niet.
Arnout Logghe
Stageverslag
Pagina 65 van 172
Fig 8: Uittesten van een Componentart Menu in Selenium IDE wanneer RenderSearchEngineStamp en RenderSearchEngine‐ Structure op true staan.
Door middel van User Extensions Na lang zoeken heb ik op volgende website een User Extension gevonden: http://www.nabble.com/Componentart‐Context‐Menu‐for‐tree‐view‐tt15829692.HTML Deze User Extension zorgt er blijkbaar voor dat Selenium wel kan testen op een Component Art Tree View context menu. We gaan de extension installeren en daarna op de website van Componentart het Node context menu uittesten met Selenium. We klikken in het Node context menu, op het menu‐ item Calender en daarna op Today (zie fig 9).
Arnout Logghe
Stageverslag
Pagina 66 van 172
Fig 9: Uittesten van een Componentart Menu in Selenium IDE door gebruik te maken van een user‐extension.
Selenium geeft hierbij geen enkele foutmelding; maar het wordt ook niet uitgevoerd want Selenium doet gewoon niets.
Conclusie We hebben lang op Internet gezocht naar een oplossing om Selenium te laten werken met Compo‐ nentart. We hebben een aantal mogelijkheden gevonden en ze uitgetest, maar een echt goede oplossing vonden we niet. We kunnen dan ook besluiten dat het niet mogelijk is om op een goede manier alle Componentart objecten uit te testen met Selenium. Misschien wordt dit in een volgende versie van Componentart of Selenium wel ondersteund.
6.3.3. Derde subproject: Uittesten van een bestaande applicatie met Selenium De volgende stap is het uittesten van een bestaand project met Selenium. Het project dat we gaan uittesten is Synergebuild.
Bespreking project Synergebuild Synergebuild is een project van Deceuninck, dat gebruikt wordt om de voorraad te beheren. Synergebuild stelt een gebruiker in staat om de beschikbare voorraad op te vragen, maar ook om bestellingen te plaatsen, claims te bekijken, technische gegevens van een bepaald product te bekijken, enz.…
Arnout Logghe
Stageverslag
Pagina 67 van 172
De pagina waarin wij geïnteresseerd zijn, en waarop we Selenium tests gaan uitvoeren, is de pagina voor het toevoegen van een product aan een claim entry. We kunnen hiernaartoe gaan, door op de startpagina in het Milonic menu op customer care – claim entry te klikken. Zo komen we terecht op een pagina, waar we een nieuwe claim entry moeten aanmaken. Wanneer dat is gebeurd, moeten we op de volgende de pagina specificeren over welk product of producten deze claim entry gaat (zie fig 9). Op deze pagina kunnen we een product invullen aan de hand van zijn eigenschappen. We kunnen . Wanneer we dat doen, wordt echter ook een deel van het product invullen en dan klikken op een soort van catalogus geopend en wordt het product opgezocht in de lijst van producten aan de hand van de opgegeven data, deze catalogus wordt de ecatalog genoemd. We kunnen ook een product toevoegen, dat doen we door te klikken op . Voor dit product, moeten we dan uiteraard ook de juiste data invullen, of laten genereren door de ecatalog. We kunnen een lijst van producten ook saven door te klikken op , nu worden de ingegeven producten toegevoegd aan de lijst van producten van deze claim. Deze lijst kunnen we onderaan zien.
Fig 9: Pagina in Synergebuild, waarin we kunnen specifieren over welk(e) product(en) de claim entry gaat.
Scenario’s
Arnout Logghe
Stageverslag
Pagina 68 van 172
Er zijn 4 scenario’s die we gaan testen met Selenium. We gaan deze scenario’s omzetten in comman‐ do’s of in Selenium RC Unittests, en ze dan laten uitvoeren op Synergebuild:
Scenario1 Bij scenario 1 vullen we 1 product in (zie tabel 1) Tabel 1: Voorstelling van de gegevens van het eerste uit te testen scenario.
Product1 Productnummer 104 Colornummer 1 Type 0 Width 0 Length 6 Quantity 1 Stillage number 3001/3/1556/-/6 Production code Select units of measurements Delivered in Wanneer we nu op save klikken, wordt het product toegevoegd aan de lijst en blijft de inhoud van het product staan.
Scenario2 Bij scenario 2 vullen we 2 producten in (zie tabel 2) Tabel 2: Voorstelling van de gegevens van het tweede uit te testen scenario.
Product1 Product2 Productnummer 104 104 Colornummer 1 1 Type 0 0 Width 0 0 Length 6 6 Quantity 1 20 Stillage number 3001/3/1556/-/6 3001/3/1556/-/6 Production code Select units of measurements Select units of measurements Delivered in We klikken op Save. Nu klikken we op de switch to e‐catalog van het tweede product. Normaal moet het tweede product dan leeg worden, het eerste blijven staan en moet in de lijst onderaan, alleen het eerste product staan. Wanneer we op e‐catalog klikken, wordt het tweede product namelijk verwijderd.
Scenario3 Bij scenario 3 vullen we 3 producten in (zie tabel 3)
Arnout Logghe
Stageverslag
Pagina 69 van 172
Tabel 3: Voorstelling van de gegevens van het derde uit te testen scenario.
Productnummer Colornummer Type Width Length Quantity Stillage number Production code Delivered in
Product1
Product2 104 1 0 0 6 1
3001/3/1556/-/6 Select units of measurements
104 1 0 0 6 20 3001/3/1556/-/6 Select units of measurements
Product3 104 1 0 0 6 40 3001/3/1556/-/6 Select units of measurements
We klikken op Save. Nu klikken we op e‐catalog van het tweede product. Het tweede product wordt nu verwijderd en de andere 2 producten blijven staan.
Scenario4 Bij scenario 4 vullen we 2 producten in (zie tabel 4) Tabel 4: Voorstelling van de gegevens van het vierde uit te testen scenario.
Productnummer Colornummer Type Width Length Quantity Stillage number Production code Delivered in
Product1
Product2 104 1 0 0 6 1
3001/3/1556/-/6 Select units of measurements
104 1 0 0 6 20 3001/3/1556/-/6 Select units of measurements
We klikken op de knop Save. Nu verwijderen we de hoeveelheid uit de 2 producten, we klikken op de switch e‐catalog knop van het eerste product. In het optimale geval, krijgen we nu een foutbood‐ schap voor het 1ste en 2de product en blijven de gegevens van deze producten staan. Wat er echter momenteel gebeurt, is dat het tweede product een foutboodschap geeft, en het 1ste product weg is.
Selenium IDE Eerst gaan we proberen om een testscenario met Selenium IDE op te stellen. Wanneer we in loggen met Selenium IDE commando’s, zien we dat het werkt. We kunnen echter niet zomaar op het menu klikken om naar claim entry te gaan. Het menu is een Milonic menu, en Selenium IDE kan niet werken met Milonic menu’s.
Arnout Logghe
Stageverslag
Pagina 70 van 172
Een oplossing voor dit probleem, bestaat erin dat we eerst zonder Selenium IDE claim entry openen, en dan het pad in de adresbalk bovenaan kopiëren en in het open commando van Selenium IDE zetten. Op die manier zal Selenium IDE direct naar de pagina Claim Entry gaan, in plaats van eerst in te loggen. Probleem: Het pad in de adresbalk heeft een variabel deel. Alles na het vraagteken is variabel en is gekoppeld aan de sessie van de ingelogde gebruiker. Als de sessie eindigt, verandert dit variabel deel ook en moeten we opnieuw inloggen en het pad kopiëren. Î Dit is geen optimale oplossing, we gaan proberen een betere oplossing uit te werken door gebruik te maken van Selenium Unittests.
Selenium Unittests Om Selenium Unittests te gebruiken, moet er weer een nieuw Testproject worden aangemaakt in Visual Studio. De Unittest files in dit testproject, gaan we nu weer aanpassen en omvormen naar Selenium testfiles. Hiervoor moeten we zoals altijd eerst alle referenties naar Selenium Remote Control toevoegen en daarna de code van de Unittests zelf aanpassen naar Selenium Unittest code.
Probleem met certificaat wanneer Synergebuild wordt opgestart In de setuptest proberen we Selenium op te starten met volgende code: [SetUp] public void SetupTest() { Selenium = new DefaultSelenium("localhost", 4444, "*Mozilla Firefox", "http://testiis.Synergebuild.com/FrontOffice/Interactive/CustomerCare/Claim EntryWelcome.aspx?sid=006374cab62749ce9ced66abbb45df3c&sDINR=2&sCUNR=30001&sEMNR=98&L NCD=E&c=1%7c41%7cycpEJ2Vlf8kOnnecNNDSJZ0sPFs%3d"); Selenium.Start(); verificationErrors = new StringBuilder(); }
Nu zouden we een nieuwe Selenium Mozilla Firefox testpagina moeten krijgen, met claimentry ingesteld als de default webpagina. Dit lukt echter niet, we krijgen een foutmelding (zie fig 10).
Fig 10: Foutmelding dat de website van Synergebuild niet kan geproxyd worden.
Deze foutmelding wordt veroorzaakt doordat mijn computer geen geldig certificaat heeft om Synergebuild aan te spreken. Synergebuild wordt aangeroepen via HTTPS en kan dus alleen via een geldig certificaat worden aangesproken.
Arnout Logghe
Stageverslag
Pagina 71 van 172
HTTPS is immers een uitbreiding op het HTTP‐protocol met als doel een veilige uitwisseling van gegevens. Bij gebruik van HTTPS wordt de data versleuteld. We hebben dan geprobeerd deze foutmelding op volgende manieren op te lossen: • • •
het bypassen van de proxy door de serverProxy uit te schakelen; door zelf een certificaat toe te voegen; door Synergebuild beschikbaar te stellen via http:/.
Proxy uitschakelen Eerst gaan we de foutmelding op een simpele manier proberen op te lossen, namelijk door de proxyserver uit te schakelen. Dit kan gebeuren door in het browservenster naar Internet Options te gaan, dan naar het tabblad connections en daar we op de knop Lan Settings te klikken. Nu komen in het venster met de eigenschappen van de LAN Settings van onze computer, hier vinken we de checkbox “Use a proxy server for your LAN” uit (zie fig 11).
Fig 11: Uitschakelen van de Proxy in de LAN settings.
Dit lost het probleem echter niet op.
Zelf certificaat toevoegen Vermits de foutmelding wordt veroorzaakt doordat we geen geldig certificaat hebben, gaan we proberen zelf een certificaat toe te kennen aan onze computer. Wanneer we de website in Internet Explorer openen, kunnen we bovenaan op certificate klikken en dan op add certificate. We krijgen een waarschuwing (zie fig 12) ivm de veiligheid van het certificaat.
Arnout Logghe
Stageverslag
Pagina 72 van 172
Fig 12: Veiligheidswaarschuwing die we krijgen wanneer we proberen een niet gevalideerd certificaat toe te voegen.
We gaan dit certificaat uiteraard installeren, maar ook hiermee is het probleem niet opgelost.
Synergebuild beschikbaar stellen op http: Er bleef dus geen andere mogelijkheid dan Synergebuild beschikbaar te stellen op een http website, zodat er geen certificaat meer nodig is. We kunnen nu via deze http website proberen onze Selenium tests uit te voeren. Dit werkt uiteraard wel.
Schrijven en uittesten testscenario’s Nu kunnen we onze testscenario’s zelf gaan schrijven en uittesten. Ook moeten volgende zaken behandeld worden om een optimale oplossing te bekomen • •
• • •
we moeten het pad achter het ?, dat aangemaakt wordt per gebruikerssessie, dynamisch kunnen genereren. Wanneer claimentry geopend wordt, moet dit pad worden toegevoegd; we moeten gebruik maken van de id van elementen, in plaats van het volledig pad van het element op te geven.(bv van de color textbox wordt dat dan “colorTextBox” in plaats van “_ctl0_mainContentPlaceHolder_claimEditProductsControl_productRepeater__ctl3_colorText Box”). Wanneer we dan de id van een element intypen, moet hij automatisch herkennen, welk element we bedoelen. We zullen dus een lijst van de elementen moeten opstellen; pagetoload moet werken met variabelen; op het einde van ieder testprogramma moeten we Assert values plaatsen, die kijken of de uitkomst van test de juiste is; We moeten ernaar streven alles zo eenvoudig en gebruiksvriendelijk mogelijk te maken.
Pad achter het vraagteken dynamisch genereren
Arnout Logghe
Stageverslag
Pagina 73 van 172
Het pad achter het vraagteken dynamisch genereren, kan heel eenvoudig opgelost worden: Ik maak een nieuwe klasse GetPad() aan, deze klasse gaat de Synergebuild startpagina opstarten, inloggen en het pad achter het ? opslaan in een string variabele sessionpad. public void GetPad(){ Selenium.OpenWindow("http://dpitest/SynergebuildTroubleshooting/FrontOffice/Logon .aspx?", "Arnout"); System.Threading.Thread.Sleep(5000); Selenium.SelectWindow("Arnout"); Selenium.Type("divisionTextBox", "2"); Selenium.Type("customerNumberTextBox", "30001"); Selenium.Type("employeeNumberTextbox", "98"); Selenium.Type("passwordTextbox", "Ramasoft"); Selenium.Click("submitImageButton"); Selenium.WaitForPageToLoad(TestSettings.DefaultPageTimeout); Selenium.SelectWindow("Arnout"); Selenium.WindowMaximize(); string[] seperator = new string[1]; seperator[0] = "?"; string[] arnout = Selenium.GetLocation().Split(seperator, StringSplitOptions.None); sessionpad = arnout[1].ToString(); Selenium.Close(); }
Opmerking: We gebruiken in deze code openwindow() ipv open(). De reden hiervoor is een inline frame in de startpagina. Dit frame is zodanig geprogrammeerd dat wanneer we proberen om de startpagina in het inline frame te openen, de pagina in een nieuw venster wordt geopend. Hierdoor ontbreekt elke verwijzing naar het venster waar de startpagina in geopend is. Om dat op te lossen gebruiken we openwindow(). We kunnen hier namelijk een id gaan meegegeven aan ons window en op die manier toch een verwijzing bijhouden. In onze testscenario’s, kunnen we dan de pagina claimentry openen, door het sessionpad achter het ? te zetten: Selenium.Open("FrontOffice/Interactive/CustomerCare/ClaimEntryWelcome.aspx?" + sessionpad);
Gebruik maken van de id van elementen Wanneer we een element oproepen om er iets in te typen, zullen we dat op de volgende manier doen: Selenium.Type(“_ctl0_mainContentPlaceHolder_claimEditProductsControl_productRep eater__ctl3_colorTextBox, "104");
In een optimale situatie, zouden we in plaats van een element op te roepen als
Arnout Logghe
Stageverslag
Pagina 74 van 172
“_ctl0_mainContentPlaceHolder_claimEditProductsControl_productRepeater__ctl3_colorTextBox”, het gewoon moeten kunnen oproepen als “colorTextBox”. Het gevolg hiervan is dat de code veel beter leesbaar en begrijpbaar is, ook voor buitenstaanders. Ik heb hiervoor 3 oplossingen uitgedokterd: • • •
de code die verwijst naar de header, footer en main, in een method laten genereren; een element gaan zoeken in het formulier door het doorlopen van de elementen en het gaan zoeken van een element in de HTMLsource; een combinatie van de vorige 2.
De code die verwijst naar de header, footer en main, in een method laten genereren Wanneer we de naam van een element gaan bekijken, zien we dat ieder element 1 van volgende strings bevat al naar gelang of het element zich in de footer, header of main bevindt: • • •
We zouden deze strings uit onze elementnamen kunnen laten wanneer we ze oproepen in de code, en ze automatisch laten genereren door de juiste method op te roepen. Voor de methods die zorgen voor het toevoegen van deze strings, maken we een nieuwe klasse TestHelper. Deze klasse bevat volgende methods die ervoor gaan zorgen dat de correcte string wordt toegevoegd: public return } public return } public return }
Het aanspreken van een element kan dan op de volgende manier gebeuren: Selenium.Type(TestHelper.GetFullIdMain("claimEditProductsControl_productRepeate r__ctl1_productNumberTextBox"), "104");
De elementen in het formulier doorlopen en een element in de HTMLsource zoeken Een andere manier is de elementen van ons formulier te doorlopen totdat we een element krijgen dat de meegegeven id bevat.
Arnout Logghe
Stageverslag
Pagina 75 van 172
Bv: In het geval van de colortextbox, zouden we de elementen van ons formulier kunnen doorlopen totdat we een element vinden die colortextbox bevat. Dat zal dan het element “_ctl0_mainContentPlaceHolder_claimEditProductsControl_productRepeater__ctl3_colorTextBox” zijn. We hebben hiervoor een method GetElement aangemaakt: public string GetElement(string element, int nr){ //1ste deel if (nr != 0){ element = nr.ToString() + "_" + element; }else{ element = "_" + element; } string fullname = ""; for (int i = 0; i < Velden.Length; i++){ if (Velden[i].Contains(element)){ fullname = Velden[i]; } } //2de deel: Als het element niet gevonden wordt if (fullname == ""){ int index = Selenium.GetHTMLSource().LastIndexOf(element); string character = ""; do{ index--; character = Selenium.GetHTMLSource()[index].ToString(); } while (character != "="); index++; do{ index++; fullname += Selenium.GetHTMLSource()[index].ToString(); } while (Selenium.GetHTMLSource()[index].ToString() != "\""); fullname = fullname.Replace("\"", ""); fullname.Remove(fullname.Length - 2, 2); } return fullname; }
In deze method, gaan we het id van een element én een nummer meegegeven. Waarom een nummer? Soms kan het gebeuren dat een element meerdere keren voorkomt. Bv zoals colortextbox, dit komt voor bij ieder product dat we toevoegen. Die colortextboxen zullen echter een verschillend nummer hebben om ze te distantiëren van elkaar. Bv: de colortextbox van product 1 geeft als naam: claimE‐ ditProductsControl_productRepeater__ctl1_colorTextBox, de colortextbox van product2 eindigt dan op __ctl3_colorTextBox. Als het nummer 0 is, betekent dat het hier om een element gaat dat niet meerdere keren kan voorkomen. Zoals je kunt zien, bestaat deze method in feite uit 2 delen: •
in het eerste deel gaan we kijken of het formulier velden bevat waar het id van ons element in voor komt. Onder velden wordt verstaan: textboxen, comboboxen, buttons, enz.…. Wan‐
Arnout Logghe
Stageverslag
Pagina 76 van 172
•
neer we een veld gevonden hebben die hieraan voldoet, geven we de naam van dat veld te‐ rug; soms echter, kan het zijn dat het meegegeven element geen veld is maar iets anders. De buttons om een nieuw product toe te voegen, te saven, enz.… zijn namelijk geen buttons maar images en images worden niet als velden aanzien. Hiervoor heb ik het tweede deel ge‐ programmeerd. Hier ga ik voor alle elementen zonder overeenkomstig veld, de HTMLSource‐ code doorlopen. Deze bevat alle code uit de form pagina, ook de namen van onze images. Wanneer het meegegeven id erin voorkomt, kunnen we de volledige naam waar die id in voorkomt eruit halen.
We kunnen in onze testprogramma’s elementen op de volgende manieren aanspreken: Selenium.Click(GetElement("footerNextImage",0));
Selenium.Type(GetElement("colorTextBox",1), "1");
Dit is een zeer goede manier van werken, omdat de code nu heel eenvoudig wordt. Deze werkwijze heeft echter enkele nadelen. Wanneer we moeten zoeken naar een element dat geen veld is, duurt het enorm lang om de naam van dat element te vinden. De reden hiervoor is dat we de hele HTML‐ Sourcecode moeten doorlopen. Daarom werd een betere oplossing uitgedacht: Een combinatie van de vorige 2 oplossingen Deze oplossing gaat het beste van de 1ste oplossing(het vlug terugvinden van de naam van een element) gaan combineren met het beste van de 2de oplossing(het werken met een id in plaats van met de volledige naam). Het komt er in feite op neer, dat we voor alle velden de 2de oplossing gaan gebruiken en dus gaan werken met een id. Daarnaast gaan we voor alle elementen die geen veld zijn, werken met de 1ste oplossing. De code ziet er dan als volgt uit: Selenium.Type(GetElement("quantityTextBox",1), "1"); Selenium.Type(GetElement("productionCodeTextBox",1), "3001/3/1556/-/6"); Selenium.Click(TestHelper.GetFullIdMain("claimEditProductsControl_addLineImageBut ton"));
Waarbij quantitytextbox en productiontextbox velden zijn, en addLineImageButton een element is.
Werken met assert values
Arnout Logghe
Stageverslag
Pagina 77 van 172
We moeten ook gebruik gebruik maken van assert om te controleren of het resultaat wel klopt. Met assert.areEqual kunnen we kijken of 2 waarden gelijk zijn aan elkaar, en dus ook of een textbox een juiste waarde bevat. Om te kijken of de productioncode leeg is, moeten we volgende assert opstellen: Assert.AreEqual(Selenium.GetValue(TestHelper.GetFullIdMain("claimEditProducts Control_productRepeater__ctl1_productionCodeTextBox")), "");
Wanneer de AreEqual true teruggeeft, slaagt onze test. Wanneer dat niet het geval is, faalt onze test.
Voorbeeld Test scenario Het eerste testscenario ziet er dus, in de meest optimale manier, als volgt uit: [Test] public void TestingDefault(){ Selenium.Open("FrontOffice/Interactive/CustomerCare/ClaimEntryWelcome.aspx?" + sessionpad); SlaFieldsOp(); Selenium.Click(TestHelper.GetFullIdFooter("footerNextImage")); Selenium.WaitForPageToLoad(TestSettings.DefaultPageTimeout); SlaFieldsOp(); Selenium.Type(GetElement("nameTextBox", 0), "a"); Selenium.Type(GetElement("phoneTextBox", 0), "a"); Selenium.Type(GetElement("customerReferenceTextBox", 0), "a"); Selenium.Click(TestHelper.GetFullIdFooter("footerNextImageButton")); Selenium.WaitForPageToLoad(TestSettings.DefaultPageTimeout); SlaFieldsOp(); Selenium.SetContext("Scenario1"); Selenium.Type(GetElement("productNumberTextBox", 1), "104"); Selenium.Type(GetElement("colorTextBox", 1), "1"); Selenium.Type(GetElement("typeTextBox", 1), "0"); Selenium.Type(GetElement("widthTextBox", 1), "0"); Selenium.Type(GetElement("lengthTextBox", 1), "6"); Selenium.Type(GetElement("quantityTextBox", 1), "1"); Selenium.Type(GetElement("productionCodeTextBox", 1), "3001/3/1556/-/6"); Selenium.Click(TestHelper.GetFullIdHeader("topSaveImageButton")); Selenium.WaitForPageToLoad(TestSettings.DefaultPageTimeout); Selenium.Click(TestHelper.GetFullIdMain("claimEditProductsControl_productRepeater __ctl1_switchToProductCatalogImageButton")); Selenium.WaitForPageToLoad(TestSettings.DefaultPageTimeout); Assert.AreEqual(Selenium.GetValue(TestHelper.GetFullIdMain("claimEditProducts Control_productRepeater__ctl1_productionCodeTextBox")), ""); Assert.AreEqual(Selenium.GetValue(TestHelper.GetFullIdMain("claimEditProducts Control_productRepeater__ctl1_quantityTextBox")), ""); }
TestSettings.DefaultPageTimeout verwijst naar een method in de class TestHelper, waar we de
Arnout Logghe
Stageverslag
Pagina 78 van 172
standaard timeout bijhouden. Deze staat op 5 sec, wat betekent dat onze applicatie 5 seconden gaat wachten op een reactie van de pagina, wanneer we op 1 van onze images geklikt hebben. SlafieldsOp() haalt de namen van alle velden uit een bepaalde pagina en slaat ze op in de tabel Velden[]. Via assert gaan we in het eerste scenario kijken ofonze productionCode en Quantity leeg is. Wanneer dit het geval is, zal de test slagen. We kunnen de Selenium tests nu uitvoeren nadat we Selenium Remote Control hebben opgestart.
Selenium Unittests uitvoeren met de NUnit console vanuit de cmd We gaan nu ook proberen de Selenium Unittests uit te voeren met de NUnit console. Om deze console te kunnen gebruiken, moeten we eerst NUnit 2.4.6 voor .NET 2.0 downloaden op http://www.nunit.org/index.php?p=consoleCommandLine&r=2.2.10. Wanneer dat gebeurd is, kunnen we in de command prompt de NUnit console opstarten. Eerst moeten we naar de installatie map van NUnit gaan met het cd commando. Daarna kunnen we met NUnit ‐console “Pad en naam van de .csproj file”, alle Unittests van een bepaald project laten lopen. Nu krijgen we een scherm waarin waarin we kunnen zien welke tests falen en waaraan het ligt (zie fig 13).
Fig 13: Opstarten van de NUnit‐ console in de command prompt, en uitvoeren van de Unittests van Synergebuild.
In ons geval zijn alvast alle tests gelukt.
Arnout Logghe
Stageverslag
Pagina 79 van 172
Conclusie Er waren enkele problemen die moesten worden opgelost, vooraleer we bepaalde scenario’s in Synergebuild konden uittesten met Selenium Unittests. Zo was er eerst en vooral een probleem met de beveiliging door een certificaat wanneer we naar Synergebuild probeerden te gaan. Dit hebben werd opgelost door Synergebuild niet meer via HTTPS:// te laten verlopen. Vervolgens was er het probleem met het pad na het vraagteken in de adresbalk van de Synergebuild pagina die we proberen te openen. Dat moest dynamisch gegenereerd worden, omdat het sessiege‐ bonden is. We hebben dat probleem opgelost door een method te schrijven. Verder zijn ook de namen van de elementen in ons formulier te lang, en daardoor werden onze testscenario’s veel te ingewikkeld. Dit hebben we echter opgelost door in onze Unittests naar simpelere namen te verwijzen. In bepaalde methods worden deze dan weer omgezet naar de oorspronkelijke naam van het element. Ook was er de vraag hoe we nu konden bekijken, of het eindresultaat van een Unittest, wel het verwachte eindresultaat was. Een dergelijke controle werd geïmplementeerd met gebruik van de functie assert. Nadat we al deze kwesties hadden afgehandeld, konden we zonder veel problemen, de testscena‐ rio’s met Selenium Unittests uitvoeren.
6.3.4. Subprogramma 4: Synergebuild applicatie laten lopen op de buildserver Deceuninck beschikt over een buildserver voor het uittesten van hun projecten. Een buildserver is een computer waar een programma zoals CruiseControl.NET wordt geinstalleerd; Dit programma werkt samen met een versiebeheersysteem. Hierbij wordt er regelmatig gekeken of er een wijziging is gebeurd in de applicaties. Indien dit het geval is, wordt de applicatie gebuild. Ook kan je een tijdsinterval instellen en zo zelf bepalen hoe vaak het buildproces automatisch wordt doorlopen. Op deze manier kunnen de programmeurs van Deceuninck heel vlug zien welke projecten wel of niet gecompileerd worden en welke aanpassingen problemen opleveren. Aanpassingen doorvoeren naar de buildserver, gebeurt door een check in te doen van deze aanpassingen naar SourceSafe. De buildserver gaat namelijk daar steeds gaan kijken naar de laatste versie van het project. Meestal laat je enkele testen meelopen met de automatische build om te kijken of er geen fouten zijn ingeslopen. In het project Synergebuild zitten de Selenium Unittest die uitgewerkt zijn in het vorige hoofdstuk.
Arnout Logghe
Stageverslag
Pagina 80 van 172
Er moet nu gezorgd worden dat de applicatie Synergebuild kan draaien op de buildserver van het bedrijf. Als buildserver gaan we CruiseControl gebruiken. CruiseControl heeft een applicatie CCTray, die de verschillende projecten grafisch voorstelt (via een GUI‐interface). Hierbij wordt ook weergegeven of de laatste build al of niet geslaagd is.
CruiseControl Vooraleer we CruiseControl kunnen gebruiken, moeten we eerst een CruiseControl buildserver installeren en configureren. Zie hiervoor bijlage 6: Installatie en configuratie van CruiseControl.
Unittests laten werken Nu kunnen we projecten gaan toevoegen aan CCTray. Wanneer die projecten echter automatisch gebuild worden, zien we dat de buildserver nooit aangeeft dat projecten niet geslaagd zijn. Ook wanneer deze projecten fouten bevatten. Dit probleem situeert zich waarschijnlijk in het feit dat de verwijzing naar onze projecten in CCTray verkeerd is. We gaan daarom proberen de .config file zodanig aan te passen dat het wel werkt: • • • • •
door in de projecttag in de CCNet.config een workingdirectory tag toe te voegen; door een batchfile uit te testen op de buildserver. Eerst zonder fout, daarna met fout; door in de projecttag in de CCNet.config, een devenv tag toe te voegen; door een Source Control tag toe te voegen aan CCNet.config; door een project van iemand anders op mijn buildserver te laten lopen.
Door in de projecttag in de CCNet.config een workingdirectory tag toe te voegen We gaan een workingdirectory gaan opgeven, van waaruit hij gaat zoeken naar het project en opgegeven mappen. Wanneer we een workingdirectory opgeven, krijgen we volgende code: <project name="TestenProblemComponentart"> <workingDirectory>D:\SOURCESAFE\Spikes\StageArnoutLogghe\Stage.ArnoutLogghe.TestenProble mComponentart\Stage.ArnoutLogghe.TestenProblemComponentart
Wanneer we nu echter het project TestenproblemComponentart opnieuw toevoegen aan de CCTray, zien we dat de build nog altijd slaagt ondanks de fouten.
Door een batchfile uit te testen op de buildserver We gaan een batchfile (met een fout erin)toevoegen aan de buildserver en daarna kijken wat het resultaat is.
Arnout Logghe
Stageverslag
Pagina 81 van 172
De batchfile noemt test.cmd, en ziet er als volgt uit: @echo off Set
In de CCNet.config file, moeten we nu volgende code uitvoeren: <project name="testcommand" > <exec> <executable>D:\SOURCESAFE\Spikes\StageArnoutLogghe\Batchproject\test.cmd
Wanneer we nu proberen ervoor te zorgen dat de buildserver deze batchfile build, zal dit uiteraard werken. Wanneer we nu echter een fout in deze batchfile zetten (bv voor echo off, de tekst ‘aaaaaaa’), zien we dat de buildserver wel herkent dat er inderdaad een fout in zit. Î De fout ligt dus in de instellingen van de CCNet.config en niet aan onze projecten zelf.
Door een devenv tag toe te voegen In feite verwijst naam, alleen maar naar de naam die we in de CCTray zien. Hij verwijst niet naar de locatie van het project. Er bestaat echter ook een devenv tag, hierin hebben we een solutionfile tag waarmee we naar een solution kunnen verwijzen en een debug tag waarmee we een manier van debuggen kunnen opgeven. Wanneer we dit toepassen, wordt onze code: <project name="C:\Documents and Settings\bealg\Desktop\CruiseControl.NET1.3.0.2918\ApplicationfortestingonCCTray\ApplicationfortestingonCCTray" > <workingDirectory>C:\Documents and Settings\bealg\Desktop\CruiseControl.NET1.3.0.2918\ApplicationfortestingonCCTray <devenv> <solutionfile>ApplicationfortestingonCCTray.sln Debug
Ook dit heeft niet echt resultaat.
Door een Source Control tag toe te voegen aan CCNet.config We kunnen ook een Source Control tag toevoegen aan ons config‐bestand. In deze Source Control, kunnen we dan opgeven uit wat voor soort van Source Control onze projecten komen(SourceSafe, gewone mappen, enz.…). Wij gaan als Source Control filesystem gaan opgeven, wat betekent dat onze projecten in gewone mappen zitten, en niet in een echte Source Control zoals SourceSafe:
Arnout Logghe
Stageverslag
Pagina 82 van 172
<Source Control type="filesystem"> D:\SOURCESAFE\Spikes\StageArnoutLogghe\Stage.ArnoutLogghe.TestenProble mComponentart\Stage.ArnoutLogghe.TestenProblemComponentart
Een project van iemand anders op mijn buildserver laten lopen Dit kunnen we uitproberen om er zeker van te zijn dat het probleem aan mijn buildserver ligt. Hiervoor gaan we eerst een nieuwe server gaan toevoegen in CCTray, namelijk de server hoobuild (zie fig 14).
Fig 14: Toevoegen van de server hoobuild aan mijn CruiseControl
Wanneer we nu het project Synergebuildacceptancetest van deze server toe te voegen en dan builden, zien we dat hij de build op failed blijft zetten (zie fig 15).
Fig 15: Proberen Synergebuildacceptancetest te force builden op de hoobuild server, geeft een failed terug.
Het probleem ligt dus wel degelijk aan een bepaalde instelling in mijn .config file. Omdat ik hierin al veel tijd heb gestoken, en het veel belangrijker is om mijn Synergebuild project met de Selenium testen op de buildserver van Deceuninck werkende te krijgen, kan ik mij daar beter op toespitsen.
Arnout Logghe
Stageverslag
Pagina 83 van 172
Synergebuild op de buildserver van het netwerk laten lopen We gaan nu het project van Synergebuild, op de buildserver van Deceuninck laten lopen en ervoor zorgen dat de Seleniumtests bij een build worden uitgevoerd. Om dat te doen, moeten we enkele stappen ondernemen: • • • • • •
omzetten project in Visual Studio 2008; het project op SourceSafe zetten; een MSBuild file aanmaken; het toevoegen van een batchfile die Selenium Remote Control opstart; enkele problemen die we ervaren en hun oplossing; enkele andere kleine instellingen.
Omzetten project in Visual Studio 2008 Voordat we ons project op de buildserver gaan zetten, gaan we het eerst gaan omzetten in Visual Studio 2008 en ervoor zorgen dat het niet meer werkt met het Micro‐ soft.VisualStudio.TestTools.Unittesting framework. Omzetten naar Visual Studio 2008 is simpel. We openen gewoon het project vanuit Visual Studio 2008 en converteren het. Omdat we niet meer werken met het Microsoft.VisualStudio.TestTools.Unittesting framework, zullen we de assert van dit framework moeten vervangen door een andere assert. We zullen de assert uit NUnit.Framework proberen gebruiken. Hiervoor gaan we eerst en vooral bovenaan in onze code using Assert = Microsoft.VisualStudio.TestTools.Unittesting.Assert;
moeten vervangen door: using Assert = NUnit.Framework.Assert;
Het eerste dat opvalt aan deze nieuwe assert, is dat we verschillende areEqual methods hebben, voor ieder type van data 1(1 voor integer, 1 voor long, enz.….), voor string staat er echter niet direct een areEqual tussen. Wanneer we echter toch proberen 2 strings te vergelijken met areEqual, zien we dat het toch werkt, ondanks dat het type string niet expliciet wordt ondersteund.
Project in SourceSafe zetten We moeten het project ook zeker in SourceSafe zetten. Je kan het daar dan gaan downloaden met get latest version (zie fig 16).
Arnout Logghe
Stageverslag
Pagina 84 van 172
Fig 16: Project downloaden van sourceSafe met Get Latest version
We kunnen nu deze solution openen, al onze bestanden erin zetten en gaan updaten met check in (zie fig 17).
Fig 17: Unittests uit ons project gaan updaten op sourcesafe met check in.
Ik kan zelf geen projecten op SourceSafe zetten, omdat ik hiervoor niet genoeg machtigingen heb; daarvoor moet ik dus een beroep doen op mijn collega’s.
MSBuild file aanmaken Wat ook zeer belangrijk is, is dat we in ons project een MSBuild file hebben die ervoor zorgt dat de juiste Unittests worden uitgevoerd, vooraleer we het project op de buildserver zetten. Wanneer de buildserver ons project probeert te builden, zal hij in feite deze MSBuild file gaan uitvoeren. De MSBuild ziet er als volgt uit:
DependsOnTargets, betekent dat de betreffende tag pas zal worden uitgevoerd, wanneer de tags in de DependsOnTargets uitgevoerd zijn. Bv: Test zal pas worden uitgevoerd, wanneer BuildSolution uitgevoerd is. In de test, zal het exec commando de NUnit console opstarten, met de .dll die in de release of debug zal zitten. Deze NUnit, zal dan de Selenium test programma’s laten lopen. We gaan deze MSBuild opslaan met de extensie .MSBuild in de root van ons project.
Dll’s toevoegen Er wordt een nieuwe map dependencies aangemaakt, en daarin komen alle dll’s, elk in hun overeen‐ komstige map(de Selenium dll’s in Selenium 0.9.2, de nunit dll’s in de map nunit, enz..)
Toevoegen script/batchfile die Selenium RC opstart Het probleem is dat wanneer we Selenium Unittests willen uitvoeren, Selenium Remote Control opgestart moet zijn in de cmd. We gaan hiervoor een nieuwe batchfile aanmaken, die de Selenium Remote Control aan het begin van ons programma zal opstarten en ze op het einde ook zal afsluiten. Dit bestand noemen we Selenium.bat en ziet er als volgt uit: cd ../ cd ../ cd ../ start "Selenium" /min C:\WINDOWS\system32\java.exe -jar Selenium0.9.2\SeleniumRC\Selenium-server.jar -port 4444
Met cd ../ gaan we naar de bovenliggende map. Dit is nodig, omdat we met een referentieel pad verwijzen naar de Selenium‐server.jar file, en ons project standaard opgestart wordt vanuit de /bin
Arnout Logghe
Stageverslag
Pagina 86 van 172
directory van ons project. Start “Selenium”, betekent dat hij aan de Selenium Remote Control console de naam Selenium geeft. We starten de Selenium‐server.jar op met het –jar commando. Met de ‐port 4444, zeggen we dat de Selenium RC console op poort 4444 moet werken. We slaan deze batchfile uiteraard op in een map in ons project. Nu moeten we deze batchfile alleen nog maar oproepen in de MSBuild. Hiervoor gaan we in de target test, voor de exec, volgende code uitvoeren: <Exec Command="StartSelenium.bat" />
Hij zal dus voor het uitvoeren van de NUnittests, eerst de Selenium RC console opstarten, en dan pas de Selenium Unittests uitvoeren.
Problemen die we ervaren en hun oplossing Probleem met sequentieel benaderen van de stappen in MSBuild Het belangrijkste probleem dat we ervaren met deze oplossing: wanneer we in de MSBuild eerst onze Selenium Remote Control console opstarten, en dan proberen onze Unittests uit te voeren. Het systeem wil dit niet doen, omdat in de MSBuild alles sequentieel en niet parallel benaderd wordt. Voor de MSBuild naar een volgende stap gaat, wacht hij tot de huidige stap beëindigd is. Dat betekent dat voor hij de Unittests wil uitvoeren, hij eerst wacht tot het script dat het opstarten van de Selenium Remote Control uitvoert beëindigd wordt. Deze stap eindigt in feite pas, als de Selenium RC console terug beëindigd is. Dit gebeurt uiteraard nooit, omdat de console pas beëindigd wordt als de tests uitgevoerd zijn. Om dit probleem op te lossen, zullen we de batchfile voor het opstarten van de Selenium RC parallel moeten benaderen. Oplossing De oplossing bestaat erin, de Selenium RC console op te starten, in de Setup van onze tests zelf, en niet in de MSBuild. Om een proces te kunnen starten in C# code, moeten we eerst bovenaan het volgende schrijven: using System.Diagnostics.Process Daarna kunnen we op de volgende manier, een proces opstarten:
Arnout Logghe
Stageverslag
Pagina 87 van 172
[SetUp] public void SetupTest() { Process proces = Process.Start(@"..\..\..\StartSelenium.bat"); Selenium = new DefaultSelenium("localhost", 4444, "*iexplore", "http://dpitest/SynergebuildTroubleshooting/"); Selenium.Start();
Het is ook zeer belangrijk dat we sequentieel het proces benaderen. De batchfile bevindt zich in de rootfolder en het project wordt gestart vanuit de bin\release folder. Dat betekent dat we dus eerst 3 keer naar de bovenliggende map moeten gaan voordat we in de map komen waar onze batchfile zich bevindt.
Probleem met het opstarten van Selenium Remote Control via batchfile Wanneer we nu echter proberen, het project op de buildserver te laten lopen, geeft het een failure. Ook wanneer ik nu probeer in de command line van de buildserver met het commando MSBuild, het project te laten builden, geeft het een failure. Mijn voorganger heeft vorig jaar te kampen gehad met hetzelfde probleem, blijkbaar ligt het aan het feit dat de buildserver een andere proxyserver gebruikt dan mijn computer. Oplossing De instellingen van de proxyserver kunnen we wijzigen in Internet Explorer door naar Tools‐ Internet Options – Connections – Lan Settings te gaan. Wanneer we nu “automatically detect settings” zouden aanvinken, zou op de buildserver automatisch de juiste proxy server gebruikt moeten worden. Mijn stagebedrijf vond dit echter een niet zo optimale oplossing. Een tweede oplossing bestaat erin te werken met Selenium RC 1.0. Dit is de nieuwste versie van Selenium Remote Control, en zou dit probleem wel al eens kunnen oplossen. Selenium RC 1.0 beta kunnen we downloaden op de website van Selenium. De gedownloade bestanden, zetten we dan in de map C:\Selenium‐remote‐control‐1.0‐beta‐1‐dist\Selenium‐remote‐control‐1.0‐beta‐1\Selenium‐ server‐1.0‐beta‐1. De .jar‐bestanden van Selenium 0.9.2 in ons project, ga ik dan gaan vervangen door de .jar‐bestanden van deze versie van Selenium. Nu zien we dat ons project Selenium Remote Control 1.0 gebruikt in plaats van 0.9.2.
Arnout Logghe
Stageverslag
Pagina 88 van 172
Fig 18: Selenium RC 1.0 opstarten met een batchfile
Wanneer we nu builden op de buildserver, werkt het nog altijd niet. Het werkt echter wel als we gebruik maken van het commando MSBuild. Het vorige probleem, kunnen we waarschijnlijk oplossen, door Selenium RC te laten lopen als een service. Hiervoor moet er Software worden gedownload, waarmee een eigen user‐defined service kan worden aangemaakt die Selenium RC opstart. De software die we hiervoor gaan gebruiken is srvany, en die kan gedownload worden op www.iopus.com . Een user‐defined service aanmaken die Selenium RC opstart met srvany, doen we in de command prompt met het commando D:\srvany\Instsrv.exe SeleniumRC D:\srvany\Srvany.exe. D:\srvany\Instsrv.exe verwijst naar de Instsrv dat we gaan oproepen, SeleniumRC verwijst naar de naam van onze user‐defined service en D:\srvany\Srvany.exe verwijst naar srvany dat we oproepen. Nu moeten we in Regedit aan deze user‐ defined service, een nieuwe string value toevoegen met als waarde het pad dat verwijst naar onze batchfile die Selenium Remote Control opstart. Nu staat SeleniumRC onder onze services, en kunnen we het starten. Daarna kunnen we onze testscenario’s uitvoeren. Nu gaan we proberen zo’n service, door middel van een batchfile aan te maken. Deze batchfile kunnen we dan uitvoeren in de MSBuild of in de code behind van ons programma. Deze batchfile, ziet er als volgt uit: %CD%\srvany\instsrv.exe SeleniumRemoteController %CD%\srvany\srvany.exe reg add HKLM\System\CurrentControlSet\Services\SeleniumRemoteController\Parameters /v Application /d "%CD% /f sc start SeleniumRemoteController
Wat hier gebeurt is het volgende: Eerst wordt er een nieuwe service SeleniumRemoteController aangemaakt met srvany. Dan wordt er in het register aan deze service een nieuwe string waarde toegevoegd met als waarde het pad naar onze batchfile die Selenium Remote Control opstart. Uiteindelijk wordt deze service dan opgestart. Met %CD% verwijzen we naar de huidige map, op deze manier kunnen we referentieel gaan verwijzen naar srvany en onze batchfile.
Arnout Logghe
Stageverslag
Pagina 89 van 172
Nu gaan we op dezelfde manier, ook een batchfile aanmaken die deze user defined service verwij‐ dert: sc stop SeleniumRemoteController sc delete SeleniumRemoteController %CD%\srvany\instsrv.exe SeleniumRemoteController remove
Eerst gaan we de service Seleniumremotecontroller stoppen. Dan verwijderen we deze service uit services.msc. Uiteindelijk verwijderen we dan deze service uit de registry met instsrv.exe Selenium‐ RemoteController remove. Ook mogen we niet vergeten in de MSBuild, de batchfiles voor de aanmaak van de user defined service en voor het verwijderen van deze service aan te roepen. In de target Test, gaan we de batchfile voor het aanmaken van de user defined service aanroepen, deze heet installserviceforrc2.bat: <Exec Command="installserviceforrc2.bat" /> <Exec Command="Dependencies\NUnit-2.2.8-net-2.0\nunit-console.exe /noshadow /XML="nunit-results.XML" /nologo Deceuninck.Synergebuild.AcceptanceTest\bin\$(Configuration)\Deceuninck.Synergebui ld.AcceptanceTest.dll" />
In de target Build, gaan we dan de batchfile voor het verwijderen van deze service aanroepen: <Exec Command="Removeservice.bat" />
Wanneer we nu echter ons project uitvoeren op de buildserver, geeft hij nog altijd ‘build failed’. Het systeem zegt dat het niet kan connecteren met de Remote Server! Het aanmaken van een user‐service en het afsluiten ervan via batchfiles is echter geen prioriteit. Het belangrijkste is dat de buildserver de Selenium tests uitvoert. Daarom gaan we gewoon de user‐ service aanmaken op de server, zonder gebruik te maken van een batchfile . We stellen de op‐ startopties van deze service in op automatisch. Daarna wordt MSBuild aangepast, zodanig dat het aanmaak‐ of verwijderscript voor de user‐defined services niet uitgevoerd wordt. Deze oplossing werkt wel!
Probleem met het niet vinden van het window ‘Arnout’ Wanneer we de testscenario’s uitvoeren op de buildserver met force build, krijgen we volgende foutmelding: “unable to find window Arnout”. Het window ‘Arnout’, is het window dat we in getPad aanmaken waar we mee gaan inloggen en waarmee we het adres in de adresbalk achter de ? gaan opslaan. De code van getPad:
Het probleem zit hier bij de Selenium.selectwindow(“Arnout”). De buildserver vindt hier het window ‘Arnout’ niet. Dit probleem wordt waarschijnlijk veroorzaakt door het feit dat mijn computer werkt met Internet Explorer 7 en de buildserver met Internet Explorer 6. We hebben IE7 geïnstalleerd op de buildserver, maar dit helpt niet. Wanneer we in de MSBuild echter het uitvoeren van de NUnittests uitschakelen werkt alles wel:
Probleem met het afsluiten van Selenium Remote Control Op het einde van ons programma, in de teardown moeten we nu zorgen dat Selenium RC afgesloten wordt. Normaal zou de code er dus als volgt moeten uitzien: [TearDown] public void TeardownTest() { Selenium.Stop(); proces.Close(); }
Dit werkt echter niet, ook met proces.Close() wil hij het proces niet afsluiten. Ook process.kill werkt niet.
Arnout Logghe
Stageverslag
Pagina 91 van 172
Oplossing Wanneer we proces.kill plaatsen onder Selenium.start() wordt het proces ook hier niet afgesloten. De reden waarom we Selenium RC niet kunnen afsluiten, ligt in het feit dat het proces niet langer verwijst naar Selenium RC. We hebben geen manier gevonden om hier toch te kunnen verwijzen. Waarschijnlijk ligt de fout in het feit dat het proces op de één of andere manier wordt afgesloten, zonder Selenium RC af te sluiten. We hebben dan ook niet echt een oplossing gevonden voor dit probleem. Het afsluiten op zich is echter niet belangrijk voor het welslagen van de testprocedures.
Conclusie De bedoeling hier was om het project van Synergebuild met de Selenium Unittests te laten draaien op een buildserver. Eerst heb ik geprobeerd om lokaal een buildserver te installeren en Synergebuild hierop te laten builden. De installatie en configuratie vormden geen probleem. Het laten builden van zelfgemaakte projecten, werd echter steeds als succesvol aanzien, ook bij fouten. Dit lag waarschijn‐ lijk aan een verkeerde instelling van mijn pc, of van de buildserver zelf. Wanneer dit echter werd uitgetest op de buildserver van het bedrijf, wilde het Synergebuild project wel een ‘failed build’ genereren. Daarna heb ik ervoor gezorgd dat het Synergebuild project met de Selenium Unittests nu wel uitgevoerd werd. Dit heeft heel wat tijd in beslag genomen en is uiteindelijk dan ook gelukt. Het probleem lag in het feit dat hij Selenium RC nooit op een goede manier wilde opstarten of afsluiten. We hebben Selenium RC geprobeerd op te starten via batchfiles, in de MSBuild, als een proces, enz… Uiteindelijk werd dit opgelost door een user‐defined service aan te maken op de buildserver die Selenium RC opstart. Daarmee was het probleem van het aflsuiten van Selenium RC nog niet opgelost. Een 100 % optimale oplossing voor dit probleem is er dan ook niet uit de bus gekomen. Toch was men tevreden over het resultaat omdat het voldoende werkbaar is en zal deze manier van automated testing dan ook in de toekomst bij Deceuninck gebruikt worden.
6.4. Vergelijken van Visual Studio 2005 – Visual Studio 2008 6.4.1. Doelstelling Men is van plan om in de toekomst Visual Studio 2008 te gebruiken, in plaats van Visual Studio 2005. Daarom kreeg ik een researchopdracht. Deze hield in dat ik moest onderzoeken hoe men de projecten kan omzetten naar Visual studio 2008 en wat de verschillen tussen beide versies zijn. Concreet bestond mijn opdracht dus uit 2 onderdelen: • •
Nagaan of we zonder veel problemen, een project van Deceuninck dat gemaakt is in Visual Studio 2005 kunnen omzetten naar Visual Studio 2008; wanneer deze conversie gelukt was, wilde men weten wat de verschillen zijn tussen dat project in beide versies, en meer bepaald wat de verschillen zijn tussen het .NET framework 3.5 in VS 2008 en het .NET framework 2.0 in VS 2005.
Arnout Logghe
Stageverslag
Pagina 92 van 172
Voor het uittesten en vergelijken, ga ik opnieuw het project Synergebuild gebruiken; maar deze keer zonder de testprogramma’s.
6.4.2. Omzetten van Synergebuild naar Visual Studio 2008 Wanneer we het Synergebuild willen omzetten naar Visual Studio 2008, zitten we meteen al met een probleem: Het project staat namelijk op SourceSafeen kan dus niet zomaar gewijzigd worden. Dit komt door bindingen met SourceSafe in het project zelf. Deze bindingen hebben de extensie .vssscc en .scc. Zolang deze bindingen in het project zitten, kunnen we niet converteren naar Visual Studio 2008. We kunnen deze bindingen heel gemakkelijk verwijderen met het programma servant salamander 2.0. Dit programma gaat net zoals Windows Verkenner, een directory structuur tonen. We kunnen hierin heel gemakkelijk zoeken naar bestanden of mappen met bepaalde extensies en deze dan verwijderen of aanpassen. Dit programma werkt trouwens veel vlugger en efficienter dan de gewone Windows search. Nadat alle bestanden met de extensie .vssscc en .scc zijn verwijderd, kunnen we gaan converteren. Daarvoor moeten we het project gewoon openen in Visual Studio 2008. Meteen krijgen we dan volgende foutmelding (zie fig 1).
Fig 1: Foutmelding die men krijgt, wanneer je een project probeert te openen met verwijderde bindings naar Source control.
Dit is zeer goed, het betekent dat de binding met SourceSafe wel degelijk verwijderd werd. Hierna krijgen we de conversie‐wizard, die we moeten doorlopen om te converteren van Visual Studio 2005 naar 2008 (zie fig 2).
Arnout Logghe
Stageverslag
Pagina 93 van 172
Fig 2: Beginscherm van de conversie wizard
We doorlopen deze wizard, en hoeven in feite niet echt instellingen op te geven. Op het einde moeten we opgeven of we alles converteren naar het 3.5 framework in Visual Studio 2008 of op het 2.0 framework laten staan. Wij laten het hier op het 2.0 framework staan. (zie fig 3).
Fig 3: Beginscherm van de conversie wizard
We kunnen na de conversie het framework ook nog altijd converteren naar 3.5. Dit kan enkel nog per afzonderlijk project, en niet meer voor de hele solution. Converteren naar een ander framework gebeurt via het instellen van de properties. (zie fig 4).
Arnout Logghe
Stageverslag
Pagina 94 van 172
Fig 4: Eigenschappen van een project. Hier kan men het target framework van een project veranderen naar bv 3.5.
6.4.3. Bekijken van de verschillen tussen VS 2008 en VS 2005 Om de verschillen te bekijken tussen een project in VS 2008 en eenzelfde project in VS 2005, gaan we het programma WinDiff gaan gebruiken. WinDiff is een programma, dat een gebruiker toestaat files en directories op een grafische manier te vergelijken met elkaar en te zien wat de verschillen zijn . Wij gaan hier de directories van de solutions vergelijken. Wanneer we in het menu klikken op Compare Directories, moeten we de locatie van de 2 directories opgeven (eventueel inclusief de subdirectories) (zie fig 5).
Fig 5: Opgeven van de directories, die moeten vergeleken worden met WinDiff
Arnout Logghe
Stageverslag
Pagina 95 van 172
Verschillen tussen VS 2008 2.0 framework en VS 2005 2.0 framework Eerst gaan we de verschillen bekijken tussen een project dat we omzetten in VS 2008 in het .NET framework 2.0 en het oorspronkelijk project in VS 2005 in het .NET framework 2.0. Normaal zouden de verschillen hier niet zo groot mogen zijn, we veranderen alleen maar van programma, niet van framework of code (zie fig 6).
Fig 6: Verschillen tussen .NET 2.0 in VS 2005 en 2008 met WinDiff
Zoals we dus zien, zijn de verschillen inderdaad niet groot. De enige 2 files die veranderen, zijn de .sln solution files en de .csproj files. Dat is ook normaal, omdat deze files gebonden zijn aan de versie van Visual Studio. Onze classen, de MSBuild files, enz… veranderen allemaal niet.
Verschillen tussen VS 2008 3.5 framework en VS 2005 2.0 framework Hier zouden de verschillen iets groter zijn, omdat we ook een heel ander framework gaan gebruiken (zie fig 7).
Arnout Logghe
Stageverslag
Pagina 96 van 172
Fig 7: Verschillen tussen .NET 2.0 in VS 2005 en 3.5 in 2008 met WinDiff
Wanneer we veranderen van framework, wordt er zoals verwacht veel meer aangepast: naast de solution en .csproj files, ook alle .dll files en .pdb files.
Arnout Logghe
Stageverslag
Pagina 97 van 172
6.5. De versie van Visual Studio tonen in de Titelbalk 6.5.1. Doelstelling We hebben verschillende versies van Visual Studio naast elkaar geïnstalleerd. Het probleem stelt zich, wanneer we in verschillende versies tegelijk werken: Welk venster bevat welke versie van Visual Studio?
6.5.2. Mijn opdracht: Zoek een manier om het pad in de titelbalk van Visual studio 2005 en 2008 te veranderen, zodanig dat ook de versie van Visual Studio erin komt.
6.5.3. Oplossing: We gaan door middel van een macro, de titel bovenaan in Visual Studio veranderen in het volledig pad van het project. Hiervoor gaan we in Visual Studio naar tools – macro’s‐ macro’s IDE. Nu komen we in het macro venster, links staat nu een boomstructuur met alle macro’s van het Visual Studio programma waar we inzitten (zie fig 1).
Fig 1: De macro EnvironmentEvents in Visual Studio
We gaan naar de EnvironmentEvents onder MyMacros, en voegen er volgend stukje code aan toe: Private Sub showTitle(ByVal title As String)
Arnout Logghe
Stageverslag
Pagina 98 van 172
Dim vs As String If DTE.Version.StartsWith("9") Then vs = "VS 2008" Else vs = "VS 2005" End If SetWindowText(New System.IntPtr(DTE.MainWindow.HWnd), vs & " - " & title) End Sub
Wanneer we dit uitproberen in Visual Studio 2005, zien we dat het werkt (zie fig 2).
Fig 2: Bovenaan in de titelbalk van Visual Studio 2005 wordt nu de versie getoond en het pad van het openstaand project
Wanneer we dit echter uitvoeren in Visual Studio 2008, werkt het niet. Het probleem zit in het feit, dat in de mymacros in de myreferences in Visual Studio 2008, we geen reference hebben naar Microsoft.VisualBasic. Dit hebben we nodig, omdat onze macro is geschreven in Visual Basic. We moeten dus nog een reference naar Microsoft.VisualBasic toevoegen in de myreferences. Dit doen we door met de rechtermuisknop op myreferences te klikken, en dan op add reference. Nu selecteren we in het Add Reference venster, de juiste reference. De reference is nu toegevoegd aan de myreferences (zie fig 3).
Arnout Logghe
Stageverslag
Pagina 99 van 172
Fig 3: Referenties binnen de macro’s van Visual Studio 2008
Nu werkt alles wel goed, en krijgen we ook in Visual Studio 2008 de tekst VS 2008 in de titelbalk:
Fig 4: Bovenaan in de titelbalk van Visual Studio 2008 wordt nu de versie getoond en het pad van het openstaand project
Bron: http://www.helixoft.com/blog/archives/32
Arnout Logghe
Stageverslag
Pagina 100 van 172
6.6. LINQ implementeren in de projecten van het bedrijf. 6.6.1. Doelstelling van het probleem: Bij dit probleem ga ik gebruikmaken van LINQ en van een n‐tier architectuur. Vooraleer ik dit probleem ga uitdiepen, eerst een woordje uitleg bij deze begrippen: •
•
LINQ of Language Integrated Query, is een component van het .NET framework 3.5, dat er‐ voor zorgt dat we in .NET kunnen werken met een op SQL lijkende set taalelementen. Met deze taalelementen, kunnen we dan verschillende systemen aanspreken op dezelfde manier. Zo kunnen we met LINQ gegevens uit een XML‐bestand, array, webservice maar ook uit een relationele database aanspreken; Een n‐tier architectuur of een multitierarchitectuur, is een softwarearchitectuur waarbij de structuur van de software is opgebouwd uit meerdere lagen. In een multitierarchitectuur, gaan we de software gaan opdelen in componenten die met elkaar samenwerken. In een be‐ paalde laag kunnen er meerdere componenten zitten, die dan met componenten uit een an‐ dere laag gaan samenwerken.
Bij Deceuninck werkt men met een façade laag voor het lezen en wegschrijven van data uit databan‐ ken. Deze façade verzorgt de mapping van de dataset met het eigen object model van de client applicatie. Voor het ophalen en wegschrijven van data uit de databank gebruikt de DAL (dataacces‐ slayer) stored procedures. De doelstelling van deze opdracht, is het gebruik van LINQ in een n‐tier architectuur bekijken. En meer specifiek het gebruik en de grafische weergave van SQL to LINQ. Ook de voordelen in beperking van code met deze werkwijze(Hebben we bv nog stored procedures nodig?) gaan we gaan bekijken. Ik ga dit verwezenlijken, door te proberen LINQ te gebruiken in de dataaccesslayer (zie fig 1).
Fig 1: Schema dat aanduid hoe de dataflow oorspronkelijk werkt in de projecten van het bedrijf.
Dit is de oorspronkelijke architectuur, hier gaan we in de DAL een dataset gaan opvullen met data uit de database, en dan de inhoud van deze dataset converteren naar ons object model. Dit object model gaan we dan in de client applicatie gaan gebruiken (zie fig 2). Arnout Logghe
Stageverslag
Pagina 101 van 172
Fig 2: Schema dat aanduid hoe de dataflow gaat veranderen wanneer we SQL to LINQ gaan gebruiken in de DAL.
In onze nieuwe datastructuur, gaan we SQL to LINQ gebruiken. We gaan de tabellen van SQL to LINQ die we gegenereerd hebben opvullen met data uit de database. Dan gaan we de inhoud van deze tabellen converteren naar ons object model.
6.6.2. Uitgevoerde tests • • • •
Het koppelen van een database aan een project met behulp van een LINQ to SQL classes object. Het aanspreken van het Datacontext object met behulp van LINQ to SQL. Het aanpassen van gegevens in een databank met behulp van het datacontext object. Het opvragen van gegevens uit een databank met behulp van het datacontext object en LINQ.
6.6.3. Extra Opmerkingen/Issues • •
Wat te doen bij databasechanges? Kunnen we werken met verschillende .dbml files?
Arnout Logghe
Stageverslag
Pagina 102 van 172
6.6.4. Uitgevoerde tests: Het koppelen van een database aan een project met behulp van een LINQ to SQL classes object Eerst en vooral moeten we ervoor zorgen dat in de server Explorer, een connectie naar de database is gelegd die we willen aanspreken door middel van LINQ. Wanneer dat is gebeurd, gaan we een LINQ to SQL classes object aanmaken. Dat doen we door eerst ons project op framework 3.5 te zetten. Daarna klikken we met de rechtermuisknop op ons project – add New item, en selecteren LINQ to SQL classes onder data. Nu is er nieuw item in ons project, met de extensie .dbml. Wanneer we dit openen, krijgen we volgend scherm (zie fig 3).
Fig 3: DBML schema
Dit scherm is uiteraard nog leeg, nu kunnen we echter tabellen en stored procedures uit onze data connecties hiernaar toe slepen.
Arnout Logghe
Stageverslag
Pagina 103 van 172
We zouden bijvoorbeeld de tabel Rawmaterial hiernaartoe kunnen slepen (zie fig 4).
Fig 4: DBML schema met de tabel RawMaterial
Zoals je kunt zien, krijgen we nu direct een grafisch overzicht van die tabel, met al zijn kolommen. Ook de onderliggende relaties van tabellen worden getoond in dit venster. We kunnen hier bv PalletPosition en PalletPositionStatus hierin slepen (zie fig 5)
Arnout Logghe
Stageverslag
Pagina 104 van 172
Fig 5: DBML schema met de tabel PalletPosition en PalletPositionStatus
Ik verwijder nu eerst PalletPosition en PalletPositionStatus, en ga Rawmaterial erin slepen, omdat ik hiermee ga werken in de rest van het project. Naast tabellen kunnen we ook stored procedures slepen in het venster rechts hiervan. We kunnen bv al de stored procedures van Rawmaterial(PR_Rawmaterial….) ernaartoe slepen (zie fig 6).
Arnout Logghe
Stageverslag
Pagina 105 van 172
Fig 6: DBML schema met de tabel RawMaterial
Wanneer we nu gaan kijken naar “dbml‐bestandsnaam”.designer.cs, zien we dat hij een heleboel code zelf aangemaakt heeft. Code in verband met de aanmaak van tabellen, stored procedures, enz.…. Zo heeft hij een nieuwe class met referenties naar de onderliggende tabel RawMaterial aangemaakt. Deze class bevat wrapping code die ons in staat stelt die tabel op een gebruiksvriendelijke en gemakkelijke manier aan te spreken: [Table(Name="dbo.RawMaterial")] public partial class RawMaterial : INotifyPropertyChanging, INotifyPropertyChanged{ private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty); private int _Id; private string _StpProductNumber; private string _StpColorNumber;
Ook heeft hij een referentie naar de stored procedure dbo.PR_RawMaterial_GET gemaakt: [Function(Name="dbo.PR_RawMaterial_GET")]
Arnout Logghe
Stageverslag
Pagina 106 van 172
public ISingleResult PR_RawMaterial_GET([Parameter(Name="Id", DbType="Int")] System.Nullable id){ IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), id); return((ISingleResult)(result.ReturnValue); }
Voor alles dat we in het .dbml venster slepen, gaat hij code gaan genereren.
Aanspreken van het Datacontext object met behulp van LINQ to SQL De genereerde code aanspreken, doen we door een nieuw DAL.DataContext aan te maken, en er een connectiestring aan mee te geven. Wij hebben ons .dbml object gewoon DataClasses1 genoemd, de code voor aanspreken wordt dan dus: DAL.DataClasses1DataContext context = new DAL.DataClasses1DataContext(dal.SqlConnectionString);
Nu hebben we een verwijzing naar de code van DataClasses1.dbml en kunnen we hier van alles mee doen: het aanpassen van gegevens in de databank, ophalen van gegevens uit een databank, enz.….
Het aanpassen van gegevens in een databank met behulp van het data context object. Aanpassen van gegevens in een databank doen we door Executecommand, ExecuteQuery of een stored procedure te gebruiken.
Executecommand Bij Executecommand, moeten we een command string meegeven, en een parameter collectie. De parameter collectie is van het type object[]. object[] param = new object[2]; param[0] = rawMaterial.Id; param[1] = (rawMaterial.Stp.ProductNumber != null) ? (string)rawMaterial.Stp.ProductNumber : String.Empty; DAL.DataClasses1DataContext context = new DAL.DataClasses1DataContext(dal.SqlConnectionString); context.ExecuteCommand("Update RawMaterial SET StpProductNumber={1} Where Id={0}", param);
ExecuteQuery
Arnout Logghe
Stageverslag
Pagina 107 van 172
Bij ExecuteQuery geven we ook een query string mee en een collectie van parameters. Of we geven een query string mee, een collectie van parameters en wat voor IEnumerable hij moet teruggeven. DAL.DataClasses1DataContext context = new DAL.DataClasses1DataContext(dal.SqlConnectionString); object[] param2 = new object[1]; string qyerystring = "select Sample.*, SampleProfileType.Description AS 'SampleProfileTypeDescription',InspectionOrder.Description AS InspectionOrderDescription FROM Sample INNER JOIN InspectionOrder ON Sample.InspectionOrderId = InspectionOrder.Id INNER JOIN SampleProfileType ON Sample.SampleProfileTypeId = SampleProfileType.Id"; IEnumerable num = context.ExecuteQuery(qyerystring, param2);
Stored Procedure Om een stored procedure te kunnen gebruiken, typen we gewoon de DataClassesDataContext.stored procedure, en geven we er de juiste parameters aan mee. DAL.DataClasses1DataContext context = new DAL.DataClasses1DataContext(dal.SqlConnectionString); context.PR_RawMaterial_UPD(rawMaterial.Id, rawMaterial.Stp.ProductNumber, rawMaterial.Stp.ColorNumber,rawMaterial.Stp.Type, rawMaterial.Stp.Width, rawMaterial.Stp.Length,rawMaterial.Description,rawMaterial.ExtrudedWeightPerVolume,rawMaterial.LastChangedByAccountName);
Het opvragen van gegevens uit een databank met behulp van het data context object en LINQ. We gaan nu niet meer proberen door middel van LINQ to SQL een resultaatset te bekomen met de data, maar door gebruik te maken van LINQ. We gaan echter wel nog de datacontext gebruiken, en gaan op deze datacontext onze LINQ query gaan uitvoeren. Om een LINQ query te kunnen uitvoeren, moeten we volgende syntax gebruiken: Var/Iqueryable variabele =
from…… where….. Orderby ……. Select ……;
De select staat hier dus achteraan, eerst moeten we in de from specificeren welke datastructuur we gaan gebruiken(wij gebruiken datacontext), dan moeten we de voorwaarden opgeven, dan sorteren en als laatste opgeven welke velden we gaan gebruiken. Het resultaat hiervan wordt opgeslaan in een var object, een IQueryable, enz.….
Arnout Logghe
Stageverslag
Pagina 108 van 172
We gaan werken met de standaard query operators, maar daarnaast ook met enkele LINQ specifieke operators. Toegepast op ons voorbeeld waarbij we een lijst van samples willen krijgen, wordt dit: IQueryable query =
from n in context.Samples Where(voorwaarden) select n;
Wanneer we nu het programma gaan uitvoeren, wordt de LINQ query geconverteerd naar SQL en uitgevoerd op het moment dat we de LINQ query voor het eerst gebruiken in ons programma. De SQL string die we dan bekomen, ziet er dan uit als iedere andere SQL query string met de voorwaar‐ den die opgegeven zijn in de LINQ query. Voor de where gaan we verschillende soorten van werkwijzen moeten gebruiken, deze worden hieronder besproken.
Where clausule Uitleggen van het probleem Het deel van ons programma, waar we een LINQ query op gaan uitvoeren om een lijst van samples terug te krijgen, ziet er als volgt uit (zie fig 7).
Arnout Logghe
Stageverslag
Pagina 109 van 172
Fig 7: Tonen van een lijst van de samples op basis van criteria’s
We hebben bovenaan enkele criteriavelden die we kunnen gaan invullen. We gaan dan de samples die aan deze criteria velden voldoen eronder gaan tonen. Wanneer bv we als set nr “1” ingeven en als inspection order type “Followup 2h”,krijgen we volgend resultaat:
Fig 8: Lijst met samples waarvan followup 2h is en setnr 1.
Voor een lijst van samples te krijgen, op basis van bepaalde criteria, gingen we vroeger volgende stored procedure oproepen en uitvoeren: SELECT Sample.*, SampleProfileType.Description AS 'SampleProfileTypeDescription', InspectionOrder.Description AS InspectionOrderDescription FROM Sample WITH (NOLOCK) INNER JOIN InspectionOrder WITH (NOLOCK) ON Sample.InspectionOrderId = InspectionOrder.Id INNER JOIN SampleProfileType ON Sample.SampleProfileTypeId = SampleProfileType.Id WHERE Sample.Id = COALESCE( @Id, Sample.Id) AND Sample.StpType = COALESCE( @StpType, Sample.StpType) AND Tool = COALESCE( @Tool, Tool) AND SetNumber = COALESCE(@SetNumber, SetNumber) AND DATEDIFF(d, COALESCE(@ExtrusionDateFrom, ExtrusionDate), ExtrusionDate ) >= 0 AND DATEDIFF(d, ExtrusionDate, COALESCE(@ExtrusionDateUntil, ExtrusionDate)) >= 0 AND ExtrusionMachineNumber = COALESCE(@ExtrusionMachineNumber, ExtrusionMachineNumber) AND
Arnout Logghe
Stageverslag
Pagina 110 van 172
Sample.ProductOrderNumber = COALESCE(@ProductOrderNumber, ProductOrderNumber) AND Sample.ProductOrderLineNumber = COALESCE(@ProductOrderLineNumber, ProductOrderLineNumber) AND InspectionOrder.InspectionOrderType = COALESCE(@InspectionOrderTypeId, InspectionOrder.InspectionOrderType) AND SampleProfileTypeId = COALESCE(@SampleProfileTypeId, SampleProfileTypeId)
Aan deze stored procedure, gingen we dan onze criteria meegeven. De stored procedure keek dan met coalesce, of de criteria niet leeg was en dus wel degelijk bestond, en plaatste het dan in de where clausule. De where clausule werdt dus dynamisch opgesteld. Wij gaan nu deze stored procedure niet meer gaan oproepen, maar gaan zelf een LINQ query gaan opstellen die op dezelfde manier werkt. We zullen dus een soort van alternatief moeten vinden voor coalesce. De where clausule in onze LINQ query, moet dus ook dynamisch opgesteld worden!!!!! Ook zullen we voor een string, datum of integer anders moeten gaan werken om dit op te lossen.
Vergelijken van een string uit de tabel met een meegegeven string en kijken of meegegeven string niet null of “” is. Nu gaan we proberen om een alternatief te cinden voor coalesce in de LINQ query. We gaan hier van een aantal meegegeven strings, eerst en vooral gaan kijken of ze null of leeg zijn. Als dat niet het geval is, gaan we ze gaan vergelijken met de overeenkomstige waarde uit de tabel. We gaan hiervoor volgende syntax moeten gebruiken: ((sampleSearchCriteria.MeasurementStationId ?? "") == "" || sampleSearchCriteria.MeasurementStationId.Equals(n.EnteredForMeasurementStationId))
SampleSearchCriteria.MeasurementStationId ?? "" betekent dat we kijken of MeasurementStationID null is. Als dat zo is, dan wordt de waarde "" eraan toegekend. Wanneer dat niet zo is, wordt er gewerkt met de waarde van MeasurementStationID. SampleSearchCriteria.MeasurementStationId.Equals(n.EnteredForMeasurementStationId) betekent dat we gaan kijken of de meegegeven string sampleSearchCriteria.MeasurementStationId en EnteredForMeasurementStationId uit de database gelijk zijn aan elkaar. Wat er dus in feite gebeurt is het volgende: •
•
Eerst wordt gekeken of de meegeven string sampleSearchCriteria.MeasurementStationId null is; o Als dat zo is wordt sampleSearchCriteria.MeasurementStationId op "" gezet; o Anders behoud sampleSearchCriteria.MeasurementStationId zijn waarde; Dan wordt gekeken of sampleSearchCriteria.MeasurementStationId gelijk is aan "" ; o Als dat zo is, is aan de voorwaarde voldaan; o Anders kijken we of sampleSearchCriteria.MeasurementStationId gelijk is aan n.EnteredForMeasurementStationId uit de tabel.
Arnout Logghe
Stageverslag
Pagina 111 van 172
Wanneer we dit uitvoeren, krijgen we als conversie in SQL: •
•
Wanneer sampleSearchCriteria.MeasurementStationId gelijk is aan null of “”: Helemaal niets! Er wordt niets omgezet naar SQL, want er is aan de voorwaarde sample‐ SearchCriteria.MeasurementStationId == “” voldaan. En alleen voorwaarden waarin we een veld uit de tabel gebruiken of vergelijken, worden ge‐ converteerd naar SQL. Bv n.EnteredForMeasurementStationId== “” zou wel geconverteerd worden. Wanneer sampleSearchCriteria.MeasurementStationId gelijk is aan BEDEV: (@p0 = [t0].[EnteredForMeasurementStationId]) met @p0='BEDEV'
Dit is dus een goed alternatief voor coalesce, want het doet juist hetzelfde.
Vergelijken van een datum uit de tabel met een meegegeven datum en kijken of meegegeven datum niet null is. We gaan hiervoor de functie DateTime.Compare gebruiken, die 2 datums met elkaar vergelijkt: DateTime.Compare(n.ExtrusionDate, sampleSearchCriteria.ExtrusionDateFrom.GetValueOrDefault(n.ExtrusionDate)) >= 0
Wanneer sampleSearchCriteria.ExtrusionDateFrom null is, we de opgegeven default waarde krijgen, dit is de waarde tussen haakjes, de n.ExtrusionDate; Wanneer sampleSearchCriteria.ExtrusionDateFrom niet null is, krijgen we de waarde van sampleSearchCriteria.ExtrusionDateFrom.
DateTime.Compare vergelijkt 2 datums met elkaar en geeft 0 terug als ze hetzelfde zijn, 1 als de eerste groter is dan de tweede en ‐1 als de eerste kleiner is dan de tweede. Wat er dus in feite gebeurt is het volgende: • •
•
We gaan de waarde n.ExtrusionDate en de tweede datum waarde gaan vergelijken met el‐ kaar; We kijken of sampleSearchCriteria.ExtrusionDateFrom null is; o Als dat zo is, wordt de tweede datum waarde, de waarde van n.ExtrusionDate; o Anders wordt de tweede datum waarde, de waarde van sampleSearchCrite‐ ria.ExtrusionDateFrom; Wanneer de eerste waarde groter of gelijk is aan de tweede , is aan de voorwaarde voldaan.
Wanneer we dit uitvoeren, krijgen we als conversie in SQL: •
Wanneer sampleSearchCriteria.ExtrusionDateFrom null is: ([t0].[ExtrusionDate] >= (COALESCE(@p1,[t0].[ExtrusionDate]))) met @p1=NULL
Arnout Logghe
Stageverslag
Pagina 112 van 172
•
Wanneer sampleSearchCriteria.ExtrusionDateFrom 29/04/2008 is: ([t0].[ExtrusionDate] >= (COALESCE(@p2,[t0].[ExtrusionDate]))) met @p2='Apr 29 2008 12:00:00:000AM'
Kijken of een integer waarde uit de tabel niet voorkomt in een meegegeven integer array en kijken of meegegeven integer array niet null is Voordat we onze LINQ query gaan opstellen, gaan we eerst gaan kijken of de meegegeven integer array null is, als dat zo is, initialiseren we het als een lege integer array met 0 elementen: if (sampleSearchCriteria.MultipleInspectionOrderTypeId == null) { sampleSearchCriteria.MultipleInspectionOrderTypeId = new int[0]; }
Nu gaan we in de LINQ query, door middel van integer[].contains kijken of die integer waarde in de array van integers zit. (sampleSearchCriteria.MultipleInspectionOrderTypeId.Length == 0 || sampleSearchCriteria.MultipleInspectionOrderTypeId.Contains(n.InspectionOrder.InspectionOrde rType))
SampleSearchCrite‐ ria.MultipleInspectionOrderTypeId.Contains(n.InspectionOrder.InspectionOrderType) gaat kijken of de integer waarde n.InspectionOrder.InspectionOrderType voorkomt in de integer array sample‐ SearchCriteria.MultipleInspectionOrderTypeId. Wat er dus in feite gebeurt is het volgende: • •
•
We gaan eerst gaan kijken of sampleSearchCriteria.MultipleInspectionOrderTypeId null is; o indien dat zo is, initialiseren we het als een lege int[]; We kijken of sampleSearchCriteria.MultipleInspectionOrderTypeId.Length een lege int[] array is; o Als dat zo is, is aan de voorwaarde voldaan, anders: Kijken we of sampleSearchCriteria.MultipleInspectionOrderTypeId de integer n.InspectionOrder.InspectionOrderType bevat; o Als dat zo is, is aan de voorwaarde voldaan; o Anders is er niet aan de voorwaarde voldaan;
Wanneer we dit uitvoeren, krijgen we als conversie in SQL: •
Wanneer sampleSearchCriteria.MultipleInspectionOrderTypeId null is: Helemaal niets! Er wordt niets omgezet naar SQL, want er is aan de voorwaarde sample‐ SearchCriteria.MultipleInspectionOrderTypeId.Length == 0 voldaan. En alleen voorwaarden waarin we een veld uit de tabel gebruiken of vergelijken, worden ge‐ converteerd naar SQL.
Arnout Logghe
Stageverslag
Pagina 113 van 172
•
Wanneer sampleSearchCriteria.MultipleInspectionOrderTypeId 2 en 4 bevat: ([t1].[InspectionOrderType] IN (@p4, @p5)) met @p4=2,@p5=4
Vergelijken van een integer waarde uit de tabel met een meegegeven integer waarde en kijken of meegegeven integer waarde bestaat. Ook hier gaan we equals gebruiken: (sampleSearchCriteria.SampleProfileType.Id == -1 || sampleSearchCriteria.SampleProfileType.Id.Equals(n.SampleProfileTypeId))
Wat er dus in feite gebeurt is het volgende: • •
Eerst kijken we of de meegegeven integer waarde bestaat of niet(= ‐1 of niet); o indien de meegegeven waarde niet bestaat, is aan de voorwaarde voldaan, anders: Dan kijken we of sampleSearchCriteria.SampleProfileType.Id en n.SampleProfileTypeId gelijk zijn aan elkaar; o Als dat zo is, is aan de voorwaarde voldaan; o Als dat niet zo is, is aan de voorwaarde niet voldaan;
Wanneer we dit uitvoeren, krijgen we als conversie in SQL: •
•
Wanneer sampleSearchCriteria.SampleProfileType.Id ‐1 is: Helemaal niets! Er wordt niets omgezet naar SQL, want er is aan de voorwaarde sample‐ SearchCriteria.SampleProfileType.Id == ‐1 voldaan. En alleen voorwaarden waarin we een veld uit de tabel gebruiken of vergelijken, worden ge‐ converteerd naar SQL. Wanneer sampleSearchCriteria.SampleProfileType.Id gelijk is aan 9: (@p0 = [t0].[SampleProfileTypeId]) met @p0=9
Vergelijken van een bool waarde uit de tabel met een meegegeven nullable bool waarde en kijken of meegegeven nullable bool waarde null is. De gebruikte code: (ApplicableForAuditSearchTyp == null || n.IsApplicableForAudit == ApplicableForAuditSearchTyp)
Wat er dus in feite gebeurt is het volgende: • •
Eerst kijken we of de meegegeven nullable bool waarde null is; o indien dat zo is, is aan de voorwaarde voldaan, anders: Kijken we of de meegegeven bool waarde gelijk is aan de bool waarde uit de tabel; o Als dat zo is, is aan de voorwaarde voldaan; o Als dat niet zo is, is aan de voorwaarde niet voldaan.
Arnout Logghe
Stageverslag
Pagina 114 van 172
Wanneer we dit uitvoeren, krijgen we als conversie in SQL: •
•
Wanneer ApplicableForAuditSearchTyp null is: Helemaal niets! Er wordt niets omgezet naar SQL, want er is aan de voorwaarde sample‐ SearchCriteria.SampleProfileType.Id == null voldaan. En alleen voorwaarden waarin we een veld uit de tabel gebruiken of vergelijken, worden ge‐ converteerd naar SQL. Wanneer ApplicableForAuditSearchTyp gelijk is aan true: ([t0].[IsApplicableForAudit] = @p4) met @p4=1 en @p4 is van het type int
Sorteren met orderby en orderbydescending Het probleem is, dat we met een switch moeten werken voor onze order by. En dat we in het LINQ commando niet zomaar een switch bij de order by clausule kunnen steken. We moeten dus sorteren nadat we ons LINQ commando hebben opgesteld. switch(sampleSearchCriteria.SampleSortOrder){ case SampleSortOrder.ByIdAsc: query = query.OrderBy(o => o.Id); break; case SampleSortOrder.ByIdDesc: query = query.OrderByDescending(o => o.Id); break; case SampleSortOrder.ByExtrusionDateDesc: query = query.OrderByDescending(o => o.ExtrusionDate); break; case SampleSortOrder.ByExtrusionDateAsc: query = query.OrderBy(o => o.ExtrusionDate); break; case SampleSortOrder.ByEntryDateDesc: query = query.OrderByDescending(o => o.EntryDate); break; case SampleSortOrder.ByEntryDateAsc: query = query.OrderBy(o => o.EntryDate); break; case SampleSortOrder.ByExtrusionDateAsc_SampleProfileTypeIdAsc: query = query.OrderBy(o => o.ExtrusionDate).ThenBy(o => o.SampleProfileTypeId); break; default: throw new ApplicationException("Unhandled SampleSortOrder: " + sampleSearchCriteria.SampleSortOrder); break; }
We kunnen op 2 manieren sorteren: • •
stijgend, door gebruik te maken van Orderby; dalend, door gebruik te maken van OrderByDescending.
Arnout Logghe
Stageverslag
Pagina 115 van 172
Sorteren, doen we door de syntax (tabelnaam => tabelnaam.Veld) We kunnen ook gaan sorteren op meer dan 1 veld, door gebruik te maken van de ThenBy. Wat er dus in feite gebeurt is het volgende: • •
We gaan onze switch gaan doorlopen, op basis van de sampleSearchCrite‐ ria.SampleSortOrder waarde; Bij de juiste case, gaan we de overeenkomstige orderby of orderbydescending met erin het juiste LINQ commando oproepen; o Bij de default, throwen we gewoon een exception.
Wanneer we dit uitvoeren, krijgen we als conversie in SQL: •
Als sampleSearchCriteria.SampleSortOrder gelijk is aan SampleSortOrder.ByEntryDateAsc: ORDER BY [t0].[EntryDate] ASC
6.6.5. Extra Opmerkingen/Issues: Wat te doen bij databasechanges? Soms kan het gebeuren dat een database die we al in een .dbml file hebben gesleept, verandert. Het probleem is dat we in een .dbml file geen refresh optie hebben. Er bestaan toch enkele oplossingen om dit op te lossen, de een al gebruiksvriendelijker dan de andere:
Verwijderen en hertoevoegen van de tabellen die veranderd zijn Deze oplossing is het simpelst. In het dbml venster, verwijderen we alle tabellen waar er wijzigingen aan gebeurd zijn en slepen ze dan terug in ons .dbml venster vanuit de server Explorer. Problemen •
•
Wanneer je al aanpassingen hebt gedaan aan de gegenereerde code van de datacontext, gaat deze verloren. Bv: Wanneer we Sample in het .dbml schema slepen, maakt hij standaard een class met de naam van die tabel aan. Maar in mijn geval, had ik al een class Sample.cs class in de Data Ac‐ cess Layer van mijn project. De naam van de sample class in het .dbml schema, moest dus veranderen naar samples.cs. Wanneer ik nu echter een andere tabel verwijderde en opnieuw in het .dbml schema sleepte, moest ik die naam iedere keer terug aanpassen! Wat bij het niet meer weten van welke aanpassingen je aan welke tabellen hebt gedaan?
Manueel de aanpassingen doen in de .dbml file We kunnen in de .dbml file, met de rechtermuisknop op een property klikken en het verwijderen,
Arnout Logghe
Stageverslag
Pagina 116 van 172
properties toevoegen, enz.… Op deze manier, kunnen we manueel op een gebruiksvriendelijke manier aanpassingen doen (zie fig 9).
Fig 9: We kunnen een waarde uit een .dbml file gaan deleten
Problemen • •
Neemt veel tijd in beslag Î Niet ideaal Wanneer je al aanpassingen hebt gedaan aan de gegenereerde code van de datacontext, gaat deze verloren.
Manueel aanpassingen doen in de code behind Je kunt uiteraard ook de kolommen enzo manueel aanpassen in de designer.cs code, die gegene‐ reerd wordt op basis van de .dbml file. Problemen •
Te complex
Door gebruik te maken van PLINQO Er bestaat een programma PLINQO, dat extra functionaliteit toevoegt aan de LINQ to SQL designers. Dit programma laat onder andere toe om een refresh te doen in de .DBML file. Op volgende website kunnen we meer info vinden over PLINQO en kunnen we het downloaden: http://www.codeplex.com/CodeSmith/Release/ProjectReleases.aspx?ReleaseId=9908
Arnout Logghe
Stageverslag
Pagina 117 van 172
Besluit Om er 100% zeker van te zijn, dat de verwijzingen naar de tabellen in ons .dbml schema overeenko‐ men met die van de database, is het het beste om de eerste werkwijze te gebruiken of te werken met PLINQO. En dus de tabellen die verandert zijn te verwijderen uit ons .dbml schema en ze er terug in te slepen of met PLINQO een refresh te doen. Wanneer er maar 1 kleine aanpassing gebeurt is, kunnen we overwegen om de andere oplossingen ook te gebruiken, maar echt 100% zeker dat hetgeen we aangepast hebben nu overeenkomt met hetgeen dat daadwerkelijk in de Database staat, zijn we dan niet.
Kunnen we werken met verschillende .dbml files? In principe kunnen we met meerdere .dbml files werken, zoals bv een .dbml file per tabel. Dit kan echter wel een probleem opleveren, wanneer er connecties liggen tussen bepaalde tabellen. Wanneer er 2 tabellen in 2 verschillende .dbml files zitten, waartussen normaal een connectie ligt, wordt die connectie niet gegenereerd in code behind. Dit gebeurt alleen maar als de 2 tabellen in dezelfde .dbml file zitten.
Besluit Wanneer je zeker alle referenties naar de connecties uit de database in de code behind wil hebben staan en niet echt weet welke connecties er allemaal zijn, is werken met 1 enkel .dbml file waar je dan alles inzet, het veiligst. We kunnen echter ook gemakkelijk werken met meerdere .dbml files en de tabellen doordacht verdelen. Dit vereist uiteraard wel dat we weten welke tabellen met elkaar verbonden zijn. We kunnen ook bepaalde tabellen in meerdere .dbml files steken. Op die manier, zullen we ook alle connectie referenties in code behind behouden en kunnen we wel bepaalde tabellen gaan samenzet‐ ten in een .dbml‐bestand. Ook gaan we dan een beter overzicht krijgen van welke tabellen bij elkaar horen.
Arnout Logghe
Stageverslag
Pagina 118 van 172
6.6.6. Conclusie We hebben nu een oplossing besproken, om ervoor te zorgen dat we LINQ kunnen gebruiken in de Data Access Layer van een n‐tier architectuur project. Wat zijn nu de voordelen en nadelen van deze oplossing?
Voordelen • •
• •
We hebben een auto complete op query syntax. We krijgen compilatie errors bij database wijzigingen. Hierdoor zullen we ook direct weten dat er iets aan de structuur van de database verandert is, en zullen we gedwongen worden te bekijken wat er verandert is en hoe het ons programma zal beïnvloeden. We zullen op deze manier ook gedwongen worden de wijzigingen door te voeren naar ons .dbml file wanneer er iets verandert aan de Database. We moeten geen gebruik meer maken van stored procedures uit de database om gegevens uit de database op te halen. Je krijgt in de .dbml files ook een grafisch overzicht van de structuur van de tabellen en hun connecties.
Nadelen • •
Wanneer er een database gewijzigd is, moeten we die wijzigingen doorvoeren naar ons .dbml file. Dit neemt tijd in beslag. Er wordt veel overbodige code in de .dbml file genereert, hierdoor wordt meer geheugen in beslag genomen en is het soms moeilijk om de code te begrijpen in de .dbml file.
Arnout Logghe
Stageverslag
Pagina 119 van 172
6.7. Russian Documents 6.7.1. Doelstelling van het probleem: We hebben data uit een database(dit kan een AS/400 of SAP database zijn). Deze data moet worden omgezet in een XML‐bestand. Dit XML‐bestand wordt dan omgezet in een .PDF, Word of Excel‐ bestand (zie fig 1). Alle XML‐bestanden worden ook nog eens opgeslaan in een SQL‐database.
Fig 1: DataFlow van de Russian Documents project.
Problemen • •
Rusland wil die XML‐bestanden ook nog kunnen laten aanpassen door de eindgebruiker. Omzetten van XML naar .pfd/Word/Excel werkt niet.
Mijn opdracht • • •
•
Zoek een invoice/DeliveryNote op Internet in XML met een header en repeatable stukken. Bekijk InfoPath en onderzoek hoe je daarmee XML‐bestanden kunt aanpassen. Kijk hoe je de XML kunt omzetten naar .pdf/Excel/Word (door eventueel gebruik te maken van het project van vorig jaar, zorg er ook voor dat we naar voorgemaakte Excel‐bestanden kunnen omzetten) Het document moet voldoen aan Unicode/multipage/….
Arnout Logghe
Stageverslag
Pagina 120 van 172
We hebben voor deze opdracht, 2 verschillende oplossingen uitgewerk. Dit komt omdat de vereisten van dit project tussentijds gewijzigd zijn. Enkele bijkomende eisen van de opdrachtgevers, zorgden ervoor dat er een tweede dataflow moest uigedacht worden.
Invoice XML‐bestand We gaan volgende XML‐structuur gebruiken voor onze bestanden: 2000-XYZ912345678912345672000-10-01POAB-12348912345678901234562000-09-0106N2.003010USDFOB DestinationShipped via 123 Shipping Co.137.001770.53This invoice references parts ordered as part of Job 1392919. Please direct any inquiries to the project manager for the job.103.53State Sales Tax123-456
Arnout Logghe
Stageverslag
Pagina 121 van 172
Extra wide machine boltxyz-123153.00EA100.000.000.00This item is about to run out of stock, so if you need more, let us know and we can order some.CostCenterCode2939191GLCode2E1ABC Company1234567890123456789012345 123 Easy Street Anytown <StateProvince>NM 99999USA12-3456789(505) 555-5555(505) 555-1222 <EMailAddress>[email protected]ABC Company 123 Easy Street Anytown <StateProvince>NM 99999USA(505) 555-5555(505) 555-1222 <EMailAddress>[email protected]Jane Doe 123 Easy Street MS 0001 Anytown <StateProvince>NM 99999USA(505) 555-5555(505) 555-1222 <EMailAddress>[email protected] <ShipToParty>
Arnout Logghe
Stageverslag
Pagina 122 van 172
Jane Doe 123 Easy Street MS 0001 Anytown <StateProvince>NM 99999USA(505) 555-5555(505) 555-1222 <EMailAddress>[email protected]XYZ Company 456 Hard Times Blvd. Someplace <StateProvince>FL 11111USA98-87654321(555) 555-4444(555) 555-1222 <EMailAddress>[email protected]XYZ Company 456 Hard Times Blvd. Someplace <StateProvince>FL 11111USA(555) 555-4444(555) 555-1222 <EMailAddress>[email protected] <ShipFromParty> XYZ Company 456 Hard Times Blvd. Someplace <StateProvince>FL 11111USA(555) 555-4444(555) 555-1222 <EMailAddress>[email protected]SpecialHandlingInstructionsThe parts in this shipment are fragile and should be handled with care.AttachmentInfo
Arnout Logghe
Zoals je kunt zien, heeft deze XML invoice, een invoiceheader en ook enkele repeatable stukken, waaronder de invoicedetails met de verschillende items.
6.7.2. Oplossing 1 •
• • • • • • • • • • • •
We maken nieuwe Templates aan in InfoPath op basis van die XML‐bestanden die we van SAP/AS400 krijgen. We kunnen deze Templates dan gebruiken om XML‐bestanden in te le‐ zen. Die Templates komen in een nieuwe Document Library map. De XML‐bestanden vormen we om tot een SharePoint XML‐bestand. We maken voor iedere Template, een nieuwe Form Library aan op SharePoint. De XML‐bestanden zetten we in hun corresponderende map op SharePoint(invoices in de invoices map, DeliveryNotes in de DeliveryNotes map, enz….) Er wordt een link van de XML‐bestanden op SharePoint doorgestuurd via mail. De bestanden kunnen aangepast worden en dan opgeslaan. De voorgedefineerde Excel‐bestanden worden aan een Template gekoppeld. Deze Excel‐bestanden worden op SharePoint gezet onder een zelf aangemaakte document library ExcelDocuments. XML‐bestanden kunnen worden omgezet in Excel en dan afgedrukt. We gaan een .xsl aanmaken op basis van een InfoPath Template We gaan die .xsl op SharePoint zetten onder een zelf aangemaakte library TemplatesWord XML‐bestanden kunnen nu geopend worden in word op basis van die .xsl‐bestand.
We maken nieuwe Templates aan in InfoPath op basis van XML bestanden. Nu gaan we voor onze invoice, een nieuw InfoPath Template aanmaken, op basis van het XML‐ bestand. We kunnen deze Template dan gebruiken om invoice XML‐bestanden in te lezen. Voor het aanmaken van een InfoPath Template, zie Bijlage 1: aanmaken InfoPath Template. Voor het definitief InfoPath Template, zie fig 2
Arnout Logghe
Stageverslag
Pagina 124 van 172
Fig 2: Eindversie van ons InfoPath Template voor invoices.
Die Templates komen in een nieuwe Document Library map. Hiervoor openen we SharePoint en klikken we links op documents. Nu krijgen we een lijst van de verschillende document libraries. We klikken hier op nieuw document library toevoegen en voegen een nieuwe document library TemplatesInfoPath toe (zie fig 3).
Arnout Logghe
Stageverslag
Pagina 125 van 172
Fig 3: SharePoint Home page, met links de verschillende document libraries, onder documents.
De XMLbestanden vormen we om tot een InfoPath XMLbestand. Hiervoor openen we het XML‐bestand in de InfoPath Template en dan moeten we een keuzevakje “Always use this form Template for this file” aanvinken. Voor meer uitleg van hoe we een XML‐document openen in een InfoPath Template (zie bijlage 7): In de header van ons XML‐bestand wordt nu volgende code toegevoegd:
Deze code zorgt er nu voor, dat iedere keer we dit XML‐bestand openen, het geopend zal worden in de InfoPath Template.
Arnout Logghe
Stageverslag
Pagina 126 van 172
We maken voor iedere Template een nieuwe Form Library aan op SharePoint. Nu gaan we voor onze Template Invoice, ook weer een nieuwe Form Library invoices aanmaken. Zie hiervoor bijlage 2: Aanmaken Form Library We gaan aan deze Form Library, ook een nieuwe kolom toevoegen, op basis van de invoicenumber. In deze kolom zal dan de invoicenumber worden getoond op SharePoint(naast de andere standaard kolommen zoals documentsnaam, laatste aanpassingen, enz….)
De XMLbestanden zetten we in hun corresponderende map op Share Point. Wat hier ook heel belangrijk is, is dat we het pad van de XML‐bestanden in de header aanpassen, en het laten verwijzen naar de Template op SharePoint. Hiervoor openen we de XML‐bestanden in notepad en passen dan de header die verwijst naar ons InfoPath Template aan en zorgen dat het verwijst naar het InfoPath Template op SharePoint. Bv: ”D:\SOURCESAFE\Spikes\StageArnoutLogghe\Templates&XML\Invoices.xsn” Wordt dan: “http://deceuninckSharePoint/sites/RussiaLegalDocuments/Templates/Invoice.xsn”
Er wordt een link naar deze XMLbestanden doorgestuurd via mail. We gaan de link naar onze XML‐bestanden op SharePoint kopiëren en dan plakken in een nieuwe mail en naar de eindgebruiker doorzenden die deze XML‐bestanden moet aanpassen (zie fig 4).
Arnout Logghe
Stageverslag
Pagina 127 van 172
Fig 4: Doorgezonden link die verwijst naar een XML‐bestand gekoppeld aan een InfoPath Template op SharePoint..
Wanneer we nu op een link klikken, wordt het XML‐bestand uit SharePoint geopend in de browser van deze gebruiker. Het is zeer belangrijk dat deze links geopend worden in Internet Explorer, en niet in Mozilla Firefox. Omdat Mozilla Firefox het openen van een InfoPath Template via doorgezonden link niet onder‐ steund, en hij hier gewoon het XML‐bestand als code opent (zie fig 5).
Arnout Logghe
Stageverslag
Pagina 128 van 172
Fig 5: Link naar XML‐document gekoppeld aan een InfoPath Template op SharePoint dat geopend wordt in Mozilla Firefox.
De bestanden worden aangepast en de wijzigingen opgeslaan op Share Point. De wijzigingen worden direct opgeslaan op SharePoint, omdat we hier met een link direct verwijzen naar het XML‐bestand zelf op SharePoint.
We kunnen nu voorgedefineerde Excelbestanden gaan koppelen aan een Template. Dit doen we door het Excel‐bestand te openen, en dan de developer tab aan te zetten(in de Excel options – Show developer tab in the ribbon). Nu gaan we naar de XML Source, en voegen we hier een nieuwe XML map toe. Als XML map, nemen we hier een XML InfoPath‐bestand van de Template waaraan we het Excel‐bestand willen koppelen (zie fig 6).
Arnout Logghe
Stageverslag
Pagina 129 van 172
Fig 6: Toevoegen van een nieuwe XML map aan een Excel‐bestand.
Wanneer we nu de juiste XML map aan ons Excel document koppelen, krijgen we in de XML source, onze structuur van ons gekoppelde XML‐bestand te zien.
Arnout Logghe
Stageverslag
Pagina 130 van 172
Fig 7: Excel‐bestand, met daaraan een XML source gekoppeld.
We kunnen nu de velden uit deze XML‐source gaan slepen naar de te koppelen velden in ons Excel‐ bestand. Wanneer we nu op import klikken, en een XML InfoPath‐bestand van de Template nemen die we gekoppeld hebben, wordt de data in dit Excel‐bestand ingevuld.
De Excelbestanden worden op SharePoint gezet onder een zelf aange maakte document library. Voor het toevoegen van een document library zie vorige. De Excel‐bestanden worden in dit docu‐ ment libtary geplaatst met upload.
Een XMLbestand in het Excelbestand importeren en afdrukken. Wanneer we nu bijvoorbeeld een voorgedefinieerd Excel‐bestand hebben (zie fig 8) kunnen we in de developer tab op import klikken, en dan een bepaald XML‐bestand selecteren dat in dit Excel‐bestand moet geïmporteerd worden (zie fig 9).
Arnout Logghe
Stageverslag
Pagina 131 van 172
Fig 8: Voorgedefinieerd Excel‐bestand voor invoices.
Fig 9: Een XML‐bestand importeren in dit voorgedefinieerd Excel‐bestand.
Arnout Logghe
Stageverslag
Pagina 132 van 172
Wanneer we nu een XML‐bestand selecteren dat gebaseerd is op het invoice schema, zal de data van dit Excel‐bestand aangepast worden aan ons XML‐bestand. Wanneer we hier echter willen verwijzen naar een SharePoint XML‐bestand, moeten we ofwel via map network drive, naar SharePoint gaan verwijzen, ofwel het XML‐bestand op onze computer opslaan. Want we kunnen alleen maar XML‐bestand importeren door ze te selecteren in het explorer venster hierboven (zie fig 9).
Een .xsl aanmaken op basis van een InfoPath Template en deze op sharepoint zetten. Dit doen we door de Template te openen in design mode, en dan save as source files te selecteren en de juiste map te selecteren waar deze source files moeten worden opgeslaan. Op deze manier, wordt ook een .xls‐bestand gegenereerd op basis van onze Template. Dit .xls‐bestand kunnen we dan later gebruiken om in Word de voorgedefinieerde opmaak van onze InfoPath Template in te voegen.
De XMLbestanden openen in Word op basis van dit .xslbestand. Om een XML‐bestand gebaseerd op een InfoPath Template te importeren in Word, gaan we het .xls‐ bestand van dit XML‐bestand gaan invoegen in Word. Hievoor gaan we ons XML‐bestand openen in Word. De opmaak is nu natuurlijk alles behalve ideaal. We hebben nu echter rechts in ons Word document ook een nieuw venstertje gekregen, XML‐document. Hier kunnen we dan een nieuw .xls‐ bestand gaan toevoegen. We klikken hiervoor op browse en kiezen dan het juiste .xls‐bestand. Elk .xls‐bestand in ons XML‐document venster, bevat een eigen opmaak. Nu kunnen we gaan kiezen welk opmaak we willen gebruiken, door het juiste .xls schematje te selecteren.
Arnout Logghe
Stageverslag
Pagina 133 van 172
Fig 10: Een XML geopend in word en met als grafische voorstelling van de inhoud, de grafische voorstelling die opgeslaan is in DeliveryNote.xls.
6.7.3. Oplossing 2 Oplossing 2 is er gekomen, omdat er ondertussen een aantal vereisten bijgekomen zijn of juist weggevallen waarop de opdracht er opeens heel anders uitzag. Daardoor werd het noodzakelijk om een nieuwe dataflow op te stellen (zie fig 11). Zo was het in feite niet echt noodzakelijk dat de XML‐bestanden in InfoPath werden aangepast, men wilde ze liever direct in Excel aanpassen. Ook moesten de XML‐bestanden niet via mail worden verstuurd naar de gebruikers, enz….
Arnout Logghe
Stageverslag
Pagina 134 van 172
Fig 11: Dataflow van onze 2de oplossing.
Hier gaan we geen gebruik meer maken van InfoPath of SharePoint. Er worden XML‐bestanden genereert op basis van data uit een AS/400 database. Deze XML‐bestanden komen dan in een map terecht. Er wordt een lijst van deze XML‐bestanden bijgehouden in de SQL database.
Mijn opdracht Mijn opdracht bestaat er nu uit, een applicatie te schrijven dat de XML‐bestanden uit deze database toont, met hun belangrijkste data. En wanneer er op 1 van deze XML‐bestanden wordt geklikt, moet dat XML‐bestand via streaming worden geopend in Excel.
Oplossing • • • •
We maken een nieuwe .aspx pagina aan, met een grid dat alle XML‐bestanden toont. We maken een nieuw aspose Excel Template aan. We maken het aspose Excel Template een embedded resource van ons project We maken een nieuwe .aspx pagina aan, die een XML‐bestand krijgt, en dit dan via streaming gaat tonen in het aspose Excel‐bestand.
Arnout Logghe
Stageverslag
Pagina 135 van 172
Een nieuwe pagina met een grid dat alle XMLbestanden toont. We maken een nieuw grid aan waarin we de XML‐bestanden onze database tonen, met hun inhoud(invoicenumber, date, customernumber). Ook moeten we bij ieder XML‐bestand, ook een knop plaatsen OpenExcel. Wanneer we hierop klikken, wordt het XML‐bestand geopend in Excel. (zie fig 12).
Fig 12: Gridview met de inhoud van onze XML‐bestanden uit de database.
De stap van het tonen van de xml bestanden die in de database zitten, laten we hier echter weg, omdat de database nog niet is aangemaakt door het bedrijf en dit slechts een triviale stap is. In plaats hiervan gaan we de XML‐bestanden uit een bepaalde map tonen. De inhoud van een bepaalde map in een array steken, doen we door gebruik te maken van Directory‐ Info.getfiles(). We krijgen dan een file‐info array terug. We kunnen dan file‐info per file‐info gaan inlezen met een XMLtextreader en dan met deze XMLtextreader de data uit deze XML files eruitha‐ len. Deze data slaan we dan op in een DataTable. Die datatable gaan we dan gaan binden aan ons grid. Het ophalen van de data uit onze map en omzetten naar een file‐info array, gebeurt in de page_load. Deze array wordt dan meegegeven aan een method GetDataTablebyXMLreader, die een datatabel met de inhoud van de XML‐bestanden uit deze array teruggeeft. De code van de method die deze datatable teruggeeft ziet er dan als volgt uit: public DataTable GetDataTablebyXMLreader(FileInfo[] fileinfoo) { DataTable tabel = new DataTable("Invoices"); tabel.Columns.Add("InvoiceNumber"); tabel.Columns.Add("Date"); tabel.Columns.Add("CustomerNumber"); tabel.Columns.Add("InvoiceName"); tabel.Columns.Add("Button"); for (int i = 0; i < fileinfoo.Length; i++) { FileInfo fileinfo = fileinfoo[i];
Arnout Logghe
Stageverslag
Pagina 136 van 172
System.XML.XMLTextReader reader = new XMLTextReader(fileinfo.OpenRead()); DataRow rij = tabel.NewRow(); while (reader.Read()) { if (reader.NodeType == XMLNodeType.Element) { switch (reader.Name) { case "InvoiceNumber": rij[0] = reader.ReadString(); break; case "InvoiceDate": rij[1] = reader.ReadString(); break; case "CustomerNumber": rij[2] = reader.ReadString(); break; } } } rij[3] = fileinfo.Name; tabel.Rows.Add(rij); } return tabel; }
In de page_load gaan we deze method gaan oproepen, een datatabel terugkrijgen en deze binden aan het grid: DirectoryInfo dirInfo = new DirectoryInfo(@"C:\WINDOWS\Temp\RussianDocs\XML"); FileInfo[] fileinfoo = dirInfo.GetFiles("*.XML"); DataTable tabel = GetDataTablebyXMLreader(fileinfoo); Grid1.DataSource = tabel; Grid1.DataBind();
In ons grid moeten we dan ook nog zorgen dat onze kolommen overeenkomen met de data uit onze invoice objecten. We hebben daarvoor volgende kolommen nodig:
Arnout Logghe
Stageverslag
Pagina 137 van 172
Er bestaat echter ook nog een andere oplossing, deze houdt in dat we in plaats van met een XMLtextreader in GetDataTablebyXMLreader, zouden werken met een XML document object. Hierbij moeten we dan niet in een switch met de reader.name gaan kijken over welke node het gaat, maar kunnen we een node van een XMLbestand direct aanspreken met XMLDocument.SelectSingleNode. De rest van de code blijft hetzelfde evenals het resultaat in ons grid.
We maken een nieuw aspose Excel Template aan. Soms kan het zijn, dat de pc waarop we deze oplossing laten draaien, geen Excel heeft staan. Daarom gaan we gaan werken met een aspose Excel‐bestand voor het openen van onze XML‐documenten. Aspose.Cells zorgt ervoor dat we een Excel werkblad kunnen aanmaken, zonder dat daarvoor Excel moet geïnstalleerd staan op onze pc. Ook hebben we met Aspose.Cells de mogelijkheid voor het genereren van werkbladen en ondersteund Aspose.Cells het openen en lezen van werkbladen via streams. Wij gaan hier deze ondersteuning gebruiken, en gaan onze XML‐bestanden via streams openen. Om de cellen in ons Excel Aspose document te koppelen aan de data van een XML‐bestand dat we dan via streaming in dit document gaan openen, moeten we een speciale syntax gebruiken (zie fig 13). Verwijzen naar een tag uit XML doen we door &=bovenliggendetag.tegebruikentag in te typen, waar bovenliggende tag verwijst naar de tag boven de te gebruiken tag, en tegebruikentag verwijst naar de tag zelf. We moeten dit voor alle data die we willen koppelen doen, we kunnen op deze manier, ook repeating values uit ons XML‐bestand gaan koppelen. De repeating values zullen dan onder elkaar geplaatst worden op het Excel document.
Arnout Logghe
Stageverslag
Pagina 138 van 172
Fig 13: Aspose Excel document dat we gaan gebruiken om XML‐bestanden in te openen.
De Excel Template als embedded resource in ons project Hiervoor voegen we eerst het aspose Excel Template natuurlijk toe aan ons proejct via add‐ new item. Dan gaan we naar de eigenschappen van dit Excel‐bestand in de solution explorer en hier zetten we de build action op embedded resource (zie fig 14).
Fig 14: De eigenschappen van ons Aspose Excel‐bestand in de solution explorer.
Een pagina die XMLbestanden via streaming opent in Excel. Aan deze .aspx pagina, geven we dan het XML‐bestand mee, waarop we hebben geklikt in ons grid en deze pagina gaat dan dit XML‐bestand met streaming gaan tonen in onze aspose Excel‐bestand. De code ziet er als volgt uit: protected void Page_Load(object sender, EventArgs e) { string naam = Request.QueryString.Get("naam"); ResourceManager resourceManager = new ResourceManager("DemorussiandocsWeb.App_Data.Resources", Assembly.GetExecutingAssembly()); pad = Request.ServerVariables.Get("APPL_PHYSICAL_PATH"); string nummer = System.Guid.NewGuid().ToString(); string padXML = @"C:\Windows\Temp\RussianDocs\XML/" + naam; System.Globalization.CultureInfo oldCI = System.Threading.Thread.CurrentThread.CurrentCulture;
Arnout Logghe
Stageverslag
Pagina 139 van 172
System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US"); DataSet ds = new DataSet(); XMLTextReader reader = new XMLTextReader(padXML); ds.ReadXML(reader); WorkbookDesigner design = new WorkbookDesigner(); design.ClearDataSource(); Stream filestream = Assembly.GetExecutingAssembly().GetManifestResourceStream("RussianDocumentsDemo. Invoice.xls"); design.Workbook.Open(filestream); design.SetDataSource(ds); design.Process(); VulExtraDataIn(design); Stream stream = design.Workbook.SaveToStream(); StreamReader streamreader = new StreamReader(stream); stream.Position = 0; byte[] data = new byte[stream.Length]; int lengte = (int)stream.Length; int aantalstream = streamreader.BaseStream.Read(data, 0, lengte); System.Threading.Thread.CurrentThread.CurrentCulture = oldCI; Response.ClearHeaders(); Response.Clear(); Response.AddHeader("Content-Disposition", "inline; filename=" + System.Guid.NewGuid().ToString()); Response.ContentType = "application/vnd.ms-excel"; Response.BinaryWrite(data); Response.Flush(); Response.End();
File.Delete(padExcel); }
Het pad van ons XML‐bestand, wordt opgebouwd door de naam van ons XML‐bestand, en de standaard locatie van de map met XML‐bestanden. We gaan dit XML‐bestand met een XMLtextrea‐ der gaan inlezen en in een DataSet gaan plaatsen. Dan gaan we een nieuwe stream gaan aanmaken op basis van ons aspose Excel‐bestand, we maken ook een Excel workbook aan met een workbook‐ designer, en we schrijven de stream van ons aspose Excel‐bestand hierin weg. Dit nieuw aangemaak‐ te Excel workbook zal dus identiek zijn aan het workbook in ons Excel aspose document. Nu gaan we aan dit workbook ons XML gaan koppelen die we hebben ingelezen in de dataset. Nu zijn alle gekoppelde cellen in ons Excel workbook ingevuld met de overeenkomstige waarden uit dit XML‐ bestand. We gaan dit workbook gaan opslaan als een stream. Deze stream gaan we gaan converteren naar een byte array data. Uiteindelijk gaan we nu aan ons formulier een nieuwe header toevoegen die verwijst naar Excel en we gaan de bytes uit onze bytearray gaan wegschrijven.
Arnout Logghe
Stageverslag
Pagina 140 van 172
We gaan in deze code ook verwijzen naar de method VulExtraDataIn, deze method gaat ervoor zorgen dat bepaalde cellen in code behind worden ingevuld. Dit is nodig voor de cellen die een waarde krijgen door een berekening, zoals de cellen die de total rate tonen. Deze code ziet er als volgt uit: public void VulExtraDataIn(WorkbookDesigner design) { //sheet.Replace(".",","); Worksheet sheet = design.Workbook.Worksheets[0]; Cell cellns3_SUmtitel = sheet.Cells.FindString("ns3:SUm", null); Cell cellnans3_SUmtitel = sheet.Cells[cellns3_SUmtitel.Row + 1, cellns3_SUmtitel.Column]; Cell celltotaalns3_sum = design.Workbook.Worksheets[0].Cells.FindFormulaContains("=SUBTOTAL(109,K13:K1 3)", null); Cell cellns3_sumlaatstecell = design.Workbook.Worksheets[0].Cells[celltotaalns3_sum.Row - 1, celltotaalns3_sum.Column]; celltotaalns3_sum.Formula = "=SUBTOTAL(109," + cellnans3_SUmtitel.Name + ":" + cellns3_sumlaatstecell.Name + ")"; Cell cellns3_sum = cellnans3_SUmtitel; while(cellns3_sum != celltotaalns3_sum) { int nr = sheet.Cells.Rows[cellns3_sum.Row].Index; Cell cellunitprice = sheet.Cells[cellns3_sum.Row, cellns3_sum.Column - 7]; Cell cellquantity = sheet.Cells[cellns3_sum.Row, cellns3_sum.Column - 5]; Cell lineitemtaxes = sheet.Cells[cellns3_sum.Row, cellns3_sum.Column - 4]; Cell freightcharge = sheet.Cells[cellns3_sum.Row, cellns3_sum.Column - 3]; Cell otherlineitemtaxes = sheet.Cells[cellns3_sum.Row, cellns3_sum.Column - 2]; cellns3_sum.Formula = "=" + cellunitprice.Name + "*" + cellquantity.Name + "+" + lineitemtaxes.Name + "+" + freightcharge.Name + "+" + otherlineitemtaxes.Name + ""; cellns3_sum = sheet.Cells[cellns3_sum.Row + 1, cellns3_sum.Column]; } }
Dit geeft het resultaat dat we willen bekomen (zie fig 15).
Arnout Logghe
Stageverslag
Pagina 141 van 172
Fig 15: XML‐bestand geopend in Excel met streaming
6.7.4. Conclusie We hebben 2 verschillende dataflow’s en dus 2 verschillende oplossingen bedacht om dit probleem op te lossen. De eerste oplossing houdt in dat we XML‐bestanden krijgen en deze omzetten naar InfoPath XML‐ bestanden. Deze bestanden zet men dan op SharePoint en ze worden via mail doorgestuurd naar de eindgebruikers. Ook kunnen we ze importeren in voorgedefinieerde Excel en Word documenten. De tweede oplossing houdt in dat er een database wordt aangemaakt die een lijst bevat van alle XML‐bestanden. Een applicatie toont deze bestanden in een gridview. Wanneer men op 1 van deze bestanden klikt, wordt het XML‐bestand door middel van streaming geopend in Excel. De tweede oplossing is de meest ideale ,voldoet aan alle eisen van de opdrachtgevers en kan meteen geïmplementeerd worden.
6.8. CodeSmith 6.8.1. Doelstelling
Arnout Logghe
Stageverslag
Pagina 142 van 172
Deceuninck maakt gebruik van CodeSmith om klassen te genereren op basis van tabellen uit SQL‐ databases. Er moet onderzocht worden of we CodeSmith ook kunnen laten werken met andere databasesyste‐ men, zoals MS Access of zelfs XML. Dit doe ik aan de hand van het bestaande CodeSmith‐bestand Listgenerator.cst.
Wat is CodeSmith? Het is een Template‐based code generator en kan automatisch grote stukken code genereren. (bv genereren van C# klassen op basis van een SQL‐database)
Het Listgenerator.cst bestand Het listgenerator.cst‐bestand is een CodeSmith‐bestand van het bedrijf (zie fig1).
Fig 1: ListGenerator.cst‐bestand
Bij het openen van het bestand verschijnt bovenstaand scherm. Hier moet je een aantal opties opgeven, zoals de tabel die gebruikt wordt en op welke wijze dit dient te gebeuren. Het voornaamste item is hier toch wel de tabel (sourcetable) die gebruikt wordt als basis om code te genereren. Ook belangrijk is het opgeven van een naam voor onze dalnamespace, entitynamescpace en managerna‐ mespace. Bij het klikken op de knop generate, wordt in het venster rechts de code aangemaakt.
Arnout Logghe
Stageverslag
Pagina 143 van 172
6.8.2. Oplossing •
Viksoe.dk OLEDB provider voor XML wordt gedownload.
•
Daarna stellen we een pad op dat gebruik maakt van deze provider en verwijst naar het XML‐ bestand.
•
We voegen een nieuwe connectie toe, nu naar een XML‐bestand en gaan daarna code gene‐ reren.
Downloaden van Viksoe.dk OLEDB provider voor XML Hiervoor moeten we naar de website http://www.viksoe.dk/code/XMLoledb.htm gaan. We dowloa‐ den deze provider en voeren het install.js‐bestand uit. Nu kunnen we, in de data connectie wizard, ook kiezen tussen de XML oledb provider Viksoe.dk.
Opstellen van een pad Deze provider wordt gebruikt in onze connectie string waarmee we een verbinding maken met ons XML‐bestand. Deze connectie string zullen we nodig hebben wanneer we de tabel ingeven die we gaan gebruiken om code te genereren. De connectiestring ziet er als volgt uit: Provider=Viksoe.dk OLE DB Provider for XML;User ID=Guest;Persist Security Info=False;Data Source=Local;Location=C:\Arnout.XML;Mode=Read;Extended Properties="";XML Root=/;Max String Length=120;Resolve Externals=True;Validate on Parse=True;Protect Missing Tables=False
De provider hier is dus Viksoe.dk, de user‐id is guest en als locatie geven we hier het XML‐bestand C:\Arnout.XML op.
Nieuwe connectie toevoegen en code genereren Wanneer we nu in de sourcetable op ‘ …’ gaan klikken, krijgen we een overzicht van de verschillende Data Sources die al toegevoegd zijn. Wij willen echter zelf een Data Source aanmaken, en dus klikken we ook in dit scherm weer op de knop ‘…’ Nu verschijnt er een scherm waarin we een nieuwe DataSource kunnen aanmaken.Hiervoor moeten we een naam, Provider Type en Connection String opgeven (zie fig 2). We maken hier een DataSource aan met als naam Arnout, als ProviderType ADOXSchemaProvider en als string de Connection String die we hierboven hebben opgesteld.
Arnout Logghe
Stageverslag
Pagina 144 van 172
Fig 2: Toevoegen van een DataSource
Wanneer we nu op OK klikken, staat deze Data Source ook in onze lijst van Data Sources. Wanneer we deze nu selecteren, zien we de verschillende tabellen die ertoe behoren (zie fig 3).
Fig 3: XML Data Source met de verschillende tabellen in de XML Data Source.
We selecteren nu de tabel Order, en klikken dan op Select.
Arnout Logghe
Stageverslag
Pagina 145 van 172
Fig 4: Ingevuld Listgenerator.cst bestand.
Nu moeten we alleen nog maar een naam geven aan de DalNamespace, de EntityNamespace en ManagerNameSpace, zodat de code gegenereerd wordt. Wanneer we dit nu echter uitvoeren, komt er een foutmelding. Waarschijnlijk ligt dit aan de code behind van de DAL.ListGenerator.cst file, en niet aan de Data Source. De DAL.ListGenerator.cst werkt met stored procedures en verwacht keys terwijl het XML‐ bestand dit niet heeft. Het doel van dit project was te werken met XML in CodeSmith en deze doelstelling is gehaald want het XML‐bestand (met tabellen) wordt herkend. Deze foutmelding is dus niet echt belangrijk voor het welslagen van het project.
6.8.3. Conclusie We kunnen in CodeSmith een DataSource aanmaken op basis van een XML‐bestand en op basis hiervan automatich code laten genereren. Voorwaarde is wel dat we eerst een OLEDB provider voor XML zoals Viksoe.dk installeren.
Arnout Logghe
Stageverslag
Pagina 146 van 172
7. Zelfreflectie Hoewel mijn stageopdracht ‐ in tegenstelling tot wat de meeste studenten moeten uitvoeren ‐ niet toegespitst was op één onderwerp met daaraan een analyse‐ en programmeeropdracht gekoppeld, maar eerder een aaneenschakeling was van verschillende kleinere researchopdrachten was het in elk geval een zeer boeiende ervaring die mijn kennis enorm verruimd heeft. In het begin was het werken op deze manier wel een hele aanpassing. Ik ben gewend om altijd in dezelfde programmeeromgeving te werken, terwijl ik nu dus vaak moest kunnen overschakelen van één bepaald programma naar een ander. Toch was het nu juist deze afwisseling die mijn stage enorm interessant maakte. Door het werken met al deze verschillende softwarepakketten, heb ik veel bijgeleerd in relatief korte tijd. Op verschil‐ lende gebieden heb ik ervaring opgedaan. Ik heb kunnen kennismaken met vele aspecten van IT en niet enkel met puur programmeren en analyses maken. Ook gaf het afwerken van een researchop‐ dracht meestal veel voldoening, vooral als je zag dat ze effectief werd gebruikt in het bedrijf. Soms is de bespreking van mijn stageopdrachten iets te veel uitgewerkt en gedocumenteerd. Dit komt omdat ik de neiging heb om alles zo grondig en correct mogelijk uit te leggen, waarbij ik niet altijd de hoofdlijnen van de details weet te scheiden. Het was waarschijnlijk beter geweest om wat vaker een algemeen overzicht van een oplossing te geven in plaats van de details ervan te bespreken. Verder kan ik toevoegen dat het belangrijk is om zelfstandig te kunnen werken. Daarbij mag je natuurlijk niet gaan overdrijven. Zo had ik vaak de neiging om een researchopdracht helemaal alleen uit te zoeken zonder al te veel hulp of uitleg te vragen. Hoewel ik hierdoor enorm veel heb bijge‐ leerd, is het vaak efficiënter om voor sommige problemen wat vlugger hulp te vragen aan collega’s die toch veel meer ervaring hebben. Soms heb ik hierdoor te veel tijd verloren. Tijd die nuttiger besteed kan worden aan nieuwe opdrachten. Een gezonde teamgeest is dus even belangrijk als zelfstandig werken. Het werken in teamverband viel echter heel goed mee, zelfs voor een volslagen individualist zoals mezelf. Deceuninck is een bedrijf met een zeer aangename werksfeer. Ik werd er ook niet echt als stagiair behandeld, maar eerder als lid van het team. Dit heeft mij aangenaam verrast en ook gemotiveerd om mijn stage zo goed mogelijk af te ronden. Ik kon ook overal terecht wanneer ik hulp nodig had. Af en toe kreeg ik ook opdrachten die bedoeld waren om mijn kennis te vergroten of iets bij te leren. Soms was dat in de vorm van lectuur (zoals het boek “The Pragmatic Programmer”) en op andere momenten dan weer als onderdeel van het project zoals bij de gridview Sorting Problem, waar ik me eerst moest verdiepen in de architectuur van de programma’s. Tijdens mijn stage moest ik ook af en toe rapport uit brengen door middel van een presentatie. Op deze manier bleven mijn stagementoren op de hoogte van mijn vorderingen zodat er ook tijdig feedback kon gegeven worden.
Arnout Logghe
Stageverslag
Pagina 147 van 172
Die presentaties waren dus bij uitstek geschikt om uitleg te geven over mijn manier van werken en om dieper in te gaan op bepaalde onduidelijkheden of problemen. Deze voorstellingen waren soms verbonden aan een deadline, wat mij dwong om alles tijdig af te werken (Iets wat jammer genoeg niet altijd gelukt is). Daarnaast heb ik met heel veel nieuwe technologieën moeten werken, meestal zaken waar ik amper van gehoord had, maar die in de IT sector en in de bedrijfswereld wel frequent gangbaar is. Software zoals Selenium, Resharper, cruiscontrol.NET, Componentart maar ook LINQ zal in de toekomst nog vaak gebruikt worden in vele bedrijven. In deze optiek zou het interessant zijn als de school deze software zou behandelen of toelichten in het lessenpakket. Dit kan het niveau van de richting alleen maar opwaarderen en een meerwaarde betekenen voor het bachelordiploma. Ook het werken voor een multinational, in een internationale omgeving was nieuw voor mij. De omvang van het bedrijf, de grote hoeveelheid informatie en de vele nieuwe indrukken die dit met zich meebrengt kwam in het begin eerder overdonderend over. Hiermee leren omgaan op zich was al een heel leerrijke ervaring en ik kan dan ook mijn stagementoren niet dankbaar genoeg zijn, dat ze me ook op dit vlak de nodige ondersteuning geboden hebben.
http://msdn.microsoft.com/en‐us/vs2005/aa718670.aspx MSBuild http://msdn.microsoft.com/en‐us/library/0k6kkbsd.aspx PLINQO http://weblogs.ASP.NET/pwelter34/archive/2007/08/10/pLINQo‐codesmith‐LINQ‐to‐sql‐ Templates.aspx Auteurs: ANDREW HUNT, DAVID THOMAS, The Pragmatic Programmer: From Journeyman to Master, 1ste druk, Addison‐Wesley Professional, 75 Arlington Street Boston, 1999 TestComplete http://weblogs.ASP.NET/pwelter34/archive/2007/08/10/pLINQo‐codesmith‐LINQ‐to‐sql‐ Templates.aspx Selenium IDE http://Selenium‐ide.openqa.org/ Selenium Remote Control http://Selenium‐rc.openqa.org/ SharePoint http://office.microsoft.com/nl‐nl/SharePointserver/default.aspx Spysmith http://agilethinking.NET/qualityforge/Spysmith/index.HTML Watir/FireWatir
Arnout Logghe
Stageverslag
Pagina 150 van 172
http://wtr.rubyforge.org/
Arnout Logghe
Stageverslag
Pagina 151 van 172
9. Bijlagen – documenten • • • • • • • •
Bijlage 1 : Een InfoPath Template aanmaken Bijlage 2 : Een Form Library aanmaken op SharePoint Bijlage 3 : De destination van de Template instellen op SharePoint Bijlage 4 : Een Template doorzenden via mail Bijlage 5 : Data Uit SharePoint converteren naar Excel Bijlage 6 : Installatie van Buildserver Bijlage 7 : Een XML‐bestand openen in een InfoPath Template Bijlage 8 : Beveiliging en machtigingen in SharePoint 2003
Arnout Logghe
Stageverslag
Pagina 152 van 172
9.1. Bijlage 1: Een InfoPath Template aanmaken Op basis van een XML‐bestand, wordt een InfoPath Template aangemaakt. Een InfoPath Template is in feite een XML‐Template, waarmee ook de grafische visualisatie van het in te vullen of aan te passen XML‐bestand kan worden ingesteld. Een nieuwe Template wordt aangemaakt door InfoPath op te starten en dan te kiezen voor een nieuwe InfoPath Template op basis van een bestaand XML‐bestand. We noemen deze Template bijvoorbeeld TravelRequest.XML. Nu verschijnt er leeg InfoPath Template, met in het DataSource venster de structuur van het XML‐ bestand. De velden uit het DataSource venster kan men nu in het formulier slepen. Daarnaast kan ook de opmaak van de Template gewijzigd worden. Dit gebeurt op een identieke manier als bij de andere Microsoft Office applicaties. Naast de opmaak, kunnen ook de veldeigenschappen ingesteld worden. Zo kun je bijvoorbeeld instellen, dat een bepaald veld in feite meerdere data bevat, en dus een soort van tabel is. Hiervoor moet repeating aangevinkt worden in de eigenschappen van dat veld (zie fig 1).
Fig 1: Veldeigenschappen van het veld TrainTicket
Naast de velden die uit de DataSource opgehaald worden, kunnen er ook nieuwe gecreëerd worden. Je kan dan hieraan ook formules toekennen ; die velden zullen dan worden ingevuld op basis van deze formules (zie fig 2).
Arnout Logghe
Stageverslag
Pagina 153 van 172
Fig 2: Veldeigenschappen van het veld TotaalAirTickets
In bovenstaand scherm zie je bijvoorbeeld het ‐zelf aangemaakt‐ veld TotaalAirTickets, dat als waarde de som van de vliegtickets zal krijgen. Het vakje “update this value when the result of the formula is recalculated” moet worden aangevinkt zodat de waarde van dit veld automatisch wordt aangepast wanneer de tarieven van de luchtvaart‐ maatschappijen wijzigen. Je kunt ook instellen dat een bepaald veld read‐only is; de inhoud kan dan niet worden aangepast door de gebruiker. Na het verslepen, maken, aanpassen en opmaken van alle velden krijg je dan een bruikbaar resultaat (zie fig 3).
Arnout Logghe
Stageverslag
Pagina 154 van 172
Fig 3: InfoPath form die wij gaan gebruiken.
Rentalcars, Remarks en RentalCasrAlternatives zijn hier repeating values. Hetzelfde geldt voor de overeenkomstige velden van Airtickets, Traintickets en Hotel Reservations. Ook Travellers is een repeating value. in Total rates onder de repeating values, komt de som van de rates van die repeating value. Aan de ‐zelf aangemaakte ‐ velden Total estimated travel cost en More economic travel costs, wordt een formule toegekend om alle Total rates te gaan optellen: sum(TotaalTrainTickets) + sum(TotaalAirTickets) + sum(TotaalReservations) + sum(TotaalRentalCars).
Arnout Logghe
Stageverslag
Pagina 155 van 172
9.2. Bijlage 2: Een Form Library aanmaken op SharePoint. Op basis van een InfoPath Template, kan een Form Library aangemaakt worden. Wanneer de gebruiker een Template invult en de gegevens doorzendt, komen de gegenereerde XML‐bestanden in deze Form Library terecht. Aanmaken van een dergelijke Form Library gebeurt door in de design mode van de InfoPath Template te klikken op Publish. Vervolgens kiest men to a SharePoint server en geeft men de naam van een SharePoint server op als plaats waar alles moet terechtkomen. Met de Publishing Wizard kan je nu extra kolommen gaan toevoegen aan de Form Library, op basis van waarden uit de InfoPath Template. (Wij voegen hier een nieuwe kolom Final Destination toe, op basis van de corresponderende waarde uit de Template) (zie fig 1).
Fig 1: Publishing Wizard
Op SharePoint is er nu een nieuwe Form Library map aangemaakt (zie fig 2).
Arnout Logghe
Stageverslag
Pagina 156 van 172
Fig 2: Travel Requests map op SharePoint
In de standaardview kunnen de XML‐bestanden niet in een andere map worden gezet, maar er bestaan nog andere views, zoals Explorer View. Deze weergave lijkt op Windows Verkenner en hierin kunnen we wel XML‐bestanden naar andere mappen slepen (zie fig 3).
Fig 3: Travel Requests map in de Explorer View.
Arnout Logghe
Stageverslag
Pagina 157 van 172
9.3. Bijlage 3: De destination van de Template instellen Een Template moet steeds naar de juiste Form Library op SharePoint verzonden worden. Dit gebeurt door de Submit‐options in te stellen. Hiervoor opent men de Template in design mode en kiest men – tools – submit options.
Fig 1: Instellen van de submit options van een InfoPath Template
De vakjes “allow users to submit this form” en “send form data to a single destination” worden aangevinkt en de SharePoint Document Library wordt gekozen (zie fig 1). Nu kan men op Add klikken om de Data Connection Wizard te doorlopen (zie fig 2).
Arnout Logghe
Stageverslag
Pagina 158 van 172
Fig 2: Data Connection Wizard
In deze wizard geef je een Form Library op op SharePoint en eventueel een bestandsnaam voor je XML‐bestand (Dit kan ook een concatenatie zijn van verschillende velden en strings). In bovenstaand voorbeeld is de bestandsnaam een concatenatie van: “TravelRequest”, de datum van vandaag, “Destination” en de waarde finaldestination. De Wizard kan nu verder doorlopen worden, zonder al te veel instellingen te veranderen door steeds op volgende te klikken. Wanneer je daarna de Template invult, komt het gegenereerde XML‐document toe in de juiste map op SharePoint.
Arnout Logghe
Stageverslag
Pagina 159 van 172
9.4. Bijlage 4: Een Template doorzenden via mail. Om een Template uit SharePoint door te zenden via mail, moet: het pad naar deze Template gekopieerd worden; daarna wordt het in een emailbericht geplakt en verzonden naar de gebruiker (zie fig 1).
Fig 1: link die verwijst naar een Template op SharePoint doorgezonden via mail
Bij klikken op deze link geklikt wordt, wordt er een lege Template geopend.
Arnout Logghe
Stageverslag
Pagina 160 van 172
9.5. Bijlage 5: Data uit SharePoint Converteren naar Excel Klik in de Form Library op Edit. De view verandert in een view die lijkt op Excel, met hier en daar enkele nieuwe knoppen, zoals task pane. Hierop klikken opent een nieuw venster met de knoppen: export to access, query list with Excel, enz… (zie fig 1)
Fig 1: Data uit Travel Requests voorgesteld in een DataSheet.
Door te klikken op Query list with Excel, wordt de mapinhoud naar Excel overgezet. Ook de inhoud van een onderliggende map kan hieraan toegevoegd worden. Het Import Data venster verschijnt in Excel. (zie fig 2).
Arnout Logghe
Stageverslag
Pagina 161 van 172
Fig 2: Import Data Scherm in Excel.
Hier kan je opgeven waar de mapinhoud wordt ingevoegd in Excel. Zo kan je het bijvoorbeeld als een tabel in de bestaande worksheet importeren.
Arnout Logghe
Stageverslag
Pagina 162 van 172
9.6. Bijlage 6: Installatie van Buildserver Eerst misschien een korte uitleg over het opzetten van een eigen buildserver.
9.6.1. Installatie Hier volgen de verschillende stappen, die moeten gebeuren om CruiseControl te installeren: •
• • •
• • • • •
vooraleer men CruiseControl kan installeren, moet men eerst Windows 2003 Service Pack 1 installeren. Dit kan gedownload worden via de site van Windows. De regionale setting is En‐ gels‐US en je moet checken of de Server op het juiste domein staat (hier dus deceunin‐ ck.com); in het Control Panel, verwijder je eerst de software Internet Explorer Enchanced Security Configuration; daarna moet een proxy client geïnstalleerd worden. (tenzij dit al vroeger gebeurd is); nu kan men een Windows Update uitvoeren, en alle belangrijke componenten gaan installe‐ ren. Sommige van deze componenten zijn nodig om CruiseControl goed te laten functione‐ ren; er moet worden gezorgd dat Remote Desktop mogelijk is op de plaatselijke pc. Dit kan je doen door in de System properties “Enable Remote Desktop on this computer” aan te vinken; daarna wordt het account waarmee we gaan inloggen op de buildserver ( in dit geval Web‐ [email protected]) toegevoegd aan de local administrators Group; ook moet ervoor georgd worden dat Systool geïnstalleerd staat op ons systeem; om ervoor te zorgen dat het systeem veilig is, en beschermd tegen virussen, kan je best Anti‐ virussoftware downloaden en installeren; uiteraard moet SourceSafe ook geïnstalleerd staan, want de buildserver gaat in SourceSafe op zoek naar de laatste versie van de projecten. (Hiervoor heeft het bedrijf een cd‐rom).
Na al deze stappen doorlopen te hebben, kan CruiseControl.NET zelf geïnstalleerd worden. De laatste versie 1.3 kan je downloaden via http://confluence.public.thoughtworks.org/. De installatie gebeurt via een Installatiewizard . We selecteren de componenten die we nodig hebben (zie fig 1).
Arnout Logghe
Stageverslag
Pagina 163 van 172
Fig 1: Aanvinken welke componenten we willen installeren bij de installatie van CruiseControl.
Bij de additional configuration, wordt CC.NET server als een Windows service geïnstalleerd en een virtual directory aangemaakt in ISS De andere instellingen moeten niet gewijzigd worden; enkel nog op finish klikken, en de buildserver is geïnstalleerd.
9.6.2. Configuratie Voordat de buildserver optimaal kan werken, moeten er nog enkele configuratie instellingen goed gezet worden. Eerst wordt de service CruiseControl.NET Server zodanig ingesteld, dat hij automatisch opstart. Dit gebeurt via de services.msc console. Ook in de .config file van CruiseControl.NET moeten nog een aantal aanpassingen gebeuren, zoals het toevoegen van “” aan “”. De service start nu nog niet automatisch, maar wordt manueel opgestart via de services.msc. Soms moet je ook nog Web Dashboard installeren.
9.6.3. Toevoegen van een project aan de buildserver
Arnout Logghe
Stageverslag
Pagina 164 van 172
Om een project te kunnen toevoegen aan de buildserver, moeten we eerst en vooral een verwijzing naar het project plaatsen in het CCNet.config‐bestand. <project name="D:\SOURCESAFE\Spikes\StageArnoutLogghe\Stage.ArnoutLogghe.TestenProbl emComponentart\Stage.ArnoutLogghe.TestenProblemComponentart" />
Daarna kan het worden toegevoegd aan de buildserver. We openen hiervoor CCTray, en kiezen bovenaan file‐settings. In het venster zien we een overzicht van alle projecten op de buildserver. We kunnen hier ook een nieuw project aan toevoegen of verwijderen. Wij klikken op add om een nieuw project toe te voegen en krijgen een overzicht van de bestaande servers . Je kan ook een server toevoegen via volgend scherm (zie fig 2).
Fig 2: Connecteren met een buildserver in CCNet.
Hier geef je de wijze van connecteren op. (In dit voorbeeld connect directly using .NET remoting). In CCTray verschijnt een overzicht van de bestaande servers. (zie fig 3).
Arnout Logghe
Stageverslag
Pagina 165 van 172
Fig 3: Overzichtje in CCTray van de verschillende buildservers en hun projecten.
Nu kunnen we een project toevoegen aan de CCTray en het vervolgens laten builden. Hier verschijnt ook info over de volgende build (zie fig 4).
Fig 4: CCTray, met de verschillende projecten.
Klik op deze info en er verschijnt een scherm met gedetailleerde informatie over de verschillende builds. (zie fig 5).
Arnout Logghe
Stageverslag
Pagina 166 van 172
Fig 5: Venster met meer info over de verschillende builds.
Om bovenstaand browservenster te kunnen gebruiken, moeten we zorgen dat Web Dashboard goed geïnstalleerd is, en dat de ASP.NET versie van CCNet 2.0 is. Om hiervoor te zorgen, gaan we naar de IIS manager. In de properties van CCNet stellen we de ASP.NET versie in op 2.0.50727.
Arnout Logghe
Stageverslag
Pagina 167 van 172
9.7. Bijlage 7: Een XMLbestand openen in een Template. Om een XML‐bestand te koppelen aan een bepaalde InfoPath Template kiezen we file – open. We selecteren het XML‐bestand en daarna de Template (zie fig 1).
Fig 1: Selecteren van een Form Template.
Wanneer we nu op OK klikken, wordt de Template opgevuld met het XML‐bestand (zie fig 2).
Fig 1: InfoPath Template opgevuld met data uit een XML bestand
We kunnen nu op elk moment ook de Template opnieuw laten opvullen met een ander XML‐ bestand.
Arnout Logghe
Stageverslag
Pagina 168 van 172
9.8. Bijlage 8: Beveiliging en machtigingen in SharePoint Het beveiligen van de bestanden gebeurt op mapniveau binnen SharePoint. Bij iedere map/document library/Form Library, kan men de permissies instellen. Dit doen we via Modify Settings and columns – change permissies for this Form Library. Nu verschijnt een scherm met de verschillende gebruikers/groepen (zie fig 1). Hier kunnen we oa gebruikers toevoegen.
Fig 1: Veranderen van permissies van Travel Requests.
Door erop te klikken, verschijnt een nieuw scherm, met een keuzelijstje, waarin we machtigingen kunnen instellen.(Items: “View items”, “View,insert,delete items”, enz…….) We kunnen ook advanced persmissies instellen (kan de gebruiker check‐outs cancellen, kan hij listitems zien, kan hij lists beheren, enz…..) Via change anonymous access, kunnen we instellen welke rechten de anonymous user heeft op deze map. Via manage request access, kunnen we instellen, of gebruikers toegang kunnen aanvragen tot deze Document Library.
Arnout Logghe
Stageverslag
Pagina 169 van 172
10.
Colofon
Deze versie van het stageverslag en de bijhorende documenten werd uitgewerkt met: • •
Intel Pentium M 1,50 Ghz Microsoft Office 2003 en 2007
Afbeeldingen zijn afkomstig van: •
Deceuninck NV :
http://www.deceuninck.com
Gebruikte lettertypes in dit document: • • • • • • • • • • •
Titel Voorkaft: Titel eerste niveau: Titel tweede niveau: Titel derde niveau: Tussenkop eerste niveau: Tussenkop tweede niveau: Tussenkop derde niveau: Tussenkop vierde niveau: Doorlopende tekst: Verklarende tekst bij figuren en tabellen: Code Voorbelden:
Tahoma – vet – 48 px Cambria – vet – 20 px Cambria – vet – 16 px Cambria – vet – 14 px Cambria – vet cursief – 14 px Cambria – vet cursief – 13 px Times new Roman – vet – 13 px Calibri – vet onderstreept – 11 px Calibri – standaard – 11 px Calibri – standaard – 9 px Courier New – standaard – 10 px
Datum van voltooiing (laatste aanpassing) : 11/06/2008