HOW TO:Silverlight 3 Applikation im Web
Seit einiger Zeit ist Silverlight ja nun auf dem Markt. Da die IN4OUT gerne auf dem neusten Stand ist, habe wir eine Bilderverwaltung mit Silverlight 3 gemacht, welche an eine Microsoft SQL Datenbank gebunden ist. Um euch alle in die Geheimnisse von Silverlight 3 Entwicklung einzuweihen, schreib ich diesen HOW TO – Eintrag.
So, damit ihr auch etwas über den Ablauf und die Technik von Silverlight wisst, hier noch einen kurzen Abschnitt über die Architektur:
Wie man auf dem obigen Architekturschema sehen kann, befinden sich auf der Serverseite die Datenbank, ein ClassContainer und Webservice. Clientseitig befindet sich dann die eigentliche Silverlight-Applikation. Um das Ganze noch etwas genauer zu veranschaulichen, gebe ich ein Beispiel:>
Was passiert wenn aus der Datenbank Daten geladen werden, welche in einem Datagrid angezeigt werden.
- In der Datenbank wird eine ganz normale Stored Procedure geschrieben um die Daten aus der entsprechenden Tabelle zu lesen.
- Im Webservice wird der Zugriff auf die Stored Procedures gemacht. Normalerweise machen wir das über die DB-Klassen im App_Code-Ordner gemacht. 3.Da Silverlight keine
Datasets und Datareader kennt, werden dann die Daten in den ClassContainer geschrieben. In diesem ClassContainer befinden sich nur Propertys. - Auf der Clientseite im Programmcode wird dann der Zugriff auf den Webservice gemacht und die gespeicherten Daten aus dem ClassContainer herausgelesen. Diese Daten können dann wie gewohnt an ein Datagrid, eine Listbox oder weitere Controls gebunden werden.
- Diese Daten können dann wie gewohnt an ein Datagrid, eine Listbox oder weitere Controls gebunden werden.
Weiter darf man nicht vergessen, die verschiedenen Packages von Silverlight zu installieren. Siehe dazu HOW TO: UPgrade von Silverlight 2 zu Silverlight3
Zuerst muss ein neues Projekt erstellt werden. Dafür wählt man in der Sprache Visual Basic ein Silverlight-Projekt. Wichtig ist dabei, dass das .NET Framework 3.5 ausgewählt ist. Wenn dann auf OK geklickt wird, erscheint ein weiterer Bildschirm. Visual Studio fragt nach ob die Silverlight-Applikation bereits in eine Webseite integriert werden soll. Wir lassen die Standardeinstellungen und klicken auf OK. So haben wir bereits genau dieselbe Umgebung wie wir es später im i4Portal benötigen. Nun ist das Projekt erstellt und die XAML-Seite Page.xaml geöffnet. Mit F5 kann die Applikation bereits gestartet werden.
Nicht vergessen denn Connectionstring im Web.config anzugeben. In der Datenbank schreiben wir eine Stored Procedure, welche uns alle Bilder aus der Datenbank liest. Diese Bilder wollen wir später in einem Datagrid anzeigen lassen.
Wenn wir mit ASP.NET arbeiten würden, würden wir in der Klasse ImageDB den Zugriff auf die Datenbank machen. Mit Silverlight ist dies alles ein bisschen speziell. Wir müssen einen Webservice machen. Dafür klicken wir mit Rechts auf das Web-Projekt und mit „Add – New item“ wählen wir den „Silverlight-enabled WCF Service“ aus und nennen in SLService. Der Webservice wird generiert und hat bereits eine vorgefertigte Funktion namens „DoWork()“.
Imports System.ServiceModel Imports System.ServiceModel.Activation <ServiceContract(Namespace:="")> _ <AspNetCompatibilityRequirements(RequirementsMode:=AspNetCompatibilityRequirementsMode.Allowed)> _ Public Class SLService <OperationContract()> _ Public Sub DoWork() ' Add your operation implementation here End Sub ' Add more operations here and mark them with <OperationContract()> End Class
Wie bereits in Kapitel 3 erwähnt, kennt Silverlight keine Datareader oder Datasets, deshalb müssen die Daten welche mit dem Webservice ausgelesen werden, in Propertys zwischen gespeichert werden. Man erstellt also im Web eine normale VB-Klasse und macht aus den einzelnen Spalten die man mit der Stored Procedure ausgelesen hat Propertys. Jetzt wagen wir uns an den Webservice. Auf einer Seite zuvor kann man das Grundgerüst erkennen. Statt der Funktion „DoWork()“ können wir dort einfach unsere Funktion benutzen.
<OperationContract()> _ Public Function GetImagesByCategory(ByVal CategoryID As Integer) As List(Of Category) ' Create Instance of Connection and Command Object Dim myConnection As New SqlConnection(ConfigurationManager.AppSettings("connectionString")) … … myCommand.Parameters.Add(parameterModuleID) myConnection.Open() Dim result As SqlDataReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection) If result IsNot Nothing Then Do While result.Read() Dim cust = New Category With {.ImageID = result.GetInt32(0), .ImageFriendlyName = result.GetString(1), .ImageCreatedDate = result.GetDateTime(11), .ImageFormat = result.GetString(8), .ImageWidth = result.GetInt32(5), .ImageHeight = result.GetInt32(6), .ImageSize = result.GetInt32(9)} custList.Add(cust) Loop End If Return custList.ToList() End Function
Dabei wird das <OperationContract()> immer benötigt wenn eine Funktion dann clientseitig erreichbar sein Interessant ist auch der eingekreiste Teil der Funktion. Dort kann man nämlich sehen wie die Ergebnisse aus dem Resultset in die einzelnen Propertys der Klasse geschrieben werden. ">Da nun also der serverseitige Teil abgeschlossen ist, machen wir uns an die Clientseite. Damit wir unsere Daten überhaupt anzeigen können, müssen wir den Webservice zuerst einbinden. Als wir den Webservice erstellt haben, wurde das Web.Config-File automatisch angepasst. Nehmen wir an, dass unser Webservice „SLService.svc“ heisst, sieht es in unserem Web.Config-File folgendermassen aus.
<system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="Silverlight.Web.SLServiceBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> <bindings> <customBinding> <binding name="customBinding0"> <binaryMessageEncoding /> <httpTransport /> </binding> </customBinding> </bindings> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> <services> <service behaviorConfiguration="Silverlight.Web.SLServiceBehavior" name="Silverlight.Web.SLService"> <endpoint address="" binding="customBinding" bindingConfiguration="customBinding0" contract="Silverlight.Web.SLService" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> </system.serviceModel>
Dabei müssen die eingekreisten Teile des neuen Web.Config-Teil’s geändert werden.
<system.serviceModel> <bindings> <basicHttpBinding> binding name="ServicesBinding" maxReceivedMessageSize="200000000" maxBufferSize="200000000"> <readerQuotas maxArrayLength="200000000" maxStringContentLength="200000000"/> </binding> </basicHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="Silverlight.Web.SLServiceBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> <services> <service behaviorConfiguration="Silverlight.Web.SLServiceBehavior" name="Silverlight.Web.SLService"> <endpoint address="" binding="basicHttpBinding" bindingConfiguration="ServicesBinding" contract="Silverlight.Web.SLService" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> </system.serviceModel> </configuration>
Nun können wir testen ob der Webservice funktioniert. Wir rufen den Webservice direkt im Browser auf: Wenn der Webservice wie auf dem linken Screen aussieht, kann er der Silverlight-Applikation problemlos hinzugefügt werden. Der rechte Screen meldet uns einen Fehler bezüglich des Webservices. Auf Fehlersuche geht man sowieso nur über den Browser. Probiert man einen fehlerhaften Webservice einer Silverlight-Applikation hinzuzufügen, kommt immer der Not-Found 404 – Fehler, welcher nicht sehr aussagekräftig ist.
Um den Webservice hinzuzufügen klicken wir mit Rechts auf „References“ und dann auf „Add Service Reference“. Nun können wir die URL des Services direkt angeben oder einfach nach allen Services suchen mit dem Klick auf „Discover“. Jetzt sehen wir dort genau unsere Methode welche auf die Stored Procedures in der Datenbank zugreift.
Unsere Webservice-Referenz nennen wir der einfachheithalber mal „Reference“.
Nachdem wir nun den Webservice hinzugefügt haben, machen wir uns an die XAML-Seite. Dort fügen wir auf der GUI-Seite ein normales Datagrid ein.
<UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" x:Class="Silverlight.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"> <Grid x:Name="LayoutRoot"> <data:DataGrid x:Name="dgImages" Width="500" Height="500"></data:DataGrid> </Grid> </UserControl>
Das x:Name ist sozusagen die ID von Objekten in ASP.NET. Danach öffnen wir die Code-Behind-Seite und geben zum Binden der Daten an das Datagrid folgenden Code an. (Der grüne Text bezeichnet dabei Kommentare.)
Partial Public Class MainPage Inherits UserControl Public Sub New() InitializeComponent() '*** Hier wird nochmals angegeben, wie der Pfad zum Webservice heisst. Dim uri As New Uri(Application.Current.Host.Source, "/SLService.svc") Dim client As New Reference.SLServiceClient("BasicHttpBinding_SLService", uri.AbsoluteUri) '*** Dieser Handler wird augerufen wenn das Abrufen der Dates '*** aus der Datenbank über die Klasse, '*** über den Webservice fertig ist und die asynchrone Methonde(siehe unten) '*** ausgeführt wurde AddHandler client.GetImagesByCategoryCompleted, AddressOf client_GetImagesByCategory '*** Asynchrone Methonde. Die zwei in der Klammer steht dabei für die '*** benötigten Parameter. client.GetImagesByCategoryAsync(2) End Sub Private Sub client_GetImagesByCategory(ByVal sender As Object, ByVal e As Reference.GetImagesByCategoryCompletedEventArgs) '*** Wenn alle Daten geladen sind, werden die Resulate an '*** das Datagrid gebunden. dgImages.ItemsSource = e.Result End Sub End Class
Wenn ich die Applikation nun starte, habe ich bereits das gefüllte Datagrid! Juchee!
Hallo, gratuliere, super Ergebnis (Juchee!) mit guter Erklärung und Kommentaren, die ich sonst oft vermisse. Von SL3 würde ich als Db- und Anwendungsentwickler von einem Service (svc)allerdings mehr erwarten, denn über Ado.Net Modelle (.edmx) lassen sich in Silverlight nicht nur Abfragen zum Anzeigen ausführen, sondern auch relationale, abhängige Tabellen auf einfache Weise ändern/bearbeiten (CRUD-Operationen mit .SaveChanges()). Auch die ObservableCollection ist für brauchbare Clientanwendung m.M.n. ein wichtier Bestandteil.
Trotzdem vielen Dank für das hilfreiche Beispiel und mfG. Wolfgang