[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ]
Genau 2 Records werden bezogen und beinhalten die gewünschten Daten. Die bezogenen Daten werden im lokalen Array "laData" aufgebaut. Dieses Array wird im (auf Form Ebene privaten) Array "aAllData" kopiert. Auf diese Weise ist sichergestellt, dass bei mehreren Tabs alle bezogenen Daten aufbewahrt werden können. Die Daten, welche im Array aAllData abgespeichert sind, werden dazu verwendet, festzustellen, ob sich Daten verändert haben oder nicht. Siehe auch weiter unten. Die Methode presentCropArea() ist für das Präsentieren der Daten zuständig. Private Sub presentCropArea() txtAreaUnit.Text = aAllData(ssTab.Tab)(0, 11) ' areaunit
' area_ii, seed_ii pvArea(lnField).ValueReal = aAllData(ssTab.Tab)(0, lnField) Next End Sub Hier werden die Daten aus dem aAllData Array in die dazu vorgesehenen Controls geschrieben. Hier besteht die Möglichkeit Daten für die Präsentation noch zu verändern. Anwendungsbeispiel 4Vor dem Wechsel zu einem anderen Node muss festgestellt werden, ob es allfällige Änderungen, welche zu speichern sind, gegeben hat. Dies erfolgt über die Methode pvExplorer_BeforeNodeSelectionChange(), welche analog zur oben beschriebenen Methode diesmal die SaveData() Methode des entsprechenden Forms aufruft. Die SaveData() Methode präsentiert sich wie folgt: Public Sub saveData(ByVal toNode As pvxNode) If hasChanged(toNode) Then
If MsgBox("Do you want to save your changes ?", vbYesNo + ' Save Changes Call saveCrop Else ' Requery Data Call RefreshData(toNode) End If End If End Sub Die Prüfung, ob Daten verändert wurden, erfolgt letztendlich durch einen Vergleich der ursprünglich bezogenen aAllData Werte mit den Werten aus den Controls, welche der Benutzer evtl. verändert hat. Die hasChanged() Methode präsentiert sich wie folgt: Dim laData() As Variant Call prepareSaveData(laData, 0)
Dim lnRow As Integer Die PrepareSaveData() Methode sieht folgendermassen aus:
Private Sub prepareSaveData(ByRef taData() As Variant, ByVal tnTab As Integer) taData(1, lnCol, 0) = aAllData(0)(1, lnCol) Next End Sub Hier wird ein neues 3D Array aufgebaut. Dieses Array hat 2 Ebenen. Die Ebene 1 beinhaltet die Daten aus aAllData und die Ebene 2 die Daten aus den Controls. Die Funktion getnextchange() ist für das Aufspüren von Veränderungen zuständig. Diese befindet sich im Modul modGeneral. Public Function getNextChange(ByRef taData() As Variant, Optional ByVal Dim lnRetVal As Integer
Dim lnRowCount, lnRow As Integer For lnField = 0 To lnFieldCount If Abs(taData(lnRow, lnField, 0) - taData(lnRow, lnField, 1)) >= 0.01 Then lnRetVal = lnRow Exit For End If Next If lnRetVal > -1 Then ' Änderung gefunden also raus End If Next getNextChange = lnRetVal ' Row in welcher eine Änderung passierte sonst -1 End Function Der eigentliche Speichervorgang wird mit der Methode SaveCrop() angestossen: Private Sub saveCrop() Dim laData() As Variant
Dim loMktPlan As ImpactData2b.clsMktPlan
' Key Array
Dim lnRetVal As Integer modConstants.const_LEVEL_CROP) Set loMktPlan = Nothing End Sub Die Applikations Logik SchichtDie Anwendungsbeispiele entsprechen den oben aufgeführten. Anwendungsbeispiel 1In diesem Beispiel wird aus der Business Komponente die Methode getindex() aufgerufen. Diese Methode ist es, welche die Verbindung zum Data Tier herstellt, einen Datenbezug durchführt und einen disconnected Recordset zurückliefert und die Verbindung wieder schliesst. _ ByVal tnYear As Integer, _ ByVal tcCrop As String, _ ByVal tcProdType As String) As ADODB.Recordset ' Optionale If tcProdType = "" Then If tcCrop = "" Then
Dim oConn As ADODB.Connection
Dim oCmd As ADODB.Command Set oCmd = New ADODB.Command
'Command definieren
'Parameter erstellen Set oParam = oCmd.CreateParameter("tnYear", Set oParam = oCmd.CreateParameter("tcCrop", Set oParam = oCmd.CreateParameter("tcProdType",
Dim lnRecsAffected As Long Anwendungsbeispiel 2In der Mittelschicht läuft folgender Code ab: Public Function queryAll() As ADODB.Recordset Dim loConn As ADODB.Connection
Dim lcSQL As String
Dim loRS As ADODB.Recordset adLockBatchOptimistic, adCmdText Set loConn = Nothing End Function Anwendungsbeispiel 3Die Methode getcropArea() ist für den Datenbezug zuständig. Public Function getCropArea(tcPLU As String, tcPLUExt As String, tnYear Dim loConn As ADODB.Connection
Dim lcWhere As String " and pluext = " & modFunctions.strsql(tcPLUExt) & _
Dim lcSQL As String "<area>;area", "val") & _ ", 'AREA' label" & _ ", areaunit uom" & _ " from PLCROP" & _
lcSQL = lcSQL & " UNION ALL " & _ "select " & modFunctions.buildFieldListExpr("isnull( " " from PLCROP" & _ lcWhere Dim loRS As ADODB.Recordset adLockBatchOptimistic, adCmdText Set getCropArea = loRS End Function Hier erkennt man, wie der Recordset mit 2 Records mittels einer UNION ALL Anweisung aufgebaut wird. Anwendungsbeispiel 4Die Methode SaveSingle() ist für das Abspeichern der Daten zuständig. Damit der Client nichts mit der Speicherlogik zu tun hat und auch um Form seitenweise jeweils in einem Prozess als Ganzes abspeichern zu können, wird der Methode auch der 3 D Array mit den Ursprungs- und den aktuellen Werten übergeben. Public Function saveSingle(ByRef taKeys(), taData() As Variant, tnLevel Dim lnRetVal, lnResult As Integer
Dim loConn As ADODB.Connection … Wird in der Speicherlogik auch noch der aktuelle Wert in der DB benötigt, so wird dieser explizit von der Mittelschicht bezogen. Die Daten SchichtDie Anwendungsbeispiele entsprechen den oben aufgeführten. Anwendungsbeispiel 1In diesem Beispiel ruft die Business Komponente die Stored Procedure "usp_getMktplanIndex" auf. create procedure usp_getMktPlanIndex /* Code… */ select "felder" from "tabelle" crop like @tcCrop and prodtype like @tcProdType order by "sortierung" end Anwendungsbeispiel 2Der Data Tier erhält über die Business Komponente eine SQL Anfrage und retourniert die angeforderten Daten. Anwendungsbeispiel 3Der Data Tier erhält über die Business Komponente eine SQL Anfrage und retourniert die angeforderten Daten. Anwendungsbeispiel 4Der Data Tier erhält über die Business Komponente eine SQL Anfrage und retourniert die angeforderten Daten. SOAP/XML vs. DCOMGrenzen von COM/DCOMCOM resp. DCOM ist ein sog. "multi port protocol". Es arbeitet hoch effizient in geschützten LAN Bereichen. Wird hingegen das Internet als globales IP Netz verwendet, so machen die dort vorhandenen "Network Address Translators" sowie die "Firewalls" DCOM das Leben schwer, da nicht alle Ports frei sind und auch schwer vorauszusagen ist, welche freigehalten werden können. Obwohl Microsoft vor einiger Zeit noch von "DCOM over HTTP" gesprochen hat, zeigt sich heute klar, dass diese Art der Kommunikation zwischen User Tier und Business Tier stattdessen via XML implementiert wird. Vision für n-Tier AnwendungenEs zeichnet sich ab, dass in Zukunft vermehrt XML als das Kommunikationsprotokoll zwischen User Tier und Business Tier verwendet wird. Die Vision, wie heutige n-Tier Entwicklungen auf die neue .net Platform gebracht werden können zeigt der im Folgenden beschriebene SOAP Toolkit Beispielanwendung auf. Von einem Anwendungs Entwicklungs Standpunkt aus müsste man sagen: "Mir ist es egal, ob DCOM oder XML, ich will einfach, dass es läuft". Um das zu erzielen, ist es sinnvoll, sich von den Spazifika DCOM vs. SOAP/XML nach Möglichkeit abzuschotten, ja sogar vor den Änderungen, die noch kommen werden. Microsoft ist ja dafür bekannt, bis kurz vor eine definitive Version noch einiges zu verbessern. Das Prinzip der Remote Message InvocationBei einem DCOM Aufruf wird ein Remote Procedure Call abgesetzt. Für den Entwickler geschieht das ganze dank DCOM völlig transparent. Der Code präsentiert sich typischerweise folgendermassen: Dim oImpData As ImpactData2b.clsMktPlan Egal, ob die aufgerufene Methode in einer dll auf Maschine x oder y, oder sogar auf der eigenen läuft, der Aufruf bleibt immer derselbe. Hierzu wird in der Registry nachgeschaut, wo diese dll registriert ist. Handelt es sich um eine Remote Registrierung, wird vollautomatisch ein RPC via DCOM abgesetzt. Bei der Kommunikation via XML wird mittels dem SOAP Toolkit versucht, dieser Lösung möglichst nahe zu kommen. Hierzu wird mit einer sog. ROPE.DLL eine Brücke zwischen dem Client und dem Server geschlagen. Die ROPE.DLL ist es, die den Hauptteil der Remote Message Invocation abnimmt und hierzu das XML als Kommunikationsmedium über das standard HTTP Protokoll verwendet. Der SOAP ToolkitMicrosoft hat mit der gesamten .net Strategie vor, die Entwicklung und das via XML zur Verfügung stellen von sog. Webservices, zu vereinfachen. Dank dem SOAP Toolkit ist es heute schon auf relativ einfache Art und Weise möglich, Business Funktionalität über das Internet den Windows Clients zur Verfügung zu stellen. Der Soap Toolkit kann von der Microsoft homepage runtergeladen werden. Ich verzichte hier darauf einen Link anzugeben, da sich die Lokationen so rasch ändern, dass Sie sowieso mit der Suche nach "SOAP Toolkit" besser bedient sind. Ich will an dieser Stelle nicht das wiederholen, was in der SOAP Toolkit Dokumentation steht. Stattdessen will ich eine verständliche Übersicht geben und das mit einer Live Demonstration einer selber entwickelten Beispielanwendung illustrieren. XMLXML ist das Format, welches für den Datenaustausch verwendet wird. Wenn man mit dem SOAP Toolkit arbeitet, muss man keine XML Dateien von Hand erzeugen! HTTPSOAP läuft über das HTTP Protokoll auf dem Port 80 und kann somit auch in Umgebungen mit Network Address Translation und Firewalls eingesetzt werden. Konkret: Überall, wo eine HTML Webseite durchkommt, kann auch ein XML File transportiert werden. SOAPSOAP steht für Simple Object Access Protocol. Die SOAP Spezifikationen sind beim W3C Konsortium hinterlegt. SDLSDL steht für Service Description Language. Diesen Begriff werden Sie noch oft unter Visual Studio.net zu hören bekommen. Es handelt sich hier um ein XML File, welches die zur Verfügung stehenden Services auflistet. Man kann sich darunter auch eine Type Lib im XML Format vorstellen. COMDer SOAP Toolkit verwendet auf der Serverseite COM um die Serverkomponenten aus dem Transaction Server aufzurufen. Generell zeigt sich in diesem Szenario, dass COM lediglich auf der Serverseite zum Einsatz gelangt. IISDer Ganze Ablauf benötigt einen Webserver. Der HTTP Post gelangt auf den Webserver und dort werden die Serverkomponenten instanziiert. Auf dem Web muss eine listener.asp vorhanden sein plus eine weitere asp Datei und ein XML File. ÜbersichtIn der SOAP Dokumentation ist eine Übersicht über den grundsätzlichen Ablauf unter Verwendung der ROPE.DLL. Im Folgenden wird anhand unserer Beispielanwendung gezeigt, wie sich das Ganze aus Entwicklersicht präsentiert: SOAP BeispielanwendungBei unserer Beispielanwendung handelt es sich um eine VB Client Applikation, welche via SOAP Daten in Form eines Recordsets bezieht. Der Client
Der Datenbezug präsentiert sich wie folgt: Dim sXML As String
Set oXML = New MSXML.DOMDocument
Set oRs = New ADODB.Recordset "http://212.59.144.70/soaptest/address.xml" sXML = oRope.GetXMLStream(vintsearchmode:=2, _ vstrfirstname:="", _ oXML.loadXML sXML oRs.Open oXML Set oXML = Nothing Set oRope = Nothing Die Rope.dll übernimmt quasi die Arbeit, die eigene dll, welche im MTS läuft und aus der ASP Seite aufgerufen wird, zur Verfügung zu stellen. Rope.dll ist auf dem Client und dem Server installiert und stellt folgenden Ablauf sicher:
Der ServerDer Server erhält einen Web Request via HTTP. Das Web beinhaltet die Dateien address.asp, address.xml sowie listener.asp. Der Ablauf ist der Folgende:
Das address.asp File präsentiert sich wie folgt: <%@ Language=VBScript %> <% Option Explicit Response.Expires = 0 '-------------------------------------------- ' SOAP ASP Interface file Address.asp ' Generated 9/25/2000 4:23:23 PM ' By Microsoft SOAP Toolkit Wizard, Version 205.0.3 '-------------------------------------------- Const SOAP_SDLURI = "http://inspiron3800/soaptest/Address.xml" 'URI of service description file %> <!--#include file="listener.asp"-->
<% '_____________________________________________________________________ Public Function GetXMLStream (ByVal vintsearchmode, ByVal vstrFirstname, ByVal vstrLastname) Dim objGetXMLStream Set objGetXMLStream = Server.CreateObject("SOAPTestData.Address") GetXMLStream = objGetXMLStream.GetXMLStream(vintsearchmode, vstrFirstname, vstrLastname) 'Insert additional code here Set objGetXMLStream = NOTHING End Function '_____________________________________________________________________
Public Function Update (ByVal tnAddressid, ByVal tcFirstname, ByVal tcLastname, ByVal tcStreet, ByVal tcZip, ByVal tcCity) Dim objUpdate Set objUpdate = Server.CreateObject("SOAPTestData.Address") Update = objUpdate.Update(tnAddressid, tcFirstname, tcLastname, tcStreet, tcZip, tcCity) 'Insert additional code here Set objUpdate = NOTHING End Function '_____________________________________________________________________ Public Function getVersion () Dim objgetVersion Set objgetVersion = Server.CreateObject("SOAPTestData.Address") getVersion = objgetVersion.getVersion() 'Insert additional code here Set objgetVersion = NOTHING End Function '_____________________________________________________________________ Public Function getNumber () Dim objgetNumber Set objgetNumber = Server.CreateObject("SOAPTestData.Address") getNumber = objgetNumber.getNumber() 'Insert additional code here Set objgetNumber = NOTHING End Function '_____________________________________________________________________ %> Man erkennt, dass der eigene Code über die server.createobject Anweisung instanziiert wird. Hier erfolgt letztendlich der Aufruf des eigenen Codes. Man erkennt, dass der eigene Code über die server.createobject Anweisung instanziiert wird. Hier erfolgt letztendlich der Aufruf des eigenen Codes. |