V centru dnešního softwarového světa stojí databáze, snad by ani nebylo troufalé říci, že většina větších aplikací nějakou tu databázi využívá. Vytvořit takovou „database-driven“ aplikaci v Silverlightu se může, zejména v porovnání s technologiemi jako ASP.NET, zdát zbytečně komplikované. Účelem tohoto článku je dokázat opak – ukázat čtenářům, že je to ve skutečnosti velmi snadné.
Vzpomínám si na své začátky se Silverlightem, mým cílem bylo vytvořit si jednoduchou, designově bohatou návštěvní knihu, která by příspěvky ukládala do databáze. V té době mnoho článků věnujících se Silverlightu nebylo a příkladů byl nedostatek. Jediné, co jsem se dočetl, bylo, že musím využít webové služby (web services), protože Silverlight z bezpečnostních důvodů nemůže přímo komunikovat s databází. Je zapotřebí ještě jedna vrstva, kterou je právě ona webová služba.
Tato oblast byla pro mne natolik nová, že jsem nebyl schopný svého cíle dosáhnout. Po pár měsících a po nabytí zkušeností v jiných oblastech webového programování jsem se k Silverlightu vrátil a službu jsem si napsal bez nějakého většího úsilí. Inu, začátky bývají vždy nejhorší.
Po lehce upovídaném úvodu jdeme k věci. Jak už je asi jasné, ukážu vám, jak pomocí Silverlightu napsat jednoduchou návštěvní knihu, kterou poté můžete například vložit jako widget do jiných aplikací.
Článek je zaměřen na začátečníky v Silverlightu a WCF (framework pro webové služby), ale budou potřeba alespoň malé znalosti práce s databází uvnitř Visual Studia. Tvorbu databáze ve Visual Studiu můžete odkoukat například z mého seriálu o ASP.NET MVC, základy fungování Silverlightu a LINQ to SQL zase ze seriálu o tvorbě Digg klienta. Vrcholem dnešní grafiky bude pár kousků textu, textových polí a jedno tlačítko, tudíž nutně Expression Blend znát nemusíte.
A jaké že technologie dnes použijeme? Samozřejmě Silverlight, konkrétně třetí verzi, dále pak WCF a LINQ to SQL, SQL Server 2008 (alespoň Express edici) a nějaké to IDE, v mém případě to bude Visual Studio 2010.
Můžeme se vrhnout na programování. Ve Visual Studiu založíme nový projekt typu Silverlight Application, pro jednotnost ho pojmenujeme Guestbook. Potvrdíme a na otázku, zda chceme vytvořit k Silverlight aplikaci i ASP.NET projekt, odpovíme, že ano, a ponecháme defaultní název, tedy Guestbook.Web.
Databáze
Začneme databází. V projektu Guestbook.Web si vytvoříme složku App_Data a do ní vložíme novou databázi, pojmenujeme ji GuestbookDB.mdf. V databázi vytvoříme jednu tabulku, buďto ručně, nebo pomocí tohoto SQL příkazu:
CREATE TABLE [dbo].[Posts]
(
[PostID] INT NOT NULL PRIMARY KEY IDENTITY(1,1),
[Author] NVARCHAR(50) NOT NULL,
[Text] NVARCHAR(500) NOT NULL,
[Date] DATETIME NOT NULL
)
Pokud chcete, naplňte si tabulku nějakými daty, abychom při implementaci čtení z databáze viděli hned výsledek.
Uživatelské rozhraní
Druhým krokem bude uživatelské rozhraní. To bude zprostředkované souborem MainPage.xaml v Silverlight projektu. Není to žádná věda, proto rovnou uvedu zdrojový kód a obrázek pro ilustraci:
<UserControl x:Class="Guestbook.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Height="350" Width="350">
<Grid x:Name="LayoutRoot" Background="White" Height="350" Width="350">
<Grid.RowDefinitions>
<RowDefinition Height="170" />
<RowDefinition Height="140" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition Width="105" />
<ColumnDefinition Width="160" />
</Grid.ColumnDefinitions>
<ListBox Margin="0,10,0,0" Name="posts_Lb" Grid.Column="1" Grid.ColumnSpan="2" />
<TextBlock Margin="0,15,40,100" Grid.Column="1" Grid.Row="1">Jméno:</TextBlock>
<TextBox Margin="0,15,0,100" Name="name_Tb" TabIndex="1" Grid.Column="2" Grid.Row="1" />
<TextBlock Margin="0,60,10,55" Grid.Column="1" Grid.Row="1">Text příspěvku:</TextBlock>
<TextBox Margin="0,60,0,20" AcceptsReturn="True" Name="text_Tb" TabIndex="2" Grid.Column="2" Grid.Row="1"/>
<Button Margin="90,0,0,8" Name="Send_Btn" Height="30" Width="70" Content="Odeslat" Click="Send_Btn_Click" TabIndex="3" Grid.Column="2" Grid.Row="2" />
</Grid>
</UserControl>
Rozhraní je tvořeno jedním ListBoxem, ve kterém budeme zobrazovat příspěvky načtené z databáze, dále pak dvěma TextBoxy a jedním odesílacím tlačítkem. Jednoduché, ale na ukázku dostačující.
LINQ to SQL
Teď přidáme něco, co nám umožní přímo ze C# kódu přistupovat k databázi. Správně, bude to LINQ to SQL, technologie, která nám pomocí pár tahů myši umožňuje vytvořit objektovou reprezentaci databáze, se kterou pak komunikujeme pomocí LINQu, jež je následně překládán na SQL dotazy. Několik dalších informací o LINQ to SQL najdete ve zde již uveřejněném seriálu o Silverlight Digg klientovi.
Pro přidání LINQ to SQL do projektu klikneme na projekt Guestbook.Web pravým tlačítkem a pomocí známého Add New Item přidáme třídu typu „LINQ to SQL Classes“. Pojmenujeme ji jednoduše Guestbook.dbml. Následně otevřeme Server Explorer a tabulku Posts z databáze přetáhneme do designeru LINQ to SQL.
Rovnou nastavíme jednu důležitou položku pro správnou funkčnost ve spojení s WCF službami. V okně Properties tohoto souboru nastavte vlastnost „Serialization Mode“ na „Unidirectional“ (původní hodnota je „None“). Umožní nám to objekty serializovat a posílat WCF službě (přesněji odekoruje třídu Post potřebnými atributy [DataContract] a [DataMember]).
WCF – Načítání z databáze
Konečně to začne být zajímavé. Do projektu Guestbook.Web přidáme nový soubor typu „WCF Service“ a pojmenujeme ho GuestbookService.svc.
Služba je tvořená rozhraním (IGuestbookService) a od něj odvozenou třídou (GuestbookService).
IGuestbookService vypadá takto (metodu DoWork jsem přejmenoval na GetAllPosts a změnil její návratový typ):
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace Guestbook.Web
{
[ServiceContract]
public interface IGuestbookService
{
[OperationContract]
List<Post> GetAllPosts();
}
}
A příslušná třída GuestbookService vypadá následovně (obsahuje již implementovanou metodu GetAllPosts, ale není na ní vůbec nic složitého):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace Guestbook.Web
{
public class GuestbookService : IGuestbookService
{
GuestbookDataContext dc = new GuestbookDataContext();
public List<Post> GetAllPosts()
{
var posts = from p in dc.Posts
orderby p.Date descending
select p;
return posts.ToList();
}
}
}
U rozhraní si všimněte dvou atributů – [ServiceContract] prostě jen dává vědět, že toto rozhraní definuje službu. Atributem [OperationContract] musí být označena každá metoda, kterou chceme na službě volat.
Metoda GetAllPosts nedělá nic jiného, než že vytáhne z databáze všechny příspěvky, seřadí je sestupně podle data napsání a uloží je do kolekce List.
WCF – Ukládání do databáze
Abychom měli práci s databází hotovou, uděláme ukládání už teď.
Do rozhraní IGuestbookService přidáme novou metodu:
[OperationContract]
void SaveNewPost(string name, string text);
A toto je její implementace:
public void SaveNewPost(string name, string text)
{
dc.Posts.InsertOnSubmit(new Post
{
Author = name,
Text = text,
Date = DateTime.Now
});
dc.SubmitChanges();
}
Ani tady není moc co komentovat.
Využití WCF služby ze Silverlight aplikace
Jako první věc musíme do Silverlight projektu přidat referenci na naši WCF službu. To uděláme tak, že klikneme pravým tlačítkem v Solution Exploreru na projekt a zvolíme položku „Add Service Reference“. V novém okně klikneme na „Discover“ a tuto referenci na službu si pojmenujeme jako GuestbookServiceReference.
Už nám nic nebrání otevřít code-behind MainPage.xaml.cs a vložit do něj metodu pojmenovanou například GetAllPosts:
private void GetAllPosts()
{
GuestbookServiceReference.GuestbookServiceClient gsc = new GuestbookServiceReference.GuestbookServiceClient();
gsc.GetAllPostsCompleted += gsc_GetAllPostsCompleted;
gsc.GetAllPostsAsync();
}
Tato metoda jen nastaví pomocí event handleru metodu, která se má zavolat (gsc_GetAllPostsCompleted), jakmile bude dokončena práce v metodě GetAllPosts na WCF službě, a následně tuto GetAllPosts metodu asynchronně zavolá. Díky asynchronnímu volání nedojde k zablokování zbytku aplikace.
Pro funkčnost potřebujeme vytvořit ještě zmíněnou metodu gsc_GetAllPostsCompleted, tady tedy je:
private void gsc_GetAllPostsCompleted(object sender, GuestbookServiceReference.GetAllPostsCompletedEventArgs e)
{
foreach (var item in e.Result)
{
posts_Lb.Items.Add(string.Format("{0} ({1}) řekl: {2}", item.Author, item.Date, item.Text));
}
}
Přiznávám, že výsledek nebude zrovna dvakrát estetický, ale o designu tu máme jiné články, tady se to pokusím udržet co nejjednodušší.
Zbývá jen přidat volání metody GetAllPosts do konstruktoru tohoto okna a máme vyhráno, příspěvky by se měly úspěšně načíst.
public MainPage()
{
InitializeComponent();
GetAllPosts();
}
Zápis do databáze
Princip je úplně stejný jako při čtení z databáze, proto vám ukážu rovnou zdrojový kód:
private void SaveNewPost()
{
GuestbookServiceReference.GuestbookServiceClient gsc = new GuestbookServiceReference.GuestbookServiceClient();
gsc.SaveNewPostCompleted += gsc_SaveNewPostCompleted;
gsc.SaveNewPostAsync(name_Tb.Text, text_Tb.Text);
}
private void gsc_SaveNewPostCompleted(object sender, EventArgs e)
{
name_Tb.Text = "";
text_Tb.Text = "";
posts_Lb.Items.Clear();
GetAllPosts();
}
Metodu SaveNewPost stačí volat z handleru události Click na tlačítku a opět by vše mělo fungovat.
To by mělo pro představu stačit. Doufám, že tento článek pomůže vyřešit problémy začínajícím v Silverlightu a že vše bylo snadno pochopitelné. Na závěr nabízím ke stažení kompletní solution.