SilverLight 4 - Invocando um serviço que expõe dados (C#) - parte 1
Vou iniciar este artigo com a minha introdução feita no em : SilverLight 4 - Criando uma aplicação SilverLight 4 com DataBinding
Tenho escrito (ou seria: tenho escrevido ?) pouco sobre a tecnologia SilverLight, primeiro devido a falta de tempo e depois pelas constantes mudanças e evoluções que o SilverLight tem passado. Você vai dormir usando a versão 3 e quando acorda já tem a versão 4.
De qualquer forma em 2011 pretendo dar um pouco mais de atenção ao SilverLight e um bom começo é ter o ambiente corretamente ajustado para poder usar a tecnologia.
Lembrando que o SilverLight foi lançado nos meados de 2007 e desde então tem evoluído muito; enquanto o ASP .NET é uma plataforma para desenvolvimento do lado do servidor, com a chegada do SilverLight o foco muda para o lado do cliente, visto que uma aplicação SilverLight roda no navegador do cliente em uma versão específica da CLR - Common Language Runtime.
Se o SilverLight é algo totalmente novo para você mas você conhece o WPF, fique sabendo que o SilverLight pode ser considerado como uma versão reduzida do WPF, sendo possível inclusive reutilizar código escrito no SilverLight 4 no WPF 4 e vice-versa.
Preparando o terreno
Para iniciar o desenvolvimento de aplicações SilverLight precisamos ter instalados as ferramentas e os SDKs necessários.
Vamos lá...
1 - Precisamos ter
instalado o Visual Studio 2010 ou o Visual Web Developer Express 2010 ;
2 - Precisamos ter instalado o SilverLight 4 Tools for Visual Studio 2010;
3 - Precisamos ter instalado o Expression Blend 4;
Após baixar e instalar as ferramentas acima, não ocorrendo nenhum erro, tudo estará pronto para você dar início a saga de desenvolver aplicações para o SilverLight.
Invocando um serviço que expõe dados
Ao trabalhar em um projeto Silverlight que envolve serviços, o WCF- Windows Comunication Foundation é a escolha preferida para a construção de um serviço, se temos que criar o serviço e o aplicativo cliente do Silverlight que irá utilizá-lo, dessa forma usando o WCF temos um controle completo sobre quais tipos serão enviados para o cliente.
Quando queremos construir
uma aplicação Silverlight que trabalha com os dados
disponíveis sobre o serviço (talvez proveniente de um banco
de dados ou outro serviço externo), precisamos ter em mente
as seguintes questões :
1-
Como devemos projetar o serviço que expõe os dados de modo que
possa ser acessível a partir do Silverlight ?
2- Como devemos proceder para projetar o aplicativo do
Silverlight, para que ele se comunica com o serviço ?
Para mostrar como podemos nos conectar a partir do Silverlight
com um serviço WCF que expõe os dados, vamos começar, com a
concepção do próprio serviço.
Obs: O artigo foi baseado em
alguns exemplos prontos que foram adaptados e ajustados.
Vamos supor que estamos construindo uma aplicação que contém
uma janela de visualização onde o usuário poderá obter
informações sobre os funcionários de uma empresa.
Os dados do funcionário serão expostos pelo serviço e o
aplicativo Silverlight irá se conectar ao serviço e trabalhar
com os dados no lado do cliente. A seguir estão os passos que
precisa executar para começar este trabalho.
Obs: Para tornar mais simples o exemplo não vamos trabalhar com um banco de dados mas iremos preencher os dados via código.
Dando a partida : Criando a aplicação SilverLight 4
Abra o Visual Web Developer 2010 Express Edition no menu File selecione New Project;
A seguir selecione Visual C# e o template SilverLight -> SilverLight Application, informe o nome SilverLightFuncionarios e clique em OK;
Será apresentada a janela abaixo onde vemos o projeto do tipo ASP .NET Web Application Project selecionado como o tipo de projeto web que será usado para hospedar a aplicação SilverLight. Note que a versão do SilverLight escolhida é a versão 4;
Após fazer estas verificações clique em OK;
Será criada uma solução com dois projetos:
Vamos concentrar-se
primeiro na aplicação web.
Os dados que desejamos expor consistem em informações sobre os
funcionários. Vamos então definir as classes que representam o
domínio do nosso problema.
A seguir vemos o diagrama de classes que mostra as classes do nosso domínio:
O diagrama de classes acima mostra o relacionamento entre as classes e a enumeração CarroTipo . Note que a classe Funcionario é uma classe abstrata e que as demais classes herdam da classe Funcionario.
Obs: Utilizando classes abstratas você pode declarar classes que definam apenas parte de uma implementação, deixando que as classes estendidas forneçam a implementação específica de alguns ou de todos os métodos. Uma classe abstrata em geral será destinada apenas a servir como base para a criação de outras classes;
O próximo passo é criar as classes no projeto SilverLightFuncionarios.web. Vamos começar criando a classe Funcionario.
Selecione o projeto SilverLightFuncionarios.web e no menu Project clique em Add Class;
A seguir informe o nome Funcionario.cs e clique no botão Add;
Repita o procedimento acima e crie as seguintes classes:
Após criar os arquivos para as classes vamos definir o código da classe Funcionario.cs conforme abaixo:
using System; using System.Runtime.Serialization; namespace SilverlightFuncionarios.Web { [DataContract] [KnownType(typeof(Gerente))] [KnownType(typeof(Consultor))] [KnownType(typeof(Administrativo))] public abstract class Funcionario { [DataMember] public int funcionarioid { get; set; } [DataMember] public string nome { get; set; } [DataMember] public string email { get; set; } [DataMember] public DateTime nascimento { get; set; } } } |
Vamos entender a classe Funcionario:
- A classe é uma
classe abstrata e isso indica que a classe não pode ser
instanciada e deve - O atributo DataContract usado na classe indica que o tipo pode ser enviado via rede; DataContract
especifica que o tipo define ou implementa um contrato de
dados e é serializável por um - O atributo DataMember
usado nos campos da classe será incluído no tipo e
poderá ser enviado O DataMember
quando aplicado ao membro de um tipo, especifica que o
membro faz parte de um contrato - O atributo KnowType
marca os tipos a serem incluídos no processo de
serialização; |
Agora vamos definir o código da classe Gerente conforme o código a seguir:
using System.Runtime.Serialization; namespace SilverlightFuncionarios.Web { public class Gerente : Funcionario { [DataMember] public double Gratificacao { get; set; } [DataMember] public CarroTipo Carro { get; set; } } } |
A classe Gerente
herda da classe Funcionario e define os campos:
Obs: Para poder usar os
atributos DataContrat
e DataMember talvez você |
Abaixo temos a definição da classe Consultor :
using System.Runtime.Serialization; namespace SilverlightFuncionarios.Web { public class Consultor : Funcionario { [DataMember] public CarroTipo Carro { get; set; } } } |
A classe Consultor
herda da classe Funcionario e define o campo:
Obs:Para poder usar os
atributos DataContrat
e DataMember talvez você |
A seguir temos o código da classe Administrativo:
namespace SilverlightFuncionarios.Web { public class Administrativo : Funcionario { } } |
A classe Administrativo
herda da classe Funcionario ; |
Finalmente o código da enumeração CarroTipo:
namespace SilverlightFuncionarios.Web { public enum CarroTipo { Esportivo, Passeio, Utilitario } } |
Nosso próximo objetivo será criar uma classe chamada FuncionarioRepositorio que será usada para carregar os dados de exemplo. Em uma aplicação real geralmente carregaríamos os dados de um banco de dados. Para criar a classe repita o procedimento usado para criar a classe Funcionario.
O código desta classe é definido como:
using System; using System.Collections.Generic; namespace SilverlightFuncionarios.Web { public class FuncionarioRepositorio { private static List<Funcionario> todosFuncionarios; public FuncionarioRepositorio() { carregaFuncionarios(); } private void carregaFuncionarios() { if (todosFuncionarios == null) { todosFuncionarios = new List<Funcionario>() { new Gerente() { funcionarioid=1, nome="Macoratti", email="macoratti@yahoo.com", carro=CarroTipo.Esportivo, gratificacao=10000.00, nascimento=new DateTime(1960, 1, 1) }, new Consultor() { funcionarioid=2, nome="Mario", email="mario@bol.com.br", carro=CarroTipo.Passeio, nascimento=new DateTime(1976, 11, 9) }, new Consultor() { funcionarioid=3, nome="Janice", email="janice@uol.com.br", carro=CarroTipo.Utilitario, nascimento=new DateTime(1983, 3, 12) }, new Consultor() { funcionarioid=4, nome="Jefferson", email="jeff@net.com", carro=CarroTipo.Passeio, nascimento=new DateTime(1984, 6, 7) }, new Administrativo() { funcionarioid=5, nome="Miriam", email="miriam@uol.com.br", nascimento=new DateTime(1970, 9, 22) }, new Administrativo() { funcionarioid=6, nome="Jose", email="jose@bol.com.br", nascimento=new DateTime(1973, 2, 19) }, }; }
public List<Funcionario>
TodosFuncionarios |
A classe FuncionarioRepositorio
usa uma lista estática de funcionários: private static List<Funcionario> todosFuncionarios; |
Agora que definimos as classes do nosso domínio e a classe que irá popular os objetos da nossa classe vamos incluir um serviço WCF no projeto web;
Selecione o projeto SilverLightFuncionarios.web e no menu Project clique em Add New Item;
Na caixa de diálogo Add New Item veremos duas opções para adicionar um serviço WCF:
Vamos selecionar o item SilverLight-enabled WCF Service e informe o nome FuncionarioService e clique no botão Add;
Será criado um arquivo FuncionarioService.csv conforme a figura abaixo:
Vamos fazer alguns ajustes no arquivo FuncionarioService gerado para habilitar os serviços WCF.
O serviço deverá apresentar dois métodos:
Cada método deverá estar disponível no lado do cliente e deverá ter o atributo OperacionContract que indica que o método define uma operação que é parte de um contrato de serviço em uma aplicação;
Dessa forma o código implementado no serviço deverá chamar o método TodosFuncionarios da classe FuncionarioRepositorio conforme abaixo:
using System.Linq; using System.ServiceModel; using System.ServiceModel.Activation; using System.Collections.Generic; namespace SilverlightFuncionarios.Web { [ServiceContract(Namespace = "")] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class FuncionarioService { [OperationContract] public List<Funcionario> GetTodosFuncionarios() { return new FuncionarioRepositorio().TodosFuncionarios; } [OperationContract] public Funcionario GetFuncionarioPorId(int funciid) { return new FuncionarioRepositorio().TodosFuncionarios.Where (e => e.funcionarioid == funciid).FirstOrDefault(); } } } |
Com isso encerramos os ajustes do lado do cliente e podemos partir para o lado do servidor. Antes de continuar compile a aplicação. (Build)
Selecione o projeto SilverLight SilverLightFuncionarios e no menu Project clique em Add Service Reference;
A seguir clique no botão - Discover - e selecione : Services in Solution;
Se o serviço estiver disponível ele deverá aparecer na caixa Services;
Se o serviço puder ser conectado sem erros, ele irá aparecer na lista de serviços e em Operations serão exibidos os nomes dos métodos criados;
A seguir selecione o serviço e informe o nome FuncionarioService e clique no botão OK;
Depois de clicar no botão OK, Visual Web Developer 2010 Express tentará construir um proxy que é mais ou menos uma cópia do lado do cliente da classe de serviço.
Na janela Solution Explorer o serviço poderá ser visto conforme mostra a figura a seguir:
Vamos agora cuidar da interface onde iremos usar o serviço criado.
Selecione o projeto SilverLightFuncionarios e no menu Project clique em Add Reference e inclua referência a System.Windows.Controls.Data;
Em seguida clique com o botão direito do mouse sobre o projeto e selecione Add -> New Folder e informe o nome Converters para a pasta criada;
Em seguida vamos incluir dois arquivos que serão usados como conversores de data para o campo Nascimento e moeda para o campo Gratificação:
CurrencyConverter.cs
ShortDateConverter.cs
Obs: Não vou entrar em detalhes nos conversores, irei apenas usá-los no projeto. Se você quiser saber mais sobre eles consulte o meu artigo: http://www.macoratti.net/10/03/wpf_cvd1.htm
Selecione o arquivo MainPage.xaml e a partir da ToolBox inclua os seguintes controles conforme o leiaute da figura abaixo;
A seguir temos a exibição do código XAML correspondente:
|
< UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"x:Class="SilverlightFuncionarios.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" xmlns:local="clr-namespace:SilverlightFuncionarios.Converters" Loaded="UserControl_Loaded" d:DesignHeight="450" d:DesignWidth="550" DataContext="{Binding}"> <UserControl.Resources> <local:ShortDateConverter x:Key="localShortDateConverter"></local:ShortDateConverter> <local:CurrencyConverter x:Key="localCurrencyConverter"></local:CurrencyConverter> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White" Height="450" Width="550"> <Grid.RowDefinitions> <RowDefinition Height="60"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock x:Name="TitleTextBlock" Grid.Row="0" Grid.ColumnSpan="2" Text="Macoratti.net - SilverLight" FontSize="24" FontWeight="Bold" HorizontalAlignment="Center"></TextBlock> <StackPanel Grid.Row="1" Background="Bisque" > <Grid Margin="3" HorizontalAlignment="Center" > <Grid.RowDefinitions> <RowDefinition Height="50"></RowDefinition> <RowDefinition Height="300"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="3*"></ColumnDefinition> <ColumnDefinition Width="2*"></ColumnDefinition> </Grid.ColumnDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal" Grid.ColumnSpan="2"> <TextBlock x:Name="FunciIDTextBlock" VerticalAlignment="Center" Margin="2">Cod. Funci.::</TextBlock> <TextBox x:Name="FunciIDTextBox" HorizontalAlignment="Left" Width="200" VerticalAlignment="Center" Margin="2"></TextBox> <Button x:Name="ProcuraFunciButton" Click="ProcuraFunciButton_Click" Margin="2" Content="Procurar Funci" Width="120" Height="30" HorizontalAlignment="Center"></Button> <TextBlock x:Name="ErrorTextBlock" Foreground="Red"></TextBlock> </StackPanel> <data:DataGrid x:Name="FunciDataGrid" Grid.Row="1" SelectionChanged="FunciDataGrid_SelectionChanged" CanUserReorderColumns="False"IsReadOnly="True" AutoGenerateColumns="False" Width="316" Margin="0,0,3,0"> <data:DataGrid.Columns> <data:DataGridTextColumn Binding="{Binding funcionarioid}" Header="Cod.Funci"></data:DataGridTextColumn> <data:DataGridTextColumn Binding="{Binding nome}" Header="Nome"></data:DataGridTextColumn> <data:DataGridTextColumn Binding="{Binding email}" Header="Email"></data:DataGridTextColumn> </data:DataGrid.Columns> </data:DataGrid> <Grid x:Name="DetailGrid" Grid.Row="1" Grid.Column="1" Width="219" Margin="3,3,-10,3"> <Grid.RowDefinitions> <RowDefinition Height="20"></RowDefinition> <RowDefinition Height="20"></RowDefinition> <RowDefinition Height="20"></RowDefinition> <RowDefinition Height="20"></RowDefinition> <RowDefinition Height="20"></RowDefinition> <RowDefinition Height="20"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"></ColumnDefinition> <ColumnDefinition Width="100"></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock x:Name="FunciIdTextBlock" Grid.Row="0" Grid.Column="0" Text="Cod. Funci:" FontWeight="Bold"></TextBlock> <TextBlock x:Name="FunciIdValueTextBlock" Grid.Row="0" Grid.Column="1" Text="{Binding funcionarioid}"></TextBlock> <TextBlock x:Name="NomeTextBlock" Grid.Row="1" Grid.Column="0" Text="Nome:" FontWeight="Bold"></TextBlock> <TextBlock x:Name="NomeTextBlockValue" Grid.Row="1" Grid.Column="1" Text="{Binding nome}" ></TextBlock> <TextBlock x:Name="EmailTextBlock" Grid.Row="2" Grid.Column="0" Text="Email:" FontWeight="Bold"></TextBlock> <TextBlock x:Name="EmailTextBlockValue" Grid.Row="2" Grid.Column="1" Text="{Binding email}" ></TextBlock> <TextBlock x:Name="NascimentoTextBlock" Grid.Row="3" Grid.Column="0" Text="Nascimento:" FontWeight="Bold"></TextBlock> <TextBlock x:Name="NascimentoTextBlockValue" Grid.Row="3" Grid.Column="1" Text="{Binding nascimento, Converter={StaticResource localShortDateConverter}}" ></TextBlock> <TextBlock x:Name="GratificacaoTextBlock" Grid.Row="4" Grid.Column="0" Text="Gratificação:" FontWeight="Bold"></TextBlock> <TextBlock x:Name="GratificacaoTextBlockValue" Grid.Row="4" Grid.Column="1" Text="{Binding gratificacao, Converter={StaticResource localCurrencyConverter}}" ></TextBlock> <TextBlock x:Name="CarroTextBlock" Grid.Row="5" Grid.Column="0" Text="Carro:" FontWeight="Bold"></TextBlock> <TextBlock x:Name="CarroTextBlockValue" Grid.Row="5" Grid.Column="1" Text="{Binding carro}" ></TextBlock> </Grid> </Grid> </StackPanel> </Grid></ UserControl>
|
Na interface teremos as informações dos funcionários exibidas no controle DataGrid e os seus detalhes nos controles TextBlock conforme o funcionário selecionado.
Observe o uso dos conversores localShortDateConverter e localCurencyConverter para os campos nascimento e gratificação.
Obs: Não vou entrar em detalhes nos conversores, irei apenas usá-los no projeto. Se você quiser saber mais sobre eles consulte o meu artigo: http://www.macoratti.net/10/03/wpf_cvd1.htm
Observe que estamos os recursos do DataBinding vinculando cada controle ao nome do membro definido na classe de domínio.
A classe
Binding esta no namespace System.Windows.Data
e é responsável por manter a comunicação entre a origem e o destino,
expondo uma série de propriedades que nos permite customizar o comportamento dessa comunicação. Entre as principais propriedades, temos:
|
Dessa forma temos a interface pronto para ser usada, receber e exibir as informações de funcionários.
Na segunda parte do artigo vamos implementar o código no arquivo MainPage.xaml.cs referente ao evento Click do botão - Procurar Funci - que irá chamar a rotina carregaTodosFuncionarios responsável por usar o serviço criado para obter as informações dos funcionários.
Aguarde a segunda parte do artigo : SilverLight 4 - Invocando um serviço que expõe dados (C#) - parte 2
Eu sei é apenas SilverLight 4 , mas eu gosto...
Referências: