SilverLight 4 - Operações CRUD usando um serviço WCF


Este artigo explica como realizar as operações CRUD usando um serviço WCF em uma aplicação SilverLight 4.

Se você não conhece o SilverLight deverá ler os meus artigos da seção  SilverLight do site.

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.

Definindo o objetivo

Nosso objetivo é mostrar como realizar as operações de manutenção de dados (operações CRUD) na tabela Customers do banco de dados Northwind.mdf usando um serviço WCF.

Teremos uma aplicação cliente Silverlight que utilizará o controle DataGrid para exibir os dados da tabela Customers conforme mostra a figura abaixo:

Os recursos usados são:

Criando o projeto

Abra o Visual Web Developer 2010 Express Edition e crie um novo projeto usando a linguagem C# , o Template SilverLight escolhendo o modelo SilverLight Application e informando o nome SilverLight_CRUD_WCF;

Na janela a seguir vamos selecionar para que a hospedagem seja feita no Web Site e escolher a versão 4 do SilverLight;

Será criado uma solução com dois projetos: o Projeto SilverLight:SilverLight_CRUD_WCF  e o projeto Web: SilverLight_CRUD_WCF.Web;

A seguir vamos definir a classe Customer que será usada para representar a nossa tabela Customers do banco de dados Northwind.mdf e que será o domínio da nossa aplicação.

Selecione o projeto SilverLight_CRUD_WCF.Web e no menu Project clique em Add Class;

A seguir selecione o template Class e informe o nome Customer.cs e clique em Add;

Defina o seguinte código na classe Customer:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;	
namespace SilverlightCRUDWCF.Web
{
    public class Customers
    {
        public string CustomerID { get; set; }
        public string CompanyName { get; set; }
        public string ContactName { get; set; }
        public string ContactTitle { get; set; }        
    }
}

Note que estamos definindo as propriedades com o mesmo nome das colunas da tabela Customers e fizemos isso apenas para 4 colunas da tabela apenas para simplificar a aplicação.

Criando o serviço WCF

A primeira etapa para criar um serviço WCF e efetuar a definição do contrato pois é o contrato que vai definir quais operações serão expostas pelo serviço, que informações são necessárias para que essas operações sejam executadas e qual o tipo de retorno esperado.

Obs: O contrato é uma interface que contém as assinaturas dos métodos que serão expostos. A interface deverá ser decorada com o atributo: ServiceContract

Existem 3 tipos de contratos WCF :

Contratos de serviços (Service Contracts) - Descrevem as operações que um serviço pode realizar. (Mapeia os tipos CLR para WSDL). Será detalhado mais abaixo. Exemplo:

<ServiceContract()> _
Public Interface IService
    <OperationContract()> _
    Function GetData(ByVal value As Integer) As String
    <OperationContract()> _
    Function GetDataUsingDataContract(ByVal composite As CompositeType) As CompositeType
    ' TODO: Add your service operations here
End Interface

Contratos de Dados (Data Contracts) - Descreve a estrutura de dados usada no serviço. (Mapeia tipos CLR para XSD).  Um Data Contract é um acordo formal entre um serviço e um cliente que descreve os dados que serão trocados entre ambos. Para estabelecer a comunicação, o cliente e o serviço não necessitam trocar necessariamente os mesmos tipos de dados, devem trocar apenas os mesmos data contracts.

Um Data Contract especifica para cada parâmetro ou tipo de retorno qual informação será serializada (convertida em XML) para ser trocada.  Os DataContracts são definidos através de classes e uma classe DataContract deverá ser decorada com o atributo DataContract e os campos e propriedades que o tipo possui devem ser decorados com o atributo DataMember. (A partir do service pack  versão 3.5 da plataforma .NET isso não é mais obrigatório)

' Use a data contract as illustrated in the sample below to add composite types to service operations.
<DataContract()> _
Public Class CompositeType
    Private boolValueField As Boolean
    Private stringValueField As String
    <DataMember()> _
    Public Property BoolValue() As Boolean
        Get
            Return Me.boolValueField
        End Get
        Set(ByVal value As Boolean)
            Me.boolValueField = value
        End Set
    End Property
End Class

Contratos de Mensagens (Message Contracts) - Define a estrutura de mensagens usadas no serviço. (Mapeia os tipos CLR para mensagens SOAP)

Os contratos de mensagens permitem ao serviço interagir diretamente com mensagens e podem ser tipados ou não tipados. Eles são úteis em casos de interoperabilidade e quanto existe um formato de mensagem com o qual devemos dar conformidade e/ou customizar.

Por meio dos contratos de mensagens podemos ter um controle maior sobre a estrutura de mensagem SOAP usada pelo WCF efetuando uma personalização dos componentes da mensagem.

Os Contratos de Mensagens são definidos em classes e as mesmas devem ser decoradas com o atributo MessageContract. Além disso uma classe definida com um contrato de Mensagem deve possuir um construtor sem parâmetros.

Nas classes para especificar se um campo fará parte do header ou do body da mensagem devemos usar os atributos MessageHeader ou MessageBodyMember.

Exemplo:

[MessageContract]
public class BankingTransaction
{
  [MessageHeader] public Operation operation;
  [MessageHeader] public DateTime transactionDate;
  [MessageBodyMember] private Account sourceAccount;
  [MessageBodyMember] public int amount;
}

Com base nisso vamos criar então o nosso serviço WCF, definir o contrato e implementá-lo.(Neste exemplo não vamos usar Contratos de Mensagens)

A seguir vamos criar um serviço WCF no projeto Web SilverLight_CRUD_WCF.Web. Selecione o projeto;

No menu Project clique em Add New Item e a seguir selecione SilverLight-> SIlverLight-enabled WCF Service e informe o nome CustomerService.svc e clique em Add;

Vamos definir o código abaixo na classe CustomerService.svc que representam os métodos que iremos usar para realizar as operações CRUD:

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Data.SqlClient;
namespace SilverLight_CRUD_WCF.Web
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class CustomerService
    {
         string conn = "Data Source=.\\SQLEXPRESS; DataBase=Northwind; Integrated Security=True";
        [OperationContract]
        public void DoWork()
        {
            return;
        }
        [OperationContract]
        public List<Customer> GetTodosCustomer()
        {
            List<Customer> customers = new List<Customer>();
            using (SqlConnection con = new SqlConnection(conn))
            {
                using (SqlCommand cmd = new SqlCommand())
                {
                    cmd.CommandText = "Select CustomerID,CompanyName,ContactName,ContactTitle From Customers";
                    cmd.Connection = con;
                    con.Open();
                    SqlDataReader reader = cmd.ExecuteReader();
                    while (reader.Read())
                    {
                        Customer customer = new Customer();
                        customer.CustomerID = Convert.ToString(reader["CustomerID"].ToString());
                        customer.CompanyName = Convert.ToString(reader["CompanyName"]);
                        customer.ContactName = Convert.ToString(reader["ContactName"]);
                        customer.ContactTitle = Convert.ToString(reader["ContactTitle"]);
                        customers.Add(customer);
                    }
                }
            }
            return customers;
        }

        [OperationContract]
        public int UpdateCustomer(Customer customer)
        {
            using (SqlConnection con = new SqlConnection(conn))
            {
                using (SqlCommand cmd = new SqlCommand())
                {
                    string CommandText = "UPDATE Customers" +
                                     "  SET(CompanyName = @CompanyName, ContactName = @ContactName, ContactTitle = @ContactTitle)" +
                                     " WHERE CustomerID =" + customer.CustomerID;
                    cmd.CommandText = "Update Customers SET CompanyName=@CompanyName,ContactName=@ContactName,ContactTitle=_
@ContactTitle WHERE CustomerID = @CustomerID";
                    //cmd.CommandText = CommandText;
                    cmd.Connection = con;
                    //cmd.CommandType = System.Data.CommandType.StoredProcedure;
                    cmd.Parameters.Add("@CustomerID", System.Data.SqlDbType.VarChar).Value = customer.CustomerID;
                    cmd.Parameters.Add("@CompanyName", System.Data.SqlDbType.VarChar).Value = customer.CompanyName;
                    cmd.Parameters.Add("@ContactName", System.Data.SqlDbType.VarChar).Value = customer.ContactName;
                    cmd.Parameters.Add("@ContactTitle", System.Data.SqlDbType.VarChar).Value = customer.ContactTitle;
                    con.Open();
                    return Convert.ToInt32(cmd.ExecuteScalar());
                }
            }
        }
        [OperationContract]
        public int InsertCustomer(Customer customer)
        {
            using (SqlConnection con = new SqlConnection(conn))
            {
                using (SqlCommand cmd = new SqlCommand())
                {
                    cmd.CommandText = "INSERT INTO Customers(CustomerID,CompanyName,ContactName,ContactTitle) Values(@CustomerID, _
@CompanyName,@ContactName,@ContactTitle)";
                    cmd.Connection = con;
                    //cmd.CommandType = System.Data.CommandType.StoredProcedure;
                    cmd.Parameters.Add("@CustomerID", System.Data.SqlDbType.VarChar).Value = customer.CustomerID;
                    cmd.Parameters.Add("@CompanyName", System.Data.SqlDbType.VarChar).Value = customer.CompanyName;
                    cmd.Parameters.Add("@ContactName", System.Data.SqlDbType.VarChar).Value = customer.ContactName;
                    cmd.Parameters.Add("@ContactTitle", System.Data.SqlDbType.VarChar).Value = customer.ContactTitle;
                    con.Open();
                    return Convert.ToInt32(cmd.ExecuteScalar());
                }
            }
        }
        [OperationContract]
        public bool DeleteCustomer(string customerId)
        {
            using (SqlConnection con = new SqlConnection(conn))
            {
                using (SqlCommand cmd = new SqlCommand())
                {
                    cmd.CommandText = "Delete FROM Customers Where CustomerID = @CustomerID";
                    cmd.Connection = con;
                    cmd.Parameters.Add("@CustomerID", System.Data.SqlDbType.VarChar).Value = customerId;
                    con.Open();
                    return Convert.ToBoolean(cmd.ExecuteNonQuery() > 0);
                }
            }
        }
    }
}

No código da nossa classe concreta CustomerService temos os seguintes métodos:

Estamos usando instruções SQL para realizar as operações e a string de conexão é definida no início da classe como:

 string conn = "Data Source=.\\SQLEXPRESS; DataBase=Northwind; Integrated Security=True";

Consumindo o serviço no cliente SilverLight

Vamos agora consumir o serviço WCF criado em nossa aplicação usando o projeto SilverLight . Para isso devemos realizar os seguintes procedimentos:

1 - Referenciar o serviço criado no projeto SilverLight : SilverLight_CRUD_WCF

- Clique com o botão direito do mouse sobre o projeto web e selecione Add Service Reference;
- Clique no botão Discover da janela Add Service Reference para localizar o serviço existente no projeto;
- Clique no serviço localizado para que os métodos seja exibidos;
- Informe o nome para referenciar o serviço criado. Eu vou aceitar o nome padrão ServiceReference1;

- Clique no botão OK para aceitar as ações realizadas;

2- Vamos agora criar a interface em nosso projeto SilverLight para poder exibir os Clientes e poder realizar as operações CRUD;

Nota: Verfique se o projeto SilverLight possui as referências para : System.Windows.Controls.Data.Input, System.Windows.Controls.Data e System.Windows.Data;

Para incluir clique com o botão direito do mouse sobre o item References no projeto SilverLIght e selecione as referências a partir da janela Add Reference:

Abra o arquivo MainPage.xaml e inclua o seguintes controles:

conforme mostra a figura abaixo:

O código XAML gerado pode ser visto a seguir:

<UserControl x:Class="SilverLight_CRUD_WCF.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"

xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

mc:Ignorable="d"

d:DesignHeight="500" d:DesignWidth="450"

xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

<Grid x:Name="LayoutRoot" Background="#7433B868">

<data:DataGrid AutoGenerateColumns="False" HorizontalAlignment="Left" Margin="5,5,0,0" Name="customerGrid" VerticalAlignment="Top"

SelectionMode="Single" AllowDrop="True" LoadingRow="customerGrid_LoadingRow"></data:DataGrid>

<sdk:Label Height="20" HorizontalAlignment="Left" Margin="22,350,0,0" Name="label1" VerticalAlignment="Top" Width="106" Content="Nome Empresa :" />

<TextBox Height="23" HorizontalAlignment="Left" Margin="134,347,0,0" Name="CompanyNametextBox" VerticalAlignment="Top" Width="160" />

<sdk:Label Height="19" HorizontalAlignment="Left" Margin="22,375,0,0" Name="label2" VerticalAlignment="Top" Width="93" Content="Nome Contato :" />

<TextBox Height="23" HorizontalAlignment="Left" Margin="133,376,0,0" Name="ContactNametextBox" VerticalAlignment="Top" Width="161" />

<sdk:Label Height="24" HorizontalAlignment="Left" Margin="22,403,0,0" Name="label3" VerticalAlignment="Top" Width="106" Content="Título Contato :" />

<TextBox Height="23" HorizontalAlignment="Left" Margin="133,404,0,0" Name="ContactTitletextBox" VerticalAlignment="Top" Width="161" />

<Button Content="Atualizar" Height="23" HorizontalAlignment="Left" Margin="210,441,0,0" Name="Updatebutton" VerticalAlignment="Top" Width="75" Click="UpdateButton_Click" />

<Button Content="Deletar" Height="23" HorizontalAlignment="Left" Margin="288,441,0,0" Name="Deletebutton" VerticalAlignment="Top" Width="75" Click="Deletebutton_Click" />

<Button Content="Inserir" Height="23" HorizontalAlignment="Left" Margin="132,441,0,0" Name="Insertbutton" VerticalAlignment="Top" Width="75" Click="Insertbutton_Click" />

<sdk:Label Height="18" HorizontalAlignment="Left" Margin="22,322,0,0" Name="label4" VerticalAlignment="Top" Width="106" Content="Cliente ID :" />

<TextBox Height="23" HorizontalAlignment="Left" Margin="134,317,0,0" Name="CustomerIDtextBox" VerticalAlignment="Top" Width="161" IsEnabled="True" />

<sdk:DataPager Height="26" HorizontalAlignment="Left" Source="{Binding Path=ItemsSource,ElementName=customerGrid}" Margin="226,271,0,0" Name="dataPager1" PageSize="10" VerticalAlignment="Top" Width="200" />

<sdk:Label Height="28" HorizontalAlignment="Left" Margin="306,315,0,0" Name="label5" VerticalAlignment="Top" Width="144" Content="Nota: Chave Primária" />

</Grid>

</UserControl>

 

3- No arquivo code-behind MainPage.xaml.vb vamos definir o código que vai usar os métodos definidos na classe

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Data;
using SilverLight_CRUD_WCF.ServiceReference1;
namespace SilverLight_CRUD_WCF
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            customerGrid.Columns.Add(new DataGridTextColumn
            {
                Header = "Cod.Cliente",
                Binding = new Binding("CustomerID"),
            });
            customerGrid.Columns.Add(new DataGridTextColumn
            {
                Header = "Nome Empresa",
                Binding = new Binding("CompanyName"),
                Width = new DataGridLength(140)
            });
            customerGrid.Columns.Add(new DataGridTextColumn
            {
                Header = "Nome Contato",
                Binding = new Binding("ContactName"),
                Width = new DataGridLength(100)
            });
            customerGrid.Columns.Add(new DataGridTextColumn
            {
                Header = "Titulo Contato",
                Binding = new Binding("ContactTitle"),
            });
           CarregaClientesGrid();
      }
        private void CarregaClientesGrid()
        {
            CustomerServiceClient customer = new CustomerServiceClient();
            customer.GetTodosCustomerCompleted += new EventHandler<GetTodosCustomerCompletedEventArgs>(customer_GetTodosCustomersCompleted);
            customer.GetTodosCustomerAsync();
        }
        void customer_GetTodosCustomersCompleted(object sender, GetTodosCustomerCompletedEventArgs e)
        {
            PagedCollectionView pageCollectionView = new PagedCollectionView(e.Result);
            dataPager1.DataContext = pageCollectionView;
            customerGrid.ItemsSource = pageCollectionView;
        }
        private bool Validar()
        {
            if (CustomerIDtextBox.Text.Trim().Length == 0)
            {
                MessageBox.Show("O nome não pode estar em branco", "Error", MessageBoxButton.OK);
                CustomerIDtextBox.Focus();
                return false;
            }
            else if (CompanyNametextBox.Text.Trim().Length == 0)
            {
                MessageBox.Show("O nome da empresa não pode estar em branco", "Error", MessageBoxButton.OK);
                CompanyNametextBox.Focus();
                return false;
            }
            else if (ContactNametextBox.Text.Trim().Length == 0)
            {
                MessageBox.Show("O telefone não pode estar em branco", "Error", MessageBoxButton.OK);
                ContactNametextBox.Focus();
                return false;
            }
            else if (ContactTitletextBox.Text.Trim().Length == 0)
            {
                MessageBox.Show("O título do contato não pode estar em branco", "Error", MessageBoxButton.OK);
                ContactTitletextBox.Focus();
                return false;
            }
            else
            {
                return true;
            }
        }
        private void customerGrid_LoadingRow(object sender, DataGridRowEventArgs e)
        {
            e.Row.MouseLeftButtonUp += new MouseButtonEventHandler(Row_MouseLeftButtonUp);
        }
        void Row_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Customer customer = customerGrid.SelectedItem as Customer;
            CustomerIDtextBox.Text = customer.CustomerID;
            ContactNametextBox.Text = customer.ContactName;
            ContactTitletextBox.Text = customer.ContactTitle;
            CompanyNametextBox.Text = customer.CompanyName;
        }
        private void UpdateButton_Click(object sender, RoutedEventArgs e)
        {
            if (Validar())
            {
                CustomerServiceClient customerServiceClient = new CustomerServiceClient();
                customerServiceClient.UpdateCustomerCompleted += new EventHandler<UpdateCustomerCompletedEventArgs>
(customerServiceClient_UpdateCustomerCompleted);
                Customer customer = new Customer();
                customer.CustomerID = CustomerIDtextBox.Text;
                customer.CompanyName = CompanyNametextBox.Text;
                customer.ContactName = ContactNametextBox.Text;
                customer.ContactTitle = ContactTitletextBox.Text;
                customerServiceClient.UpdateCustomerAsync(customer);
            }
        }
        void customerServiceClient_UpdateCustomerCompleted(object sender, UpdateCustomerCompletedEventArgs e)
        {
            if (e.Result > -1)
            {
                MessageBox.Show("Registro atualizado com sucesso", "Atualizar", MessageBoxButton.OK);
                LimpaTextBox();
                CarregaClientesGrid();
            }
        }
        private void LimpaTextBox()
        {
            CustomerIDtextBox.Text = string.Empty;
            CompanyNametextBox.Text = string.Empty;
            ContactNametextBox.Text = string.Empty;
            ContactTitletextBox.Text = string.Empty;
            CustomerIDtextBox.Focus();
        }
        private void Deletebutton_Click(object sender, RoutedEventArgs e)
        {
            if (CustomerIDtextBox.Text == "")
            {
                MessageBox.Show("Selecione um registro para deletar", "Deletar", MessageBoxButton.OK);
            }
            else
            {
                if (MessageBox.Show("Confirma exclusão deste registro ? ", "Deletar", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
                {
                    CustomerServiceClient customerServiceClient = new CustomerServiceClient();
                    customerServiceClient.DeleteCustomerCompleted += new EventHandler<DeleteCustomerCompletedEventArgs>(customer_DeleteCustomerCompleted);
                    customerServiceClient.DeleteCustomerAsync(CustomerIDtextBox.Text);
                }
            }
        }
        void customer_DeleteCustomerCompleted(object sender, DeleteCustomerCompletedEventArgs e)
        {
            if (e.Result)
            {
                MessageBox.Show("Registro Deletado", "Deletar", MessageBoxButton.OK);
                LimpaTextBox();
                CarregaClientesGrid();
            }
            else
            {
                MessageBox.Show("A exclusão falhou", "Deletar", MessageBoxButton.OK);
            }
        }
        private void Insertbutton_Click(object sender, RoutedEventArgs e)
        {
            if (Validar())
            {
                CustomerServiceClient customerServiceClient = new CustomerServiceClient();
                customerServiceClient.InsertCustomerCompleted += new EventHandler<InsertCustomerCompletedEventArgs>
(customerServiceClient_InsertCustomerCompleted);
                Customer customer = new Customer();
                customer.CustomerID = CustomerIDtextBox.Text;
                customer.CompanyName = CompanyNametextBox.Text;
                customer.ContactName = ContactNametextBox.Text;
                customer.ContactTitle = ContactTitletextBox.Text;
                customerServiceClient.InsertCustomerAsync(customer);
            }
        }
        void customerServiceClient_InsertCustomerCompleted(object sender, InsertCustomerCompletedEventArgs e)
        {
            if (e.Result > -1)
            {
                MessageBox.Show("Registro incluído com sucesso", "Inserir", MessageBoxButton.OK);
                LimpaTextBox();
                CarregaClientesGrid();
            }
        }
    }
}

Neste código temos o tratamento dos eventos de cada um dos botões de comando.

Na figura a abaixo vemos o projeto sem execução realizando uma atualização de um registro:

A seguir vemos o processo de inclusão de um registro e a exibição do registro incluído:

Como você pode constatar não é preciso ser um expert para realizar as operações básicas de manutenção de dados em uma aplicação SilverLight usando um serviço WCF.

Pegue o projeto completo aqui: SilverLight_CRUD_WCF.zip

"Portanto agora nenhuma condenação há para os que estão em Cristo Jesus, que não andam segundo a carne, mas segundo o espírito." Romanos 8:1

Referências:


José Carlos Macoratti