Migreren naar ASP.NET deel II
Door Michiel van Otegem
30 juni 2003
Door de komst van het .NET Framework en ASP.NET, denk je mogelijk aan het migreren
van je applicaties. In deel I van dit artkel heb ik behandeld wat de voordelen zijn
van migratie. De basis redenen zijn prestatie, stabiliteit, beveiliging en snel
kunnen ontwikkelen. Het opnieuw bouwen van jouw applicatie in ASP.NET zal je uiteindelijk
veel meer opleveren dan migreren, maar migratie maakt het mogelijk om je applicatie
in fases opnieuw te bouwen. In dit tweede en laatste deel van dit artikel, zal ik de
stappen behandelen die nodig zijn om te migreren.
Als je optimaal gebruik wilt maken van wat ASP.NET te bieden heeft, dan zit er
niks anders op dan het compleet herontwerpen en opnieuw bouwen van je applicatie.
Vanwege het verschil in programmeermodel en architectuur zullen de overwegingen
die gemaakt zijn bij het ontwerpen van je applicatie in ASP waarschijnlijk zijn
verdwenen. Neem bijvoorbeeld sessie ondersteuning. Als je een grote applicatie hebt
gemaakt, dan heb je waarschijnlijk geen gebruik gemaakt van het Session object in
ASP, en heb je een eigen sessie systeem gemaakt. In ASP.NET is het Sessie object
volledig omgegooid, zodat je geen eigen oplossing hoeft te bouwen, ook als je
website door een web farm uitgevoerd wordt. Het is zelfs zo dat een eigen oplossing
de prestaties van de website eerder schaadt in plaats van helpt. Veel van de
veranderingen en toevoegingen zullen beslissingen die je in ASP hebt genomen in
een ander daglicht stellen.
De grootste verandering naar ASP.NET is dat het volledig object georiėnteerd
en control gebaseerd is. De vele standaard controls die ASP.NET biedt en de
mogelijkheid om zelf nieuwe controls te maken betekenen dat je totaal anders moet
denken over hoe je applicaties bouwt. Dat gezegd hebbende zal je waarschijnlijk
niet zitten te springen om een applicatie helemaal opnieuw op te bouwen in plaats
van deze te migreren. Toch is dat zeker het overwegen waard, omdat ASP.NET de
ontwikkeltijd van een applicatie dramatisch verminderd vergeleken met ASP, en als
je de gehele applicatie opnieuw bouwt, heb je waarschijnlijk een veel beter
product dan met puur en alleen migreren.
ASP.NET en ASP pagina“s kunnen in dezelfde directory draaien, ze zullen zich
echter gedragen als twee totaal aparte applicaties. Beiden hebben een apart
Applicatie en Session object enzovoort. Informatie delen met het Session object
in een ASP applicatie met het Session object in ASP.NET is niet mogelijk zonder
een aanzienlijke omweg (zie
How to Share Session State Between Classic ASP and ASP.NET). Het is ook zo
dat ASP applicaties de extensie .asp gebruiken en ASP.NET applicaties de .aspx
extensie, door de vele verschillen tussen de twee. Omdat het programmeermodel van
ASP.NET veel verschilt met ASP, zou je denken dat migreren niet mogelijk is.
Hoewel het inderdaad niet zo eenvoudig is als migreren van ASP 2.0 naar ASP 3.0,
of zo simpel als het hernoemen van de bestanden naar .aspx. Extensies, ondersteunt
ASP.NET nog steeds een niet object georiėnteerd model wat erg lijkt op hoe ASP
werkt. De verschillen tussen deze 'mode' van ASP.NET en ASP manifesteert zichzelf
in een paar dingen, die hieronder besproken zullen.
Programmeertaal ondersteuning
Het .NET Framework, en dus ASP.NET, ondersteunt veel verschillende talen. De
talen die standaard ondersteund worden zijn VB.NET, JScript.NET en C# (en vanaf
versie 1.1 ook J#, de Java-taal). Andere talen verkrijgbaar als third party plug-ins
zijn Perl, Cobol, Fortran en vele anderen. Omdat ASP standaard alleen VBScript en
Jscript ondersteunt, richt ik me op het migreren van deze twee talen naar VB.NET
en Jscript.NET. Jscript is niet veel veranderd, behalve dat alle talen in .NET
(dus ook JScript) volledige getypeerd zijn. VBScript wordt echter niet langer
ondersteund en is vervangen door zijn grote broer Visual Basic. Dit is VB.NET,
wat een aantal significante syntax veranderingen heeft ten opzichte van VB 6 en
VBScript.
Als je Option Explicit al niet gebruikte en al jouw variabelen al declareerde,
dan moet je dat vanaf nu in ieder geval wel doen. Option Explicit staat aan
standaard aan, en als je dat herhaalt dan krijg je een foutmelding. Dus als je
Option Explicit gebruikte
.verwijder die opdracht. Als je dat niet deed
.dan wacht
je de leuke klus van het declareren van AL je variabelen met Dim. In tegenstelling
tot VBScript, is VB.NET volledig getypeerd, wat betekent dat je het data type van
een variabele kan specificeren. Je kan een variabele nog steeds declareren zonder
een type, maar hoe strikter je typeert hoe beter. Hier moet je erg aan wennen,
maar omdat je het niet hoeft te doen, kan je er langzaam aan in rollen. Ik raad
juist ook aan om niet meteen te types toe te kennen als je een pagina migreert.
Zorg eerst dat de code werkt, ga dan types toekennen aan variabelen. De reden hiervoor
is dat als je een foutief type toekent, bijvoorbeeld door een Long in plaats van
een Integer te gebruiken, je code niet zal werken en uitzoeken wat er fout ging
niet altijd makkelijk is. VB.NET is veel lastiger als het aankomt op impliciete
conversie omdat in alle variabele types in essentie objecten zijn. Dit vergt ook
wat gewenning. Omdat alle variabele types hetzelfde zijn, is het Set keyword niet
meer nodig om een object aan een variabele toe te wijzen. Je kan gewoon schrijven:
ObjX = Server.CreatObject("ADODB.Connection")
Als je het Set keyword toevoegt, treedt er een fout op in ASP.NET.
Een laatste belangrijke verandering is dat als een Sub aanroept de parameters
tussen haakjes moeten staan, net zoals bij een Function. Als je net zoals ik
werkt, dan ben je blij met deze verandering, en heb je voorheen het Call statement
gebruikt in VBScript, wat nu overbodig is (maar nog wel kan). Er zijn meer
veranderingen in VB.NET, maar de meeste zijn niet belangrijk bij het migreren.
Een complete lijst is te vinden op
http://www.4guysfromrolla.com/webtech/053001-1.shtm.
Een verandering die van invloed is op alle programmeertalen is dat standaard
eigenschappen en methoden van objecten niet langer ondersteund worden. Als je
ASP.NET pagina“s maakt vanuit niets, is het niet zo lastig, omdat het in de meeste
gevallen als een voordeel werkt omdat je code veel makkelijker te lezen is. Als je
migreert is het erg onhandig, omdat de meeste van ons gewoon
rs("fieldname")
gebruiken om een waarde uit een veld in een ADO Recordset (en gelijksoortige operaties
bij objecten) te krijgen. Dit werkt niet meer en moet vervangen worden door
rs.Fields("fieldname").Value
Script Blokken
In ASP ben je gewend gebruik te maken van <% en
%> rondom je ASP script. Hoewel dit nog steeds
mogelijk is voor een groot deel van je script, wordt er aanbevolen dat je ze
alleen gebruikt als het echt nodig is. In plaats daarvan moet je script blokken
gebruiken, in ieder geval voor functies en procedures.
<script language="VB" runat="server">
Function Add(A As Long, B As Long) As Long
Return A + B
End Function
</script>
Let op het runat="server" attribuut, wat ASP.NET vertelt dat het script blok
uitgevoerd moet worden op de server. Als je dit niet toevoegt, wordt het script
niet uitgevoerd en gestuurd naar de client.
De declaratie bovenaan elke pagina is ook enigszins veranderd en heeft meer opties
gekregen (die ik niet zal bespreken). Wees er zeker van dat je de juiste taal
specificeert. Voor VB ziet de declaratie er bijvoorbeeld als volgt uit:
<%@ Page LANGUAGE="VB" %>
Noot: De waarde van het Language attribuut kan ook VBScript zijn, omdat daarmee ook
verwezen wordt naar VB.NET.
Vanwege deze declaratie en de taal attributen van de script blokken, zou je denken
dat je meerdere talen in een pagina kan gebruiken, net zoals in ASP. Maar
helaas.
Bij de eerste versie van ASP.NET is dit niet mogelijk (in toekomstige versies is
het mogelijk dat deze beperking wordt verwijderd). Je kan echter creėren en bouwen
op objecten die gemaakt zijn met een andere taal, maar als je migreert is het niet
waarschijnlijk dat je dit tegenkomt. Als je ASP pagina“s hebt die meerdere talen
gebruiken, dan wordt het erg lastig om deze te migreren. Een oplossing is om de
pagina op te delen in User Controls, omdat de taal waarmee een User Control werkt
anders mag zijn dat de pagina die de User Control gebruikt.
.NET Objecten
Zoals ik eerder gezegd heb, zijn de talen die in ASP.NET gebruikt worden volledig
getypeerd. Je kan een type alleen van een variabele definiėren als het type bekend
is tijdens de implementatie. Dit geldt voor de basis types en veel van de types
die al verkrijgbaar zijn als je een ASP.NET pagina maakt. Dus in plaats van een
variabele te declareren zonder type, kan je bijvoorbeeld een variabele van het
type Integer declareren als volgt:
[VB.NET]
Dim I As Integer
[JScript.NET]
var I : int;
Omdat je een type specificeert, kan ASP.NET geheugen toewijzen voor elke variabele
als hij gedeclareerd is, en spaart zo geheugen en prestatie. ASP.NET en het .NET
Framewrok bevatten tal van objecten, voor veel gewone opdrachten. Het bevat o.a.
een compleet omgegooide ADO voor database toegang (genaamd ADO.NET), omgegooide
XML ondersteuning en veel meer. Deze objecten zijn gestructureerd in een hiėrarchie
die aangesproken kan worden door Namespaces te gebruiken. In plaats van Server.CreateObject
te gebruiken om een van de objecten te maken, importeer je zijn Namespace, en
gebruik je vervolgens het New Keyword om het te instantiėren, zoals te zien is in
onderstaande code:
[VB.NET]
<%@ Page LANGUAGE="VB" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.OleDb" %>
<%@ Import Namespace="System.Xml" %>
<script runat="server">
Sub Page_Load(Sender As Object, E as EventArgs)
Dim oConn As OleDb.OleDbConnection
Dim oDs As DataSet
oDs = New DataSet()
...
End Sub
</script>
[JScript.NET]
<%@ Page LANGUAGE="JScript" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.OleDb" %>
<%@ Import Namespace="System.Xml" %>
<script runat="server">
function Page_Load(sender : Object, e : EventArgs) : void {
var oConn : OleDb.OleDbConnection;
var oDs : DataSet;
oConn = new DataSet();
...
}
</script>
Merk op dat nadat de Namespace geļmporteerd is, je er rechtstreeks types van kan
selecteren, zoals gedaan is met de DataSet, of met (een deel van) de Namespace,
zoals met de OleDbConnection. Merk verder op dat in plaats van Server.CreateObject,
je ook het New keyword kan gebruiken om nieuwe objecten te maken. Als je objecten
wilt importeren die je zelf hebt gemaakt, moet je mogelijk ook de Assembly (de DLL)
specificeren die de objecten bevat.
<%@ Assembly Name="MyObject" %>
COM Objecten gebruiken
Erg belangrijk in ASP is de mogelijkheid COM objecten te gebruiken om de functionaliteit
uit te breiden. Elke keer dat je Server.CreateObject in een script gebruikt, roep
je in feite een COM component aan. Een aantal hiervan, zoals ADODB, zijn onderdeel
van het operating systeem, anderen kunnen third party componenten zijn of componenten
die je gemaakt hebt met Visual Basic, Visual C++ of andere talen. Hoewel het .NET
Framework niet langer COM gebruikt en je in plaats daarvan componenten kan maken die
gebruik maken van het Framework, is het gebruiken van legacy COM componenten iets
dat je waarschijnlijk nog wel even moet doen. Als je migreert, is de kans groot dat
je voornamelijk COM componenten aanroept in de code.
COM components hebben verschillende threading modellen, waarvan de belangrijkste
Single Thread Apartment (STA) en Multi-Threaded Apartment (MTA) zijn. ASP is
geļmplementeerd gebruikmakend van een STA thread pool wat het erg efficiėnt maakt
om STA componenten aan te roepen. Dit is er handig aangezien de meeste componenten
gemaakt worden met Visual Basic 6, wat STA componenten maakt. STA components hebben
een aantal nadelen, bijvoorbeeld dat ze meestal niet erg schaalbaar zijn, en dus
vooral in zware applicaties beperkt bruikbaar zijn. Om dit te vermijden, is ASP.NET
geļmplementeerd gebruikmakend van een MTA thread pool. Hoewel dit veel voordelen
heeft, is het niet erg efficiėnt om STA componenten uit ASP.NET aan te roepen, en
derhalve wordt dat standaard niet ondersteund. Als je STA componenten in een pagina
moet gebruiken, dan moet je dit in ASP.NET als volgt aangeven:
<%@ Page LANGUAGE="VB" aspcompat="True"%gt;
In de declaratie hierboven, vertelt het aspcompat attribuut ASP.NET dat het STA
componenten moet accepteren. Als je een STA component probeert te maken met Server.CreateObject,
zonder ASP compatibiliteit te specificeren, dan zal ASP.NET een foutmelding geven.
Let op dat ADO standaard STA threaded is, omdat dit nodig is als je werkt met
Microsoft Access en sommige andere databases. Als je alleen werkt met bijvoorbeeld
SQL Server of Oracle, kan dit uitgezet worden door makfre15.bat uit te voeren in
de \Program Files\Common Files\System\ado map. Hierdoor wordt ADO free threaded
maakt, waardoor ASP compatibiliteit niet nodig is. ADO.NET heeft dit probleem niet,
zodat als je van plan bent je database code te migreren naar ADO.NET (wordt later
behandeld) je dit niet hoeft te doen.
Als je een COM component wilt gebruiken, dan kan je een variabele declareren
(zonder type) en Server.CreateObject te gebruiken om het object te maken. Als je
dat op deze manier doet, dan gebruik je late-binding, wat betekent dat ASP.NET het
type moet bepalen tijdens het uitvoeren van de code. Late-bound code is daardoor
relatief langzaam. Sterker nog, ASP.NET pagina“s die late-bound COM calls gebruiken
zijn meestal langzamer dan de oorspronkelijk ASP pagina. Je kan deze prestatie
verbeteren door de code early-bound te maken, wat betekent dat het type bekend is
op het moment dan gecompileerd wordt, in plaats van bij het uitvoeren van de code,
zodat dit tijdens het uitvoeren van de code niet meer gedaan hoeft te worden.
Hoewel dit nog steeds niet in de buurt komt van pure .NET code (met .NET componenten),
is het zeker sneller dan de oude ASP pagina“s. Om early-bound code te maken moet
je de Type Library importeren van een COM component. In Visual Studio .NET kun je
dit doen door een reference toe te voegen, anders kun je van de command line
tlbimp.exe gebruiken. Je doet dit als volgt:
Tlbimp MyObject.dll /out:MyObjectNET.dll
Dit maakt een nieuw bestand geheten MyObjectNET.dll, die je kunt kopiėren naar de
\bin directory van het web project. Je kunt ook Tlbimp.exe met .tlb Type Library
bestanden gebruiken.
Nadat je de nieuwe DLL hebt gekopieerd, moet je de Namespace importeren net zoals
je dat moet doen met .NET objecten. Je moet ook de gebruikte assembly specificeren.
<%@ Import Namespace="MyObjectNET" %gt;
<%@ Assembly Name="MyObjectNET" %gt;
Je kan nu als volgt een variabele declareren door een class in het COM component te
gebruiken:
[VB.NET]
Dim objX As MyObjectNET.MyClass
objX = New MyObjectNET.NyClass
[JScript.NET]
var objX : MyObjectNET.MyClass;
objX = new MyObjectNET.MyClass;
Als je met COM componenten werkt op deze manier in ASP.NET, zijn er twee problemen
die je beter kunt vermijden. Ten eerste kan ASP.NET als je de Type Library van een
COM component importeert niet meer zien of het COM component STA of MTA threaded
is. Het zal dus geen waarschuwing meer geven als je een STA component aanmaakt
zonder ASPCOMPAT="True" te gebruiken. Het lijkt er dus op dat dan alles goed gaat,
maar dat is niet zo. Het gevolg is dat je applicatie zeer traag en onstabiel kan
worden. Ten tweede is het onverstandig om het object meteen te instantiėren wanneer
je de variabele maakt, als volgt:
[VB.NET]
Dim objX As New MyObjectNET.MyClass
of
Dim objX As MyObjectNET.MyClass = New MyObjectNET.MyClass
[JScript.NET]
var objX : MyObjectNET.MyClass = new MyObjectNET.MyClass;
Ook als je dit doet, wordt ondanks ASPCOMPAT="True", een STA component geļnstantieerd
vanuit een MTA thread, hetgeen de applicatie onstabiel maakt. Met .NET componenten,
of in Windows Forms applicaties is dit allemaal geen probleem.
ADO
ADO is compleet vernieuwd voor het .NET Framework en is hernoemd tot ADO.NET. De
hele object structuur is veranderd en objecten die overeenkomen met objecten in
ADO hebben allemaal verschillende namen. Er is feitelijk geen 1-op-1 relatie tussen
ADO objecten en ADO.NET objecten. Dit betekent dat tenzij je jouw database toegang
code herschrijft, deze code nog steeds ADO nodig heeft in plaats van ADO.NET. ADO.NET
is duidelijk sneller, dus als je de mogelijkheid hebt om je data toegang code te
herschrijven, doe het dan! Dit geldt vooral als je generieke functies hebt geschreven,
die HTML output maakt met slechts een paar parameters, omdat je dan alleen een klein
gedeelte van je applicatie hoeft te herschrijven. Daarbij zal je code mogelijk tot
500% sneller werken.
Als je data toegang code in de meeste ASP pagina“s hebt geschreven, dan is het
waarschijnlijk beter om bij ADO te blijven en te wachten met ADO.NET tot je de
applicatie kunt overdoen. Microsoft heeft ingezien dat dit waarschijnlijk zal gebeuren,
dus bestaat er al een DLL met de geļmporteerde Type Library in
C:\Program Files\Microsoft.NET\Primary Interop Assemblies, zodat je niet veel hoeft
te doen om code early-bound te maken. Je ADO kan natuurlijk late-bound gebruiken,
maar dit zal geen positief effect hebben op de prestaties van de applicatie. Omdat
het waarschijnlijk is dat ADO vaak gebruikt wordt, kan het echt helpen om early-binding
te gebruiken. Het enige wat je hoeft te doen is de Namespace en Assembly specificeren,
zoals hier:
<%@ Import Namespace="ADODB" %gt;
<%@ Assembly Name="ADODB" %gt;
Als je dat gedaan hebt, kan je de variabele als volgt declareren:
[VB.NET]
Dim oConn As ADODB.Connection
[JScript.NET]
var oConn : ADODB.Connection;
Dit werkt prima met Server.CreateObject, maar je kan ook overwegen om New te gebruiken,
om de prestatie iets meer te verbeteren.
Het migratieproces
Zoals je hierboven hebt gezien, moet je behoorlijk wat veranderingen doorvoeren
nadat je een .asp bestand hebt hernoemd naar een .aspx bestand, zelfs meer met VBScript
dan met JScript. Je kunt het proces het beste stap voor stap doen. Begin zoveel
mogelijk met het aanpakken van de wijzigingen in de taal door het gebruiken van
zoek en vervang. Als je een programma hebt dat dit over meerdere bestanden tegelijkertijd
kan doen, gebruik dit dan. Let echter wel goed op dat je niet code vervangt en
verwijdert waar dat niet nodig is. Bijvoorbeeld als je "Set" vervangt met een lege
string, doe het dan case sensitive, omdat "Recordset" anders "Record" wordt. Als
je de code draaiende hebt, begin dan met het verbeteren van de prestatie door namespaces
te importeren, variabelen te maken, etc. Daarna kan je ervoor kiezen om .NET objecten
toe te voegen om de prestatie van je applicatie nog verder te verbeteren. Omdat je
er niet onderuitkomt een aantal keren te kijken naar de documentatie van ASP.NET
tijdens het migreren, leer je al behoorlijk wat van ASP.NET tijdens het migreren
van je applicatie.
Dit artikel is eerder
verschenen in Code Magazine (issue 4, 2001) onder de naam "Migrating to ASP.NET part II
".
Zie voor meer informatie ook http://msdn.microsoft.com/asp.net/using/migrating/
|