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
ser herdada por outra classe;

- 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
serializador, como o
DataContractSerializer. Para fazer o seu tipo serializável, os autores do tipo devem
definir um contrato de dados para o seu tipo.

- O atributo DataMember usado nos campos da classe será incluído no tipo e poderá ser enviado
via rede para o lado do cliente;

O DataMember quando aplicado ao membro de um tipo, especifica que o membro faz parte de um contrato
de dados e é serializável pelo
DataContractSerializer.

- O atributo KnowType marca os tipos a serem incluídos no processo de serialização;

O atributo de classe KnownType permite que você especifique, antecipadamente, os tipos que devem ser
incluídos para apreciação durante a desserializaçã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:
  • Gratificacao
  • Carro

Obs: Para poder usar os atributos DataContrat e DataMember talvez você
terá que incluir uma referência a System.RunTime.Serialization no
projeto web;

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:
  • Carro

Obs:Para poder usar os atributos DataContrat e DataMember talvez você
terá que incluir uma referência a System.RunTime.Serialization no
projeto web;

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
         {
            get
            {
                return 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:

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:
  • ElementName: define o nome do elemento que servirá como fonte. Utilize esta propriedade quando desejar preencher uma outra propriedade com o valor de um controle do WPF.
  • Mode: determina a direção das informações.
  • NotifyOnSourceUpdated: valor boleano indicando se o evento SourceUpdated é disparado quando alguma atualização na fonte das informações ocorrer.
  • NotifyOnTargetUpdated: valor boleano indicando se o evento SourceUpdated é disparado quando alguma atualização no destino das informações ocorrer.
  • Path: espefica o nome da propriedade que será exibida.
  • RelativeSource: especifica uma fonte de forma relativa à posição do objeto atual.
  • Source: define o nome do objeto que servirá como fonte. Utilize esta propriedade quando desejar preencher com uma instância de um objeto.
  • XPath: a mesma finalidade da propriedade Path, mas define uma expressão XPath quando a fonte de informações for um arquivo Xml.

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:

José Carlos Macoratti