Silverlight 4: CRUD im DataGrid mit WCF RIA Services auf ASP.NET und SQL Server

Silverlight 4: CRUD im DataGrid mit WCF RIA Services auf ASP.NET und SQL Server

Mit automatisch generierten Klassen läßt sich mit minimalem Aufwand eine Silverlight 4 Anwendung gestalten, mit der sich Daten via WCF RIA Servies sowie ADO.NET Entity Framework aus einem SQL Server anzeigen sowie verändern lassen.

Damit haben wir schon eine beachtliche Anzahl von Buzzwords und TLAs (Three Letter Acronyms) beieinander.

Voraussetzung ist, dass die verschiedenen Tools für Silverlight 4 unter VS 2010 sowie für WCF RIA Services installiert sind.

Vorbereiten einer Datenbank

Zunächst erzeugt man eine Datenbank mit dem SQL Server 2008 Management Studio, sofern man nicht bereits eine (Beispiel)Datenbank wie Northwind oder AdventureWorks (oder eigene Datenbank) installiert hat.

Hier wollen wir bei 0 anfangen.

Im Management Studio verbinden wir uns zu unserem SQL Server. Wenn wir SQL Server nicht separat installiert haben wird mit Visual Studio 2010 automatisch die Express-Version installiert, diese finden wir unter localhostSQLEXPRESS. (Typischerweise müssen wir hier nichts ändern, das Management Studio hat automatisch die richtigen Daten als Auwahl vorgegeben. Diese können wir uns für später, für die Benutzung aus Visual Studio heraus, merken.) Dort erweitern wir dann das Feld Datenbanken, rechtsklicken auf Datenbanken und wählen Neue Datenbank aus.

Hier erstellen wir ID als int sowie Identity (unten auf Ja/Yes setzen), FirstName, LastName sowie FavouriteColour jeweils als nvarchar(50). Wichtig: ID müssen wir explizit als Primären Schlüssel (Primary Key) defineren, indem wir links neben die Zeile rechtsklicken und diese Option auswählen. Wenn wir das nichtmachen, bekommen wir nachher mit den automatisch generierten Klassen Ärger und können nicht schreibend auf die Daten zugreifen (hierfür wird ein Primary Key benötigt). Schließlich ändern wir noch rechts den Namen der Tabelle auf Person. Anschließend gehen wir auf Save Person (Diskettensymbol oben in der Leiste).

Jetzt klicken wir im Object Explorer links die neue dbo.Person mit der rechten Maustaste an und wählen Erste 200 Reihen bearbeiten (Edit Top 200 Rows). Dann füllen wir die Tabelle mit ein paar Beispieldaten.

Jetzt können wir das Management Studio wieder schließen und Visual Studio 2010 öffnen.

Erzeugen der beiden Projekte

Als erstes erzeugen wir ein Projekt (Silverlight Applikation) names SilverlightWCFRIA. Sobald wir die Dialogbox bestätigen kommt eine neue Box, in der wir Unterstützung für „WCF RIA Services“ aktivieren (ganz unten). Das .Web Projekt brauchen wir ebenfalls, um den Service zu hosten.

Als nächstes brauchen wir serverseitig einen Zugang zu der Datenbank. Dies geschieht über ein ADO.NET Entity Data Model. Dieses fügen wir also dem .web Projekt hinzu. Wir finden das passende Template, wenn wir auf Daten klicken und benennen es PersonModel. Im nächsten Dialog wählen wir „Generieren aus Datenbank“ aus. Wiederum öffnet sich ein Dialog, hier müssen wir eine „Neue Verbindung“ erzeugen.

Bei wem der Servername nicht automatisch gefunden, der benutzt localhostsqlexpress (für die Express Edition). Die volle Version des Servers heißt normalerweise mssqlserver und findet sich somit unter localhostmssqlserver. Wenn die Daten stimmen können wir unten unter „Datenbank Name wählen oder eingeben“ auf den Pfeil rechts klicken und SilverlightTest auswählen.

Nach Bestätigung und Weiter wählen wir noch die von uns gewünschte Tabelle Person aus.

Um die in PersonModel.edmx neu generierten Zugriffsmöglichkeiten in Intellisense zu integrieren compilieren wir das Projekt einmal durch, entweder per Button oder über Strg + Umsch + B.

Als nächstes wollen wir diese Möglichkeit als DomainService zur Verfügung stellen und fügen daher dem .web Projekt einen DomainService hinzu. (DomainService Klasse in der Web Kategorie.) Diese nennen wir PersonService. Im nächsten Dialog wählen wir Person aus und erlauben editieren (wichtig für später) und bestätigen.

Noch einmal compilieren wir die Lösung durch.

Damit sind wir mit dem Service-Projekt erst einmal durch und wenden uns dem Silverlight-Projekt zu.

cRud

Jetzt fügen wir die Read-Funktionalität hinzu.

Wir öffnen die MainPage.xaml und doppelklicken in der Toolbox auf ein DataGrid. Dieses ziehen wir etwas größer, benennen es dataGridPerson und setzen die AutoGenerateColumns Eigenschaft auf True.

Im MainPage.xaml.cs fügen wir eine Variable auf Klassenebene

1
PersonContext context;

hinzu. Die IDE unterschlängelt PersonContext. Wenn wir mit der Maus über dem Wert schweben oder Strg+. drücken können wir links unten ein neues Feld aufmachen und die Option „using SilverlightWCFRIA.Web;“ auswählen. Im Konstruktor fügen wir folgenden Code hinzu:

1
2
3
4
            context = new PersonContext();
            LoadOperation<person> lo =
            context.Load(context.GetPeopleQuery());
            dataGridPerson.ItemsSource = lo.Entities;

Über LoadOperation (siehe oben) können wir „using System.ServiceModel.DomainServices.Client;“ hinzufügen.

Wenn wir die Lösung jetzt über F5 starten finden wir unsere eben eingegebenen Daten im DataGrid wieder. Aktuell sind dienen die Daten nur der Anzeige, wir wollen das im nächsten Schritt erweitern.


EntityModel
Beim Erzeugen des Models wurde automatisch der Code erzeugt, über den wir einfach mit der SQL Datenbank kommunizieren können.
DomainService
Beim Erstellen der DomainService Klasse wurde automatisch der Code generiert, der nun für die Kommunikation zwischen dem Server (dem Service der Webseite im Projekt .web) sowie dem Client (der Silverlight Anwendung) sorgt.

crUd

Als nächstes widmen wir uns dem Update.

Hierzu fügen wir dem Datagrid eine Routine für das RowEditEnded Ereignis hinzu. Also im Eigenschaften-Fenster auf Ereignisse gehen und dort RowEditEnded auswählen (ZeilenEditierungGeendet?).

Die Routine sieht folgendermaßen aus:

1
2
3
4
5
            if (!context.IsSubmitting)
                if (context.HasChanges)
                {
                    context.SubmitChanges();
                }

Und das war auch schon das Update. Wenn wir nun Daten in dem Datengrid ändern, werden diese autmatisch am Ende des Editiervorgangs auf dem Server übernommen. Sollte es hier zu Problemen kommen, so wurde entweder die Tabelle zu Anfang falsch angelegt (Primary Key setzen) oder beim DomainService die Update-Funktion nicht ausgewählt.

CruD

Nun kommen wir zu zwei weiteren Operationen. Diese können wir einfach analog zu oben durchführen:

1
2
            context.Persons.Add(new Person());
            context.SubmitChanges();

bzw.

1
2
            context.Remove(dataGridPerson.SelectedItem);
            context.SubmitChanges();

Hier wird jedoch ein Problem deutlich: das DataGrid updatet nicht. Und auch mit einem erneuten Laden der Daten (neu setzen der ItemSource) ist das Problem nicht ohne weiteres gelöst.

Lösen können wir dieses Problem jedoch einfach: mit einer DomainDataSource.

Hierzu löschen wir wieder das DataGrid und allen Code, den wir von Hand zur MainPage.xaml.cs hinzugefügt haben. Anschließend können wir ein neues DataGrid auf die Leinwand aufbringen, und zwar unter Datenquellen (Data Sources) sollte Person bereits vorhanden sein. Hier können wir den Typ nochmal ausdrücklich auf DataGrid setzen. So werden direkt alle benötigten Verbindungen erzeugt.

Alternativ überschreibt man den Code in der MainPage.xaml mit folgender Lösung:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    <grid x:Name="LayoutRoot" Background="White">
        <riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my:Person, CreateList=true}" Height="0" LoadedData="personDomainDataSource_LoadedData" Name="personDomainDataSource" QueryName="GetPeopleQuery" Width="0">
            <riaControls:DomainDataSource.DomainContext>
                <my:PersonContext />
            </riaControls:DomainDataSource.DomainContext>
        </riaControls:DomainDataSource>
        <sdk:DataGrid AutoGenerateColumns="False" Height="200" HorizontalAlignment="Left" ItemsSource="{Binding ElementName=personDomainDataSource, Path=Data}" Margin="12,34,0,0" Name="personDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top" Width="400">
            <sdk:DataGrid.Columns>
                <sdk:DataGridTextColumn x:Name="favouriteColourColumn" Binding="{Binding Path=FavouriteColour}" Header="Favourite Colour" Width="SizeToHeader" />
                <sdk:DataGridTextColumn x:Name="firstNameColumn" Binding="{Binding Path=FirstName}" Header="First Name" Width="SizeToHeader" />
                <sdk:DataGridTextColumn x:Name="lastNameColumn" Binding="{Binding Path=LastName}" Header="Last Name" Width="SizeToHeader" />
                <sdk:DataGridTextColumn x:Name="personIDColumn" Binding="{Binding Path=PersonID, Mode=OneWay}" Header="Person ID" IsReadOnly="True" Width="SizeToHeader" />
            </sdk:DataGrid.Columns>
        </sdk:DataGrid>
    </grid>

Wir haben so einfach eine ähnliche Lösung wie zu Anfang, allerdings nur mit R, nicht mit CUD.

crUd

Wir benutzen wieder das RowEditEnded Event.

1
2
3
4
5
6
        private void personDataGrid_RowEditEnded(object sender, DataGridRowEditEndedEventArgs e)
        {
            if (!personDomainDataSource.IsSubmittingChanges)
                if (personDomainDataSource.HasChanges)
                    personDomainDataSource.SubmitChanges();
        }

CruD

Endlich kommen wir zu den Operationen, die sich nun einfach und elegant abbilden lassen.

Dazu fügen wir in der MainPage.xaml Datei 2 Buttons hinzu und beschriften den einen Create, den anderen mit Delete. Entsprechend benennen wir dei Buttons auch buttonCreate und buttonDelete.

Anschließend fügen wir durch jeweils einen Doppelklick auf die Buttons 2 Click-Handler hinzu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        private void buttonCreate_Click(object sender, RoutedEventArgs e)
        {
            if (!personDomainDataSource.IsSubmittingChanges)
            {
                personDomainDataSource.DataView.Add(new Person());
                personDomainDataSource.SubmitChanges();
            }
        }
 
        private void buttonDelete_Click(object sender, RoutedEventArgs e)
        {
            if (!personDomainDataSource.IsSubmittingChanges)
            {
                personDomainDataSource.DataView.Remove(personDataGrid.SelectedItem);
                personDomainDataSource.SubmitChanges();
            }
        }

Et voilà. Natürlich lassen wir es uns nicht nehmen, die Applikation an dieser Stelle einmal auszuprobieren.

Durch den Zugriff auf personDomainDataSource wird das DataGrid automatisch mit upgedatet, wenn sich am Inhalt etwas ändert. personDataGrid.SelectedItem liefert stets ein selektiertes Item zurück, auch, wenn wir nichts ausgewählt haben. Etwas ungewöhnlich, aber so funktioniert ein DataGrid wohl. Wenn wir prüfen wollen, ob wirklich etwas selektiert wurde, könnten wir uns dies z. B. im SelectionChanged Event merken (was wir hier aber nicht machen).

Besonders spannend ist, wie wenig Code wir wirklich selber geschrieben haben, und wie viel automatisch generiert wurde. Aber vorerst bleibt Programmieren in der „richtigen Welt“ wohl anspruchsvoll genug um Programmierer zu rechtfertigen. 🙂


Die komplette Lösung mit CRUD zum Download. Auf den SQL Server wird via ADO.NET Entity Framework zugegriffen. Diese Zugriffe werden wiederum via WCF RIA DomainServices freigegeben und schließlich von der Silverlight anwendung benutzt.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.