Easy-Net 016 Tfdxzasg
Easy-Net 016 Tfdxzasg
2
Este artigo tem o objetivo de demonstrar,
em termos gerais, como o recurso do .NET
Framework denominado LINQ To XML pode
ser utilizado em operações envolvendo a
leitura e / ou escrita de informações no
formato XML, empregando-se para isto a
linguagem C#.
A manipulação de dados em XML tende a ser uma tarefa trabalhosa, haja vista
a necessidade de se efetuarem numerosas operações para o tratamento de
informações dispostas em uma estrutura hierárquica. Em virtude disto, não é
fato raro que funcionalidades destinadas à manipulação de XML sejam
implementadas através de extensos trechos de código. Procurando oferecer
uma alternativa que reduza a complexidade inerente a este tipo de processo, a
plataforma .NET disponibiliza o mecanismo denominado LINQ to XML para
a realização de operações sobre dados no formato XML.
LINQ to XML
Na edição anterior iniciou-se a discussão sobre LINQ, com isto sendo feito
por meio da demonstração de como consultas poderiam ser efetuadas sobre
coleções de objetos (LINQ to Objects). Neste novo artigo será explicado como
a manipulação de informações no formato XML pode ser simplificada com
LINQ, empregando-se neste caso o recurso conhecido como LINQ to XML.
Nota do DevMan
• XDocument;
• XElement;
• XAttribute.
...
...
Nota do DevMan
DTD (do inglês Document Type Definition) é um tipo de arquivo que pode co
nter as regras que definem a estrutura de um documento contendo marcações.
Trata-se de um recurso que pode ser empregado tanto com XML, quanto com
a linguagem XHTML (formato que mescla características de HTML com XM
L).
...
XmlDocument xmldoc = new XmlDocument();
XmlDeclaration xmldec = xmldoc.CreateXmlDeclaration(
"1.0", "utf-8", "");
xmldoc.AppendChild(xmldec);
europa.AppendChild(paises);
continentes.AppendChild(europa);
xmldoc.AppendChild(continentes);
...
...
var dadosPaises =
(from e in xmldoc.Element("Continentes")
.Element("Continente").Element("Paises")
.Elements("Pais")
select new
{
Nome = e.Element("Nome").Value,
Capital = e.Element("Capital").Value
});
foreach (var pais in dadosPaises)
{
string nome = pais.Nome;
string capital = pais.Capital;
...
...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace TesteLinqToXml
{
public static class AgendaWriter
{
private static XElement CriarElementoEmpresa(
string codigo,
string nome,
string cidade,
string estado)
{
return new XElement("Empresa",
new XAttribute("Codigo", codigo),
new XElement("Nome", nome),
new XElement("Cidade", cidade),
new XElement("Estado", estado));
}
xmldoc.Save("agenda.xml");
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TesteLinqToXml
{
public class Contato
{
public string Nome { get; set; }
public string Departamento { get; set; }
public string Telefone { get; set; }
public string Email { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TesteLinqToXml
{
public class Empresa
{
public string Codigo { get; set; }
public string Nome { get; set; }
public string Cidade { get; set; }
public string Estado { get; set; }
public Empresa()
{
_Contatos = new List<Contato>();
}
}
}
List faz uso do tipo genérico List o qual, por sua vez, deriva de IEnumerable.
T representa, para a classe List e a interface IEnumerable, qualquer tipo válido
dentro do .NET Framework. No caso da classe Empresa, utilizou-se Contato
como tipo base para a definição da coleção em questão.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace TesteLinqToXml
{
public class AgendaReader
{
private XDocument _xmlAgenda;
return empresa;
}
return
(from e in dadosEmpresa.
Element("Contatos").Elements("Contato")
select new Contato()
{
Nome = e.Element("Nome").Value,
Departamento = e.Element("Departamento").Value,
Telefone = e.Element("Telefone").Value,
Email = e.Element("Email").Value
}).ToList();
}
}
}
...
...
...
lstSelecaoEmpresa.Items.AddRange(
_agenda.CodigosEmpresas.ToArray());
lstSelecaoEmpresa.SelectedIndex = -1;
}
...
...
txtNomeEmpresa.Text = empresa.Nome;
txtCidade.Text = empresa.Cidade;
txtEstado.Text = empresa.Estado;
grdContatos.DataSource = empresa.Contatos;
grdContatos.Columns["Nome"].Width = 200;
grdContatos.Columns["Email"].Width = 200;
}
...
...
...
Já o botão btnCarregarArquivo tem por objetivo exibir a tela de consulta às
informações da agenda. Para isto, o formulário invocado necessitará do
arquivo agencia.xml, sendo que o mesmo só será exibido caso este documento
realmente exista no diretório em que se encontra a aplicação de testes
(Listagem 13): esta checagem é feita por meio do método Exists da classe
File (namespace System.IO). Ao final do evento Click, caso não existam
restrições, o formulário FormConsultaAgenda é acionado por meio do método
ShowModal.
...
...
...
...
Conclusão
O uso de XML está disseminado pelos mais variados tipos de aplicações de
software. Arquivos neste formato constituem um importante meio de
integração entre sistemas. Diante de fatos como este, é mais do que certo que
o desenvolvimento de novas soluções computacionais envolva, de alguma
forma, a utilização de informações neste padrão.
LINQ
http://msdn.microsoft.com/en-us/netframework/aa904594
LINQ to XML
http://msdn.microsoft.com/en-us/library/bb387098.aspx
O Data Binding é útil para criar projetos de softwares mais flexíveis, aplicar
conceitos de vínculo de dados, possibilitar um desenvolvimento mais
produtivo e com códigos mais enxutos. Elimina a necessidade de se escrever
dezenas de linhas de código para apresentar dados ao usuário, recuperar
alterações e enviar a uma classe de acesso a dados, por exemplo.
O Data Binding não é uma total novidade para os desenvolvedores .NET. Ele
tem acompanhado e auxiliado muito no desenvolvimento de aplicativos em
Windows Forms e também ASP.NET, porém em WPF ele foi melhorado
muito em questão de funcionalidades e performance. Seu conceito básico
ainda é o mesmo: vincular dados de uma fonte para um destino, ou seja, ao
contrário de você ter que alimentar seus controles na interface de usuário um a
um via código, ou então percorrer coleções via código e jogar dados em um
ListView ou DataGrid, o Data Binding permite que você apenas informe de
onde ele deve obter o valor para ser alimentado em algum lugar específico.
Imagine que você tenha uma ListView em seu formulário e você possui em
seu código uma coleção de dados em memória. Ao invés de percorrer esta
coleção criando um ListViewItem para cada iteração percorrida, você possa
fazer algo mais prático como simplesmente dizer: “ListView, obtenha seus
itens daqui, mantenha seus valores atualizados e formate-os para ficar assim.”.
O Data Binding fornece esta funcionalidade e muito mais ainda.
O Data Binding possui como sua classe principal a Binding, que é responsável
por vincular as propriedades do objeto de fonte com as propriedades do objeto
de destino de forma a manter um canal de comunicação aberto entre estes,
permitindo a sincronização de dados e atualização de um objeto a partir do
outro conforme configuração definida.
01 <Window x:Class="ListBoxBinding.MainWindow"
02
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
03 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
04 Title="MainWindow" Height="350" Width="525">
05 <Grid>
06 <Grid.RowDefinitions>
07 <RowDefinition Height="*" />
08 <RowDefinition Height="*" />
09 </Grid.RowDefinitions>
10 <TreeView x:Name="treeView" Margin="20">
11 <TreeViewItem Header="Item Pai 1">
12 <TreeViewItem Header="Item Filho 1" />
13 <TreeViewItem Header="Item Filho 2" />
14 <TreeViewItem Header="Item Filho 3" />
15 </TreeViewItem>
16 <TreeViewItem Header="Item Pai 2">
17 <TreeViewItem Header="Item Filho 4" />
18 </TreeViewItem>
19 <TreeViewItem Header="Item Pai 3" />
20 </TreeView>
21 <TextBlock x:Name="textBlock" Grid.Row="1" Height="20"
Width="100"
22 HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="20,10"/>
23 </Grid>
24 </Window>
01 <Window x:Class="ListBoxBinding.MainWindow"
02
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
03 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
04 Title="MainWindow" Height="350" Width="525">
05 <Grid>
06 <Grid.RowDefinitions>
07 <RowDefinition Height="*" />
08 <RowDefinition Height="*" />
09 </Grid.RowDefinitions>
10 <TreeView x:Name="treeView" Margin="20">
11 <TreeViewItem Header="Item Pai 1">
12 <TreeViewItem Header="Item Filho 1" />
13 <TreeViewItem Header="Item Filho 2" />
14 <TreeViewItem Header="Item Filho 3" />
15 </TreeViewItem>
16 <TreeViewItem Header="Item Pai 2">
17 <TreeViewItem Header="Item Filho 4" />
18 </TreeViewItem>
19 <TreeViewItem Header="Item Pai 3" />
20 </TreeView>
21 <TextBlock x:Name="textBlock" Grid.Row="1" Height="20"
Width="100"
22 HorizontalAlignment="Left"
VerticalAlignment="Top"
23 Margin="20,10"
24 Text="{Binding ElementName=treeView,
Path=SelectedItem.Header}"/>
25 </Grid>
26 </Window>
Para utilizar o Binding com a propriedade Source você deverá ter informado a
propriedade x:Name da TreeView, pois caso você tenha informado o nome
através da propriedade Name apenas, na compilação de seu formulário você
receberá uma exception informando que o provider não conseguiu encontrar o
objeto com o nome especificado.
Podemos vincular qualquer dado através do Data Binding. Vamos supor que
temos uma classe chamada Produto em nossa aplicação conforme a Listagem
5. Ainda em nossa aplicação, temos um formulário básico conforme Figura 1,
o qual contém o código XAML da Listagem 6, onde são apresentadas as
informações detalhadas de cada Produto. Para fins de exemplo, no evento de
inicialização do nosso formulário vamos criar um novo produto para
podermos vincular os dados. Em uma aplicação real você provavelmente faria
a chamada de um método de consulta de um produto a partir de uma coleção
filtrando pelo código do mesmo, por exemplo. O código da instância do nosso
novo Produto é apresentado na Listagem 7.
01 <Window x:Class="ListBoxBinding.frmProduto"
02
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
03 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
04 Title="Produto" Height="300" Width="400">
05 <Grid>
06 <Grid.RowDefinitions>
07 <RowDefinition Height="35" />
08 <RowDefinition Height="35" />
09 <RowDefinition Height="35" />
10 <RowDefinition Height="35" />
11 <RowDefinition Height="35" />
12 <RowDefinition Height="*" />
13 </Grid.RowDefinitions>
14 <Grid.ColumnDefinitions>
15 <ColumnDefinition Width="130" />
16 <ColumnDefinition Width="*" />
17 </Grid.ColumnDefinitions>
18 <TextBlock Text="Código:" Grid.Column="0" Grid.Row="0"
Margin="5"
19 HorizontalAlignment="Left"
VerticalAlignment="Center" />
20 <TextBlock Text="Nome:" Grid.Column="0" Grid.Row="1"
Margin="5"
21 HorizontalAlignment="Left"
VerticalAlignment="Center"/>
22 <TextBlock Text="Valor:" Grid.Column="0" Grid.Row="2"
Margin="5"
23 HorizontalAlignment="Left"
VerticalAlignment="Center" />
24 <TextBlock Text="Quantidade Estoque:" Grid.Column="0"
Grid.Row="3" Margin="5"
25 HorizontalAlignment="Left"
VerticalAlignment="Center" />
26 <TextBox x:Name="txtCodigo" Grid.Column="1" Grid.Row="0"
27 HorizontalAlignment="Left"
VerticalAlignment="Center"
28 Margin="5" Width="100" />
29 <TextBox x:Name="txtNome" Grid.Column="1" Grid.Row="1"
30 HorizontalAlignment="Left"
VerticalAlignment="Center"
31 Margin="5" Width="220" />
32 <TextBox x:Name="txtValor" Grid.Column="1" Grid.Row="2"
33 HorizontalAlignment="Left"
VerticalAlignment="Center"
34 Margin="5" Width="75" />
35 <TextBox x:Name="txtQtd" Grid.Column="1" Grid.Row="3"
36 HorizontalAlignment="Left"
VerticalAlignment="Center"
37 Margin="5" Width="75" />
38 </Grid>
39 </Window>
• TwoWay: Esta forma faz com que qualquer alteração, independente de ser
na propriedade fonte ou na propriedade destino, implica na atualização da
outra. Mantém seus objetos sempre sincronizados com os mesmos valores de
propriedades (as vinculadas);
<Window>
...
<Grid>
...
<DataGrid x:Name="dataGrid" Grid.Row="4" Grid.ColumnSpan="2"
AutoGenerateColumns="True"
IsReadOnly="True">
</DataGrid>
</Grid>
</Window>
Projeto de exemplo
Antes de começarmos, para este exemplo prático, vamos utilizar o banco de
dados VentoNorte, o qual já foi muitas vezes utilizado nos artigos aqui da
Devmedia. Este banco de dados é uma versão brasileira para o banco de dados
padrão do SQL Server, o Northwind, com estruturação em português. O banco
se encontra junto com o código fonte para download deste artigo.
01 <Window x:Class="DataBindingBasico.MainWindow"
02
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
03 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
04 Title="Lista de Produtos" Height="350" Width="525">
05 <Grid>
06 <Grid.ColumnDefinitions>
07 <ColumnDefinition />
08 <ColumnDefinition Width="2*" />
09 </Grid.ColumnDefinitions>
10 <ListBox Margin="10" ItemsSource="{Binding Path=Produtos}"
Name="lstProdutos"
11 HorizontalAlignment="Stretch" Grid.Column="0"
12 SelectionChanged="lstProdutos_SelectionChanged">
13 <ListBox.ItemTemplate>
14 <DataTemplate>
15 <StackPanel Orientation="Horizontal">
16 <TextBlock Text="{Binding Path=ProdID}" />
17 <TextBlock Text=" - " />
18 <TextBlock Text="{Binding Path=ProdName}"
/>
19 </StackPanel>
20 </DataTemplate>
21 </ListBox.ItemTemplate>
22 </ListBox>
23 <Grid Grid.Column="1" x:Name="dgvDetalhes">
24 <Grid.ColumnDefinitions>
25 <ColumnDefinition />
26 <ColumnDefinition Width="2*" />
27 </Grid.ColumnDefinitions>
28 <Grid.RowDefinitions>
29 <RowDefinition />
30 <RowDefinition />
31 <RowDefinition />
32 <RowDefinition />
33 <RowDefinition />
34 <RowDefinition />
35 <RowDefinition />
36 </Grid.RowDefinitions>
37 <TextBlock Text="ID:" Grid.Column="0" Grid.Row="0"
HorizontalAlignment="Left"
38 Margin="5" VerticalAlignment="Center" />
39 <TextBlock Text="Nome:" Grid.Column="0" Grid.Row="1"
HorizontalAlignment="Left"
40 Margin="5" VerticalAlignment="Center" />
41 <TextBlock Text="Fornecedor:" Grid.Column="0"
Grid.Row="2" HorizontalAlignment="Left"
42 Margin="5" VerticalAlignment="Center" />
43 <TextBlock Text="Grupo:" Grid.Column="0" Grid.Row="3"
HorizontalAlignment="Left"
44 Margin="5" VerticalAlignment="Center" />
45 <TextBlock Text="Preço:" Grid.Column="0" Grid.Row="4"
HorizontalAlignment="Left"
46 Margin="5" VerticalAlignment="Center" />
47 <TextBlock Text="Estoque:" Grid.Column="0"
Grid.Row="5" HorizontalAlignment="Left"
48 Margin="5" VerticalAlignment="Center" />
49 <TextBlock Text="Descontinuado:" Grid.Column="0"
Grid.Row="6" HorizontalAlignment="Left"
50 Margin="5" VerticalAlignment="Center" />
51 <TextBox Grid.Column="1" Grid.Row="0" Text="{Binding
Path=ProdID, Mode=OneWay}"
52 Margin="10" Width="50"
HorizontalAlignment="Left" IsReadOnly="True" />
53 <TextBox Grid.Column="1" Grid.Row="1" Text="{Binding
Path=ProdName, Mode=OneWay}"
54 Margin="10" IsReadOnly="True" />
55 <TextBox Grid.Column="1" Grid.Row="2" Text="{Binding
Path=FornID, Mode=OneWay}"
56 Margin="10" Width="50"
HorizontalAlignment="Left" IsReadOnly="True" />
57 <TextBox Grid.Column="1" Grid.Row="3" Text="{Binding
Path=GrupoID, Mode=OneWay}"
58 Margin="10" Width="50"
HorizontalAlignment="Left" IsReadOnly="True" />
59 <TextBox Grid.Column="1" Grid.Row="4" Text="{Binding
Path=ProdPreco, Mode=OneWay}"
60 Margin="10" Width="100"
HorizontalAlignment="Left" IsReadOnly="True" />
61 <TextBox Grid.Column="1" Grid.Row="5" Text="{Binding
Path=ProdEstoque, Mode=OneWay}"
62 Margin="10" Width="100"
HorizontalAlignment="Left" IsReadOnly="True" />
63 <CheckBox Grid.Column="1" Grid.Row="6"
IsChecked="{Binding Path=ProdDescontinuado, Mode=OneWay}"
64 HorizontalAlignment="Center"
VerticalAlignment="Center" IsEnabled="False"/>
65 </Grid>
66 </Grid>
67</Window>
Como não queremos que a nossa ListBox apresente todos os campos da tabela
produtos e sim somente o código e a descrição, incluímos um DataTemplate
para ela (linha 14), que serve para definir como os dados devem ser
apresentados na Listbox. Em nosso caso, incluímos um StackPanel para
arranjar três TextBlocks de forma horizontal, sendo o primeiro vinculado à
coluna ProdID da tabela Produtos, através do DataBinding, o segundo
TextBlock contendo uma string para somente incluir um separador entre
código e nome e o terceiro TextBlock vinculado ao campo ProdName da
tabela Produtos, também através do Data Binding.
Na linha 23 incluímos uma nova Grid que estará contida dentro da coluna 1 da
Grid principal. Esta nossa segunda Grid terá o nome de dgvDetalhes e conterá
os detalhes do produto selecionado na ListBox. Para a grid de detalhes
configuramos duas colunas, sendo a segunda coluna duas vezes o tamanho da
primeira e sete linhas de tamanhos iguais, conforme pode ser verificado nas
linhas de 24 a 36. Depois de termos configurado a grid de detalhes, nas linhas
de 37 a 49 incluímos os rótulos de cada informação que será apresentada, ou
seja, cada coluna da tabela Produtos de nosso banco de dados.
Agora apenas temos que vincular os dados em nosso formulário, para isto
utilizaremos a propriedade DataContext conforme foi apresentada
anteriormente. No código fonte de nosso formulário, após chamar o método
InitializeComponent, temos que criar uma instância do nosso modelo de dados
configurado pelo Entity Framework. Para isto vamos criar uma variável do
tipo VentoNorteEntities chamada entidades. Após termos criado nossa
instância de entidades, podemos vincular na propriedade DataContext de
nossa ListBox o nosso modelo, conforme Listagem 15.
01 public MainWindow()
02 {
03 InitializeComponent();
04 VentoNorteEntities entidades = new VentoNorteEntities();
05 lstProdutos.DataContext = entidades;
06 }
Com esta alteração já temos vinculado nosso modelo na ListBox, porém ainda
precisamos apresentar os detalhes do Produto que será selecionado na mesma.
Para isto temos que implementar o evento SelectionChanged da ListBox.
Na Listagem 14 o evento já estava implementado na linha 12 então basta
criarmos o código do mesmo conforme Listagem 16.
Conclusão
Podemos verificar que o Data Binding está muito evoluído em WPF,
auxiliando o desenvolvedor a escrever códigos mais limpos e fornecendo
funcionalidade de produtividade em uma aplicação.
Links
Este artigo finaliza o mini-curso de criação de uma loja virtual com C#, VS e
ASP.NET, mostrando como requisitar dados adicionais do usuário para envio
do pedido, usando o recurso de Profiles. Mostra também como enviar as
informações do carrinho para o banco de dados, bem como enviar um email
de confirmação.
Criando uma aplicação completa passo a passo com C#, Visual Studio e
ASP.NET
Nesta última etapa, o usuário poderá informar seus dados adicionais para o
envio da compra, para isso será usado o recurso de Profiles e um recurso
interessante do CreateUserWizard, que é a possibilidade de incluir novos
“steps” ao assistente. Os dados que até agora estão na sessão do usuário, em
memória, serão finalmente persistidos no banco de dados criado na primeira
parte desta série. O processo é simples, varrer o DataSet em memória e gerar
comandos SQL de inserção. Será usado ADO.NET diretamente no form, mas
considere o uso de classes separadas para criar acesso a dados em uma
situação real, ou mesmo o uso de um framework de persistência. O ADO.NET
já foi amplamente abordado em etapas anteriores, de forma que não será
discutido aqui. O exemplo emprega classes já discutidas, como
SqlConnection, SqlCommand, DataSet e DataRow. Observações serão feitas
quando pertinente, sempre comparando com o Delphi Win32 tradicional e
VCL, objetivo principal desta série.
Profiles
Uma das técnicas mais interessantes que este exemplo usa é o recurso de
Profiles. O ASP.NET possui inúmeros recursos para manter o estado em
aplicações Web, como o simples uso de Cookies, passando por variáveis de
Sessão, Cache, ViewState e mais. Imagine a seguinte situação: o usuário entra
no seu site e customiza algumas partes da página, define algumas preferências
de navegação, usabilidade etc. Quando ele retornar ao seu site, não importa
quanto tempo isso leve, devemos ter uma forma de “lembrar” essas
preferências definidas pelo usuário. Antigamente (ASP.NET 1.x), era preciso
criar uma tabela no banco de dados com campos que definiam o perfil
(profile) do usuário. Após a sua visita, era preciso gravar suas preferências no
banco e o ID em um cookie local no browser. Quando ele retornava,
informações eram recuperadas a partir do cookie.
<add name="VARIAVEL"/>
Profile.VARIAVEL = TextBox1.Text;
Isso difere de Sessions, onde não há tipo específico, sempre temos um Object
e não há verificação em tempo de compilação:
Session[“VARIAVEL”] = TextBox1.Text;
Sem dúvida aqui está um belo exemplo para o uso de Profiles. É justamente
isso que o exemplo faz, utiliza o recurso para armazenar os dados adicionais
do usuário, além dos dados padrão já requeridos pelo sistema de membership
do ASP.NET. Nada melhor do que solicitar essas informações durante o
próprio processo de cadastro, na tela NewUser.aspx, no controle
CreateUserWizard, que agora contém alguns TextBoxs adicionais (Figura 1).
Nota do DevMan
<profile>
<properties>
<add name="FullName"/>
<add name="Address"/>
<add name="City"/>
<add name="Country"/>
<add name="ZIPCode"/>
<add name="EMAIL"/>
</properties>
</profile>
Página de Checkout
A Figura 2 mostra a página de Checkout do sistema de E-Commerce. É nessa
página que o usuário irá confirmar os dados de envio, bem como confirmar o
método de pagamento. O exemplo simplesmente simula um pagamento como
cartão de crédito, solicitando os dados básicos, já que a implementação
completa de um processo de pagamento on-line está fora do escopo deste
artigo. Os Labels vistos na tela simplesmente são alimentados com os dados
de endereço do usuário, através do Profile. Isso é feito no Page_Load quando
a página é carregada, como mostra a Listagem 3.
Figura 2. Design da página Checkout.aspx
Type Inference
var
Cds: TClientDataSet;
begin
Cds := TClientDataSet.Create(nil);
Isso tem uma explicação. Em alguns casos, uma variável não é declarada do m
esmo tipo que vai ser instanciada, mas sim de um tipo ancestral. Assim, é poss
ível aplicar técnicas de orientação a objeto como polimorfismo e abstração, al
ém de programação para interfaces. É por esse motivo, por exemplo, que oQui
ck Report aponta para umTDataSete até hoje pode suportar inúmerosenginesd
e acesso a dados, inclusive os que venham a ser criados.
O código a seguir declara uma variável do tipoTDataSet, classe base para muit
as outras classes de acesso a dados naVCL. Porém, na implementação essa var
iávelpode receber tanto umTQueryquanto um TClientDataSet, pois ambos des
cendem deTDataSet. Quando o método Post do DataSet é chamado, o compila
dor vai analisar a assinatura do método na classe declarada (TDataSet), e vai d
etectar que ele évirtual. Com isso, o verdadeiroPostque será chamado não é o
da classeTDataSet, mas deTClientDataSet, que sobrescreve o Postcomoverrid
e, configurando uma chamadapolimórfica:
E noC#?Bem, é possível fazer tudo isso sem problemas noC#, usando objetos
daFCLao invés daVCL, claro. Porém, aqui existe uma novidade. Se não formo
s usarpolimorfismo, ou seja, o tipo que vamos instanciar é o mesmo que vai se
r declarado, pode-seomitiro tipo:
// Declarando o tipo
O for each percorre o DataSet, que nada mais é que o carrinho de compras em
memória e para cada item encontrado executa um insert no banco. A Stored
Procedure retorna o ID do pedido, que é usado para ser incluído como valor
do campo da chave estrangeira na tabela de detalhes do pedido, fazendo assim
o relacionamento correto entre tabelas master e detail.
WebConfigurationManager.ConnectionStrings["LOJAConnectionString"].Conn
ectionString);
var cmd = new SqlCommand("InsertOrder", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@DATE", DateTime.Now);
cmd.Parameters.AddWithValue("@CUSTOMER",
User.Identity.Name);
cmd.Parameters.Add("@RETURN_VALUE", SqlDbType.Int);
cmd.Parameters["@RETURN_VALUE"].Direction =
ParameterDirection.ReturnValue;
con.Open();
try
{
cmd.ExecuteNonQuery();
OrderId = cmd.Parameters["@RETURN_VALUE"].Value;
var ds = Session["ShoppingCart"] as DataSet;
foreach (DataRow row in ds.Tables[0].Rows)
{
var SQL = System.String.Format(
"insert into OrderDetails (OrderId,ProductId) values
({0},{1});", OrderId, row["ProductId"]);
var cmd2 = new SqlCommand(SQL, con);
cmd2.ExecuteNonQuery();
}
}
finally
{
con.Close();
}
return Convert.ToInt32(OrderId);
}
Nota do DevMan
Um StringBuilder do .NET Framework é semelhante a um TStringList da VC
L. É usado para concatenar strings. No .NET, quando strings são concatenadas
com o operador “+”, a plataforma simplesmente descarta a string original e cri
a outra em memória, ao contrário do Delphi Win32, onde uma string cresce di
namicamente. Isso causa sérios problemas de performance, e na Web cada mil
issegundo perdido pode se tornar uma grande dor de cabeça em um Web Site
com centenas ou milhares de conexões simultâneas. O StringBuilder resolve o
problema e é tão simples de usar quanto o TStringList a VCL, senão análogo.
msg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpaut
henticate", 1);
msg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/senduse
rname", UserName);
msg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpas
sword", Password);
msg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusi
ng", 2);
msg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpser
ver", MailServer);
msg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpcon
nectiontimeout", 10);
msg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpser
verport", 587);
msg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpuse
ssl", false);
SmtpMail.Send(msg);
}
Teste final
O teste final no exemplo comprova a funcionalidade dos códigos expostos.
A Figura 3 mostra alguns produtos adicionados ao carrinho. Ao fazer o
Checkout, a Figura 4 solicita a confirmação dos dados de envio e o método e
pagamento. Finalizando a compra, o usuário recebe um email, como mostra
a Figura 5.
Conclusão
Ao longo desta série muitos recursos do Visual Studio, C# e ASP.NET foram
explorados, sempre confrontando com as metodologias tradicionais do Delphi
Win32. Não tenho dúvida que o leitor constatou que desenvolver para Web,
com ASP.NET e C#, é tão simples quanto desenvolver aplicações Desktop,
podendo ainda aproveitar muitos conhecimentos já adquiridos. Desejo aos
novos desenvolvedores Web sucesso em seus novos projetos com C# e
ASP.NET.
Links
Visual Studio
www.microsoft.com/visualstudio
Blogs do autor
http://twitter.com/guintherpauli
http://gpauli.com
A estrutura fornecida pelo WCF, assim como todas do Framework .Net, busca
providenciar todos os recursos para o desenvolvedor focar ao máximo na
funcionalidade que quer criar e não se preocupar (tanto) com os problemas
citados anteriormente.
A arquitetura do WCF
Desde a versão 2.0 do Framework .Net, a base para desenvolvimento de
serviços continua a mesma, ou seja, as classes principais são desta versão.
Isto significa que para ativar estes tipos de aplicação, se deve instalar a versão
correta do Framework. Um detalhe importante é que as versões Windows 7
para o desktop e Windows 2008 para o servidor já estão com as versões
necessárias instaladas. Outro ponto a considerar é que ao instalar a versão 3.5
a 2.0 já é instalada por padrão.
Observe que as versões 1.0, 1.1 e 2.0 também são instaladas para manter
compatibilidade. Sabendo desta arquitetura, ao fazer a hospedagem do WCF
no IIS, a configuração da aplicação ficará um pouco mais fácil de entender
principalmente quando se estiver configurando o pool de aplicações que é
como o IIS faz o gerenciamento destas. Em alguns casos, como o WCF, é
indicado usar o Classic App que usa a versão 2.0 como base.
Nota: é bom verificar a versão do Windows que está instalada. Algumas versõ
es mais básicas, apesar de terem as versões do Framework, podem não ter alg
umas ferramentas como o IIS instaladas por padrão.
O seu acesso pode ser feito de diversas maneiras sendo as mais simples
através das ferramentas administrativas, digitando “IIS” no menu iniciar
(Figura 3) ou através da opção “Executar” do menu iniciar do Windows
digitando “inetmgr”.
Figura 3. Acessando o IIS
Se o mesmo não estiver instalado, é fácil fazer isto através da opção Adicionar
Programas do painel de controle do Windows. Nesta tela, deve se abrir a
ferramenta para habilitar ou desabilitar recursos do Windows (Figura 4).
> aspnet_regiis –i
Após isto, outro passo também deve ser feito ainda no prompt, porém agora
na pasta da versão 3,0 c:\Windows\Microsoft.NET\Framework\v3.0\Windows
Communication Foundation executando o comando:
> ServiceModelReg.exe –i
<?xml version="1.0"?>
<configuration>
<system.web>
<appSettings>
<system.serviceModel>
<services>
<behaviors>
</system.serviceModel>
<startup>
</configuration>
Este recurso já resolve o problema para editar a seção appSettings, mas ainda
não é satisfatório para editar configurações do serviço. Lembrando que o
arquivo de configurações usa um documento XML que precisa obedecer um
schema, ou as configurações não serão validadas e o serviço poderá ficar
inoperante.
> SvcConfigEditor.exe
A aplicação cliente
Ao fazer a publicação do serviço usando o IIS, uma das vantagens é que este
fará o gerenciamento total da aplicação que poderá ser acessada tanto pelos
protocolos HTTP e TCP (sendo que se este último for usado, o IIS deverá ser
preparado para isto).
Como eu espero ter demonstrado, o WCF também é versátil para ser usado
com o IIS que é o cenário típico para este tipo de aplicação, embora, possua
mais detalhes. Para que estes detalhes possam ser explorados pelo leitor,
resolvi criar um pequeno serviço e demonstrar quase que passo a passo a
criação deste tipo de aplicação.
O projeto tem como objetivo, retornar quantos meses será necessário fazer
depósitos em uma conta de investimento fictícia, a uma taxa de juros mensal
pré-determinada, para que se tenha um valor montante especificado. O usuário
precisará informar o valor das parcelas mensais, o valor final desejado e a data
do início do investimento. O serviço retorna um objeto contendo:
Criando o serviço
O projeto WCF foi criado a partir do template WCF Service Library do Visual
Studio. A Figura 8 demonstra a caixa de diálogo do Visual Studio que
permite ajustar detalhes como o nome do projeto, a linguagem a ser usada (no
caso do exemplo, o C#) e também a versão do Framework.
É possível usar criar uma aplicação do zero, usando um projeto do tipo Class
Library e acrescentando referências a System.ServiceModel que é a principal
biblioteca do WCF. Só que isto costuma exigir mais esforço na hora de ajustar
o funcionamento do projeto causando atrasos. Optei usar o template do Visual
Studio e acho que é a melhor opção para a maior parte dos casos.
Ao escolher a opção do menu, uma janela se abre para que seja escolhido o
tipo do item que será acrescentado. O correto é usar a opção Interface que é a
maneira usada para definir o ServiceContract. Os detalhes da janela para a
criação da interface no projeto podem ser conferidos na Figura 10.
1 using System;
2 using System.Configuration;
3 using System.Runtime.Serialization;
4 using System.ServiceModel;
5
6 namespace InvestServiceDemo
7 {
8 [ServiceContract]
9 interface IInvestService
10 {
11 [OperationContract]
12 invest Target(double target, double monthlyQuota, DateTime
startDate);
13 }
14
Em segundo lugar, no exemplo foi usado o prefixo “I” para o nome de uma
interface. É uma convenção e pode ser feito de forma diferente. Mais uma vez,
é boa prática também seguir este padrão, já que boa parte dos programadores
faz assim e mesmo os códigos de exemplo da Microsoft seguem esta prática.
Listagem 3. O DataContract
15 [DataContract]
16 public class invest
17 {
18 [DataMember]
19 public double Target { get; set; }
20 [DataMember]
21 public double MonthlyQuota { get; set; }
22 [DataMember]
23 public DateTime StartDate { get; set; }
24 [DataMember]
25 public DateTime EndDate { get; set; }
26 [DataMember]
27 public int TotalMonths { get; set; }
28 [DataMember]
29 public double Final { get; set; }
30
31 public invest Estimate(double target, double monthlyQuota,
DateTime startDate)
32 {
33 Target = target;
34 MonthlyQuota = monthlyQuota;
35 StartDate = startDate;
36 TotalMonths = 0;
37 Final = 0;
38 double tax =
double.Parse(ConfigurationManager.AppSettings["tax"].ToString());
39
40 while (Final < Target)
41 {
42 Final = (Final + MonthlyQuota) * (100 + tax) / 100;
43 TotalMonths++;
44 }
45
46 EndDate = StartDate.AddMonths(TotalMonths);
47 return this;
48 }
49 }
Estas propriedades são usadas no método Estimate que está descrito a partir da
Linha 31. As propriedades precisam obrigatoriamente ter o get; set; informado
ou o serviço não funciona corretamente.
Para fazer o cálculo a classe vai obter a taxa a ser usada lendo este dado do
arquivo de configuração (linha 38). Esta sintaxe básica considera que a chave
de configuração está corretamente configurada no arquivo. Portanto, em um
serviço do mundo real é conveniente certificar-se de que um valor é retornado
ou uma exception será gerada e nada será retornado.
O cálculo é feito dentro de um loop while (linha 40). Note que os meses
(TotalMonths) vão sendo incrementados a cada cálculo feito. Por fim, é obtida
a data final para o investimento adicionando meses para a data inicial (linha
46). E o método retorna a instância atual da classe.
Nota do DevMan
1 using System;
2
3 namespace InvestServiceDemo
4 {
5 public class InvestService : IInvestService
6 {
7
8 public invest Target(double target, double monthlyQuota,
DateTime startDate)
9 {
10 return new invest().Estimate(target, monthlyQuota,
startDate);
11 }
12 }
13 }
14
Nesta implementação estou usando uma prática que é bem comum, em classes
que retornam poucos dados e tem poucas operações: usar o seu construtor para
fazer uma chamada ao método Estimate. A lógica disso já foi passada em
outros artigos, mas, relembrando, o construtor retorna uma instância da classe,
logo faz sentido fazer uma chamada de um método deste.
Como não será necessário usar a instância criada da classe, basta usar este tipo
de codificação para resolver o problema todo com apenas uma linha.
O arquivo SVC
O arquivo que publica o serviço deve ser adicionado ao projeto como um
novo item e com o nome InvestService.svc. O seu conteúdo deve ser a linha:
Configurando
O ponto fundamental do WCF é o arquivo de configurações. Em tempo de
projeto este arquivo é nomeado como “app.config” e pode ser encontrado na
janela Solution Explorer conforme demonstrado na Figura 13.
Figura 13. Arquivo de configurações
1 <?xml version="1.0"?>
2 <configuration>
3 <system.web>
4 <compilation debug="true"/>
5 </system.web>
6 <appSettings>
7 <add key="tax" value="0.5" />
8 </appSettings>
9 <system.serviceModel>
10 <services>
11 <service name="InvestServiceDemo.InvestService"
behaviorConfiguration="InvestServiceDemo.ServBehavior">
12 <host>
13 <!--<baseAddresses>
14 <add
baseAddress="http://localhost:8732/invest/WCFDemo2/"/>
15 </baseAddresses>-->
16 </host>
17 <endpoint address="" binding="wsHttpBinding"
contract="InvestServiceDemo.IInvestService">
18 <identity>
19 <dns value="localhost"/>
20 </identity>
21 </endpoint>
22 <endpoint address="mex" binding="mexHttpBinding"
contract="IMetadataExchange"/>
23 </service>
24 </services>
25 <behaviors>
26 <serviceBehaviors>
27 <behavior name="InvestServiceDemo.ServBehavior">
28 <serviceMetadata httpGetEnabled="True"/>
29 <serviceDebug
includeExceptionDetailInFaults="False"/>
30 </behavior>
31 </serviceBehaviors>
32 </behaviors>
33 </system.serviceModel>
34 <startup>
35 <supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.0"/>
36 </startup>
37 </configuration>
38
Esta versão do arquivo, apesar de um pouco longa está bem resumida, se for
levar em consideração todas as possibilidades da sua estrutura.
O atributo binding (ainda na linha 17) refere-se ao protocolo que será usado
para acessar e descobrir o serviço. No caso, wsHttpBinding indica um
tratamento mais aprofundado usando o protocolo HTTP e que deve ser usado
sempre quando se trabalha com o IIS.
As configurações deste arquivo estão muito restritas ao exemplo que foi dado.
Não estão sendo considerados aspectos como disponibilização de outros
protocolos como net.tcp. Nem foi feito um ajuste fino no endereçamento do
servidor. Existem vários ajustes que podem ser feitos.
A estrutura de pastas do servidor, se for seguida uma instalação típica irá ficar
da forma como é apresentada na Figura 14.
Figura 14. Pasta do serviço com os arquivos
Normalmente isto é feito clicando com o botão direito sobre o ícone do site
padrão (Default Web Site) e escolhendo a opção Add Application. Esta irá
abrir uma janela onde devem ser fornecidos os dados do Alias da aplicação,
que é o endpoint do WCF. Outro campo que deve ser preenchido é a
localização da pasta dentro do servidor (no campo Physical Path).
http://localhost/investDemo/service.svc
Figura 16. O serviço publicado
E é este também o endereço que deve ser passado para a aplicação cliente
quando for fazer referência ao serviço e se criar a classe proxy.
Consumindo o serviço
Normalmente, se o serviço está acessível pelo browser é um forte indicador de
que tudo está funcionando bem. Entretanto, diferentemente dos Web Services,
não é possível testar o seu funcionamento através do browser.
A melhor alternativa então é criar um projeto do tipo Console para testar seu
funcionamento e foi isto que foi feito.
Configurando o acesso
Ao fazer a referência, um arquivo de configurações chamado app.config é
gerado e populado dentro da solução já contendo todos os elementos
essenciais para que a aplicação possa se comunicar corretamente com o
serviço. O conteúdo completo deste pode ser conferida na Listagem 6.
A tag binding cuida da maneira como é feita a conexão. Estou omitindo aqui
maiores explicações sobre a mesma pois, normalmente, esta configuração é
feita automaticamente ao se fazer a referência ao serviço e não ser necessário
executar ajustes. Se estiver procurando maiores dados, é importante ler a
documentação da própria Microsoft sobre estas configurações que é extensa.
Consumindo o serviço
Ao fazer a referência para o WCF é gerada a classe proxy cujo principal
objetivo é permitir ao programador trabalhar com objetos remotos como se
estivessem localizados na máquina de trabalho.
1 using System;
2 using System.Text;
3
4 namespace WCFConsoleDemo
5 {
6 class Program
7 {
8 static void Main(string[] args)
9 {
10 double target = 100000D;
11 double mQuota = 200D;
12 var initialDate = DateTime.Today;
13 var item = new
investService.InvestServiceClient().Target(target, mQuota,
initialDate);
14 var sb = new StringBuilder();
15 sb.AppendFormat("{0, -20}{1, 20:c}\n", "Target",
item.Target);
16 sb.AppendFormat("{0, -20}{1, 20:c}\n", "Monthly Quota",
item.MonthlyQuota);
17 sb.AppendFormat("{0, -20}{1, 20:dd/MM/yyyy}\n", "Start
Date", item.StartDate);
18 sb.AppendFormat("{0, -20}{1, 20:dd/MM/yyyy}\n", "End
Date", item.EndDate);
19 sb.AppendFormat("{0, -20}{1, 20}\n", "Total Months",
item.TotalMonths);
20 sb.AppendFormat("{0, -20}{1, 20}\n", "Years",
item.TotalMonths / 12);
21 sb.AppendFormat("{0, -20}{1, 20:c}\n", "Final",
item.Final);
22 Console.WriteLine(sb.ToString());
23 }
24 }
25 }
26
Para produzir uma saída minimamente formatada eu optei por usar a classe
StringBuilder que inclusive deve ser usada para evitar concatenação de
strings. Na linha 14 é criada a instância desta.
Ao executar o programa, deve ser exibida uma tela parecida com a da Figura
22.
Conclusão
Web Services já eram robustos para desenvolvimento de serviços distribuídos.
Os WCF acrescentaram mais recursos, embora com um pouco mais de
complexidade. Sob o ponto de vista das boas práticas, obriga o desenvolvedor
a pensar melhor no seu código e estruturá-lo de forma mais organizada, já que
deve explicitamente estabelecer o contrato para os serviços e para os dados
que serão expostos.
A configuração está longe de ser uma das tarefas mais simples e requer muita
atenção, o aspecto negativo é que é muito fácil bagunçar tudo e deixar o
serviço inoperante. O aspecto positivo das configurações é que se pode usá-las
para refinar o comportamento do aplicativo e, dependendo da forma que se
escreve o código, pode-se fazer customizações apenas usando os arquivos de
configuração.
O artigo procurou dar uma visão em mais alguns detalhes. Existe um aspecto
do WCF que é muito interessante que é a possibilidade de realizar call-backs
com a aplicação cliente, ou seja, o aplicativo cliente interage com o WCF e
este pode, de formas bem distintas, interagir com a aplicação cliente. Espero
poder tratar deste assunto em um artigo em breve.
Links
Callback
http://blogs.msdn.com/b/rafaelgodinho/archive/2010/12/23/callback-com-wcf.
aspx
WCF - Hosting
http://www.israelaece.com/post/WCF-Hosting.aspx
Configuring WCF and IIS 7 With HTTP Bindings and Multiple Host Hea
ders
http://keithelder.net/2008/04/28/configuring-wcf-and-iis-7-with-http-bindings-
and-multiple/
Deploying WCF Tutorial App on IIS7: "The type could not be found"
http://stackoverflow.com/questions/2739465/deploying-wcf-tutorial-app-on-ii
s7-the-type-could-not-be-found
WCF: The type provided as the Service attribute could not be found
http://thejoyofcode.com/WCF_The_type_provided_as_the_Service_attribute_
could_not_be_found.aspx
Blogs
http://vladimirrech.blogspot.com.br
http://twitter.com/vladimirrech