Você está na página 1de 265

Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Conforme declarado anteriormente, DataTable é um conjunto de dados e consiste em linhas e colunas. A classe DataTable tem as
três coleções importantes a seguir:

•A coleção Columns é exposta como a propriedade Columns e é uma instância da classe DataColumnCollection.
Ele contém zero ou mais objetos DataColumn. Cada objeto DataColumn representa uma coluna ou campo
da DataTable, assim como uma coluna de banco de dados. Essas colunas definem a estrutura de uma
DataTable.

•A coleção Rows é exposta como a propriedade Rows e é uma instância da


classe DataRowCollection. Ele contém zero ou mais objetos DataRow. Cada DataRow é semelhante a um
registro de banco de dados e contém os dados reais da DataTable.

•Assim como uma tabela de banco de dados, uma DataTable também pode ter restrições, como restrições de chave
única e restrições de chave estrangeira. A coleção Constraints é exposta como a propriedade Constraints e é
uma instância da classe ConstraintCollection. Ele pode conter zero ou mais instâncias das classes
UniqueConstraint ou ForeignKeyConstraint.

Além das classes anteriores, há um objeto especial chamado DataView baseado em uma DataTable. Como o nome sugere,
DataView é usado para apresentar diferentes exibições de dados classificando e filtrando dados do DataTable. Observe que DataView não
tem existência independente e é sempre baseado em um DataTable.

Geralmente, você preencherá seu DataSet com os dados de uma fonte de dados, como um banco de dados do SQL Server.
No entanto, o DataSet está totalmente desconectado. A maioria dos objetos do DataSet explicados anteriormente podem ser criados
independentemente sem qualquer interação com qualquer fonte de dados. Isso significa que você pode criar programaticamente seu DataSet
sem se conectar a nenhuma fonte de dados. Por exemplo, você pode querer importar uma lista separada por vírgulas de dados de string
para uma tabela de banco de dados. Nesses casos, você pode criar objetos DataSet e DataTable programaticamente e preencher os dados.
Mais tarde, você pode salvar esses dados em uma tabela de banco de dados.

Compreendendo o DataAdapter O DataAdapter é uma

ponte entre a fonte de dados subjacente e o DataSet. DataAdapter entra em cena quando você deseja realizar qualquer um dos seguintes:

•Preencher o DataSet a partir dos dados do banco de dados

• Atualize a fonte de dados após modificar o DataSet adicionando, excluindo ou atualizando


Objetos DataRow

Antes de ver um exemplo de como preencher um DataSet e atualizar a fonte de dados, você precisa entender a arquitetura
do DataAdapter. Dê uma olhada na Figura 7-4.

184
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Figura 7-4. Arquitetura do DataAdapter

Conforme mostrado na Figura 7-4, o DataAdapter usa quatro objetos Command para executar as consultas SELECT,
INSERT, UPDATE e DELETE. Cada comando é representado pelas propriedades SelectCommand, InsertCommand,
UpdateCommand e DeleteCommand do DataAdapter, respectivamente. Observe que esses objetos Command são os
mesmos que você viu no acesso a dados conectados. No entanto, cada um recebe uma tarefa específica de selecionar,
inserir, atualizar e excluir registros da fonte de dados. Assim como ocorre com os objetos Command padrão, a propriedade
CommandText desses objetos Command pode ser qualquer consulta SQL ou procedimento armazenado válido.
DataAdapter fornece o método Fill() que usa o objeto Command especificado pelo SelectCommand
propriedade e preenche o DataSet. Se você alterar o DataSet preenchido pelo método anterior e quiser propagar as
alterações de volta para a fonte de dados subjacente, será necessário definir outras propriedades (InsertCommand,
UpdateCommand e assim por diante) para instâncias de Command válidas. DataAdapter fornece outro método chamado
Update() que usa os objetos Command especificados pelas propriedades InsertCommand, UpdateCommand e DeleteCommand
e leva as alterações de um DataSet de volta para a fonte de dados subjacente.

185
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Trabalhando com DataSet e DataAdapter


Para entender como DataSet e DataAdapter podem ser usados para manipular dados, você criará um aplicativo como o mostrado
na Figura 7-5.

Figura 7-5. Aplicativo para ilustrar a funcionalidade DataSet

O aplicativo é uma tela típica de entrada de dados. A caixa de combinação mostra uma lista de todos os IDs de
funcionários. Depois de selecionar um ID de funcionário, os detalhes desse funcionário (nome, sobrenome, telefone residencial
e notas) são exibidos nas caixas de texto. Os botões Inserir, Atualizar e Excluir executam as respectivas operações. Todas as
operações - INSERT, UPDATE e DELETE - são executadas no DataSet e não na tabela real do banco de dados. Depois que
todas as operações forem concluídas, você pode clicar no botão Salvar para fazer todas as alterações no banco de dados real.
O aplicativo usa a tabela Employees do banco de dados Northwind.
Agora vamos dissecar o aplicativo passo a passo e ver como o DataSet e o DataAdapter foram colocados
usar.

Preenchendo um DataSet

Se você vir o código-fonte do aplicativo anterior, encontrará algumas variáveis declaradas no nível do formulário. A declaração
é mostrada na Listagem 7-3.

Listagem 7-3. Variáveis de nível de formulário

string strConn = @"fonte de dados=.\sqlexpress;catálogo inicial=northwind; ÿ segurança


integrada=verdadeiro"; DataSet ds = new DataSet(); SqlDataAdapter da = new SqlDataAdapter();
SqlConnection cnn;

A variável de string strConn armazena a string de conexão do banco de dados, que usa uma instalação local do SQL Server
Express conforme indicado pelo atributo da fonte de dados. Em seguida, as variáveis do tipo DataSet, SqlDataAdapter e
SqlConnection são declaradas. Você deve garantir que importou System.Data e System.
Data.SqlClient antes de declarar essas variáveis.
O manipulador de eventos Form_Load do formulário contém o código mostrado na Listagem 7-4.

186
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Listagem 7-4. Preenchendo um DataSet

private void Form1_Load(remetente do objeto, EventArgs e) { cnn = new


SqlConnection(strConn); SqlCommand cmdFuncionários = new SqlCommand();
cmdEmployees.CommandText = "SELECIONE * DE funcionários";
cmdEmployees.Connection = cnn; da.SelectCommand = cmdEmpregados;
da.Fill(ds, "Empregados"); PreencheFuncionários(); }

O código cria um objeto SqlCommand e define sua propriedade CommandText para buscar todos os registros de
a tabela Empregados. A propriedade Connection é definida como o objeto SqlConnection criado anteriormente. O objeto SqlCommand
recém-criado é atribuído à propriedade SelectCommand da instância SqlDataAdapter.
A propriedade SelectCommand determina os registros a serem preenchidos no DataSet posteriormente.
Em seguida, o método Fill() do SqlDataAdapter é chamado. Leva dois parâmetros: o DataSet a ser preenchido e o nome do
DataTable resultante. Observe que o código não abre a conexão nem a fecha. Isso ocorre porque a classe SqlDataAdapter faz isso
internamente para nós. Por fim, um método auxiliar, FillEmployees(), é chamado e preenche a caixa de combinação com a lista de IDs de
funcionários. O método FillEmployees() será discutido posteriormente.

ÿ Observação A classe SqlDataAdapter fecha a conexão automaticamente para nós apenas se for
aberta pelo próprio SqlDataAdapter . Se a conexão for aberta antes de chamar o método Fill() , o SqlDataAdapter não
a fechará automaticamente.

Acessando dados do DataSet


Quando você seleciona um ID de funcionário na caixa de combinação, os detalhes do funcionário devem ser exibidos nas outras caixas de texto. O
código relevante é escrito no evento SelectedIndexChanged da caixa de combinação e é mostrado na Listagem 7-5.

Listagem 7-5. Acessando dados de um DataSet

private void comboBox1_SelectedIndexChanged(remetente do objeto, EventArgs e) { string id =


comboBox1.SelectedItem.ToString(); DataRow[] linhas = ds.Tables["Empregados"].Select($"EmployeeID={id}");
textBox1.Text = rows[0]["firstname"].ToString(); textBox2.Text = rows[0]["lastname"].ToString(); textBox3.Text =
linhas[0]["homephone"].ToString(); textBox4.Text = linhas[0]["notas"].ToString(); }

O código primeiro armazena o ID do funcionário selecionado em uma variável de string. Para localizar o registro de funcionário
correspondente no DataSet, usamos o método Select() de DataTable, que aceita os critérios de seleção e retorna uma matriz de objetos DataRow
correspondentes a esses critérios. Em nosso exemplo, precisamos selecionar o funcionário cujo valor da coluna EmployeeID corresponda ao
selecionado na caixa de combinação. EmployeeID é a coluna de chave primária da tabela Employees e, portanto, sabemos que ela retornará apenas
um DataRow. O DataRow pode 187
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

ser acessado usando a notação típica de array. Observe como os nomes das colunas são usados para acessar os valores das colunas
individuais. Em vez de nomes de coluna, você poderia ter usado índices de coluna. Os vários valores de coluna são exibidos nas
respectivas caixas de texto.

Adicionando Novas Linhas

Depois de inserir os detalhes de um novo funcionário a ser adicionado e clicar no botão Inserir, uma nova linha é adicionada à DataTable
subjacente. O código que torna isso possível é mostrado na Listagem 7-6.

Listagem 7-6. Adicionando um novo DataRow

private void button2_Click(object sender, EventArgs e) { DataRow row =


ds.Tables["Employees"].NewRow(); linha["employeeid"] = comboBox1.Text;
linha["nome"] = textBox1.Text; linha["sobrenome"] = textBox2.Text;
linha["telefone residencial"] = textBox3.Text; linha["notas"] = textBox4.Text;
ds.Tables["Empregados"].Rows.Add(linha); PreencheFuncionários(); }

O código cria um novo DataRow chamando o método NewRow() na DataTable Employees. O


O método NewRow() cria uma nova linha autônoma na memória, correspondendo ao esquema da DataTable subjacente. Em
seguida, vários valores de coluna do DataRow são atribuídos. A linha recém-criada ainda não faz parte da DataTable, portanto, para
adicioná-la à DataTable, o método Add() da coleção Rows é chamado. Por fim, a caixa de combinação é preenchida novamente para
exibir o ID do funcionário recém-adicionado.

Atualizando uma linha existente

Para atualizar uma linha existente, você deve localizá-la primeiro e depois atualizar os valores da coluna. Para encontrar uma linha
específica, você pode usar o mesmo método Select() que usamos anteriormente. Isso é mostrado na Listagem 7-7.

Listagem 7-7. Atualizando um DataRow

private void button1_Click(remetente do objeto, EventArgs e) {

if (comboBox1.SelectedItem == nulo) {

MessageBox.Show("Por favor, selecione o ID do funcionário!");


retornar;

} string id = comboBox1.SelectedItem.ToString(); DataRow[] linhas


= ds.Tables["Empregados"].Select($"EmployeeID={id}"); linhas[0].BeginEdit(); linhas[0]["nome"] =
textBox1.Text; linhas[0]["sobrenome"] = textBox2.Text; linhas[0]["homephone"] = textBox3.Text;
linhas[0]["notas"] = textBox4.Text; linhas[0].EndEdit(); }

188
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

O código seleciona o registro do funcionário que deve ser atualizado usando o método Select() da DataTable. O
método BeginEdit() da classe DataRow usa a linha no modo de edição. Os valores da coluna são então atribuídos. Finalmente,
o método EndEdit() da classe DataRow é chamado. Isso salva as alterações no DataTable subjacente.

Excluindo uma linha Para

excluir uma linha, primeiro você deve localizá-la e depois chamar o método Delete() nela. Isso é ilustrado na Listagem
7-8.

Listagem 7-8. Excluindo um DataRow

private void button3_Click(object sender, EventArgs e) { if


(comboBox1.SelectedItem == null) { MessageBox.Show("Por favor, selecione
o ID do funcionário!"); retornar; } string id = comboBox1.SelectedItem.ToString();
DataRow[] linhas = ds.Tables["Empregados"].Select($"EmployeeID={id}");
linhas[0].Delete(); PreencheFuncionários(); }

O código recupera a linha a ser excluída usando o método Select() da classe DataTable.
O método Delete() da classe DataRow marca a linha subjacente para exclusão. Por fim, a caixa de combinação é preenchida
novamente para que o ID do funcionário excluído não apareça.

Usando Estados DataRow Nas

seções anteriores, você inseriu, atualizou e excluiu objetos DataRow de uma DataTable. Sempre que você executa qualquer uma
dessas operações (inserir, atualizar ou excluir) em um DataRow, sua propriedade RowState é afetada automaticamente. A
propriedade RowState é uma enumeração do tipo DataRowState e indica o estado do DataRow. A Tabela 7-1 mostra vários
valores possíveis da enumeração DataRowState.

Tabela 7-1. Enumeração DataRowState

Configuração de RowState Descrição

Inalterado A linha permanece inalterada desde que foi colocada no DataSet.


Adicionado A linha foi adicionada recentemente ao DataTable.
modificado A linha é alterada.
Excluído A linha é excluída do DataTable.

independente A linha foi criada, mas ainda não foi anexada à DataTable.

189
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

A propriedade RowState é usada pela função auxiliar FillEmployees(), conforme mostrado na Listagem 7-9.

Listagem 7-9. Usando a propriedade RowState

private void FillEmployees()


{ comboBox1.Items.Clear(); foreach
(linha DataRow em
ds.Tables["Funcionários"].Rows) { if (row.RowState != DataRowState.Deleted)
{ comboBox1.Items.Add(row["EmployeeID"].ToString()); } } }

O método FillEmployees() simplesmente itera através de cada DataRow da Employees DataTable e adiciona o EmployeeID
à caixa de combinação. Observe o código marcado em negrito. Antes de adicionar qualquer valor na caixa de combinação, o código
verifica se o RowState da linha foi excluído. Somente as linhas cujo RowState não é Deleted são adicionadas à caixa de combinação.

Salvando as alterações no banco de dados


Até agora, todas as alterações que fizemos são salvas apenas no DataSet; eles ainda precisam ser confirmados no banco de dados.
Você pode testar isso fazendo algumas alterações nos registros e fechando o aplicativo sem clicar no botão Salvar. Você observará
que as alterações são perdidas. O manipulador de eventos Click do botão Salvar contém código que propaga as alterações do DataSet
de volta ao banco de dados. A Listagem 7-10 mostra esse código.

Listagem 7-10. Salvando as alterações do conjunto de dados no banco de dados

private void button4_Click(remetente do objeto, EventArgs e) { SqlCommand


cmdInsert = new SqlCommand(); SqlCommand cmdUpdate = new
SqlCommand(); SqlCommand cmdDelete = new SqlCommand();
cmdInsert.Connection = cnn; cmdUpdate.Connection = cnn;
cmdDelete.Connection = cnn; cmdInsert.CommandText = "INSERT INTO
funcionários(nome, sobrenome, telefone residencial, notas)

VALUES(@fname,@lname,@phone,@notes)";
cmdUpdate.CommandText =
"UPDATE empregados SET firstname=@fname,lastname=@lname,homephone=@phone
WHERE id do funcionário=@empid";
cmdDelete.CommandText = "EXCLUIR FROM funcionários ONDE funcionárioid=@empid";

SqlParameter[] pInsert = new SqlParameter[4]; pInsert[0] = new


SqlParameter("@fname", SqlDbType.VarChar); pInsert[0].SourceColumn = "nome";
pInsert[1] = new SqlParameter("@lname", SqlDbType.VarChar);

190
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

pInsert[1].SourceColumn = "sobrenome";
pInsert[2] = new SqlParameter("@phone", SqlDbType.VarChar);
pInsert[2].SourceColumn = "telefone residencial"; pInsert[3] = new
SqlParameter("@notas", SqlDbType.VarChar); pInsert[3].SourceColumn =
"notas"; foreach (SqlParameter p em pInsert) {

cmdInsert.Parameters.Add(p); }

SqlParameter[] pUpdate = new SqlParameter[5]; pUpdate[0]


= new SqlParameter("@fname", SqlDbType.VarChar); pUpdate[0].SourceColumn
= "nome"; pUpdate[1] = new SqlParameter("@lname", SqlDbType.VarChar);
pUpdate[1].SourceColumn = "sobrenome"; pUpdate[2] = new
SqlParameter("@phone", SqlDbType.VarChar); pUpdate[2].SourceColumn =
"telefone residencial"; pUpdate[3] = new SqlParameter("@notes",
SqlDbType.VarChar); pUpdate[3].SourceColumn = "notas"; pUpdate[4] = new
SqlParameter("@empid", SqlDbType.VarChar); pUpdate[4].SourceColumn =
"id do funcionário"; foreach (SqlParameter p em pUpdate) {

cmdUpdate.Parameters.Add(p); }

SqlParameter[] pDelete = new SqlParameter[1]; pDelete[0]


= new SqlParameter("@empid", SqlDbType.VarChar); pDelete[0].SourceColumn
= "id do funcionário"; foreach (SqlParameter p em pDelete) {

cmdDelete.Parameters.Add(p); }

da.InsertCommand = cmdInsert;
da.UpdateCommand = cmdUpdate;
da.DeleteCommand = cmdDelete;
da.Update(ds,"Empregados");
ds.AcceptChanges(); }

O código cria três objetos SqlCommand para operações INSERT, UPDATE e DELETE, respectivamente. A
propriedade Connection desses objetos SqlCommand é definida como o mesmo objeto SqlConnection que declaramos
inicialmente na parte superior. A propriedade CommandText de cada SqlCommand é definida para a instrução SQL correspondente.
Observe o uso do caractere @ para representar parâmetros. Para cada um desses espaços reservados de
parâmetro, um objeto SqlParameter precisa ser criado. Isso é feito declarando três arrays dos objetos SqlParameter:
pInsert, pUpdate e pDelete.
Em seguida, cada elemento da matriz é instanciado como um objeto SqlParameter passando o nome do
parâmetro e o tipo de dados do parâmetro no construtor da classe SqlParameter. A propriedade SourceColumn de
SqlParameter especifica o nome do DataColumn que fornecerá o valor para o parâmetro. Todos os parâmetros das
matrizes correspondentes são adicionados à coleção Parameters do respectivo objeto SqlCommand. Esses três
objetos SqlCommand são atribuídos às propriedades InsertCommand, UpdateCommand e DeleteCommand da instância
SqlDataAdapter que declaramos na parte superior.

191
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

O método Update() da classe SqlDataAdapter é chamado e leva todas as alterações — inserções, atualizações e
exclusões — do DataSet de volta ao banco de dados. O método Update() recebe dois parâmetros: o DataSet a ser atualizado
e o nome do DataTable do DataSet a ser atualizado. Depois que as alterações são salvas no banco de dados subjacente, as
propriedades RowState de todos os objetos DataRow devem se tornar inalteradas. Isso é feito chamando o método
AcceptChanges() da classe DataSet.
É isso! Agora você pode executar o aplicativo e testá-lo quanto à funcionalidade esperada.

Salvando o conteúdo do conjunto de dados como XML Um

dos recursos poderosos da classe DataSet é que você pode serializá-lo no formato XML, o que significa que os dados
relacionais podem ser salvos no formato XML. Esse recurso é útil ao trabalhar no modo offline e ao transportar dados para
sistemas heterogêneos.
O método WriteXml() da classe DataSet grava o conteúdo do DataSet em um fluxo ou físico
arquivo em formato XML. Opcionalmente, você também pode adicionar informações de esquema. Para ilustrar o uso de
WriteXml(), você precisa criar um aplicativo como o mostrado na Figura 7-6.

Figura 7-6. Aplicativo que grava o conteúdo do DataSet como um arquivo XML

O aplicativo consiste em uma caixa de texto para especificar o caminho do arquivo XML de saída. Os dois primeiros
botões de opção especificam se as informações do esquema devem ser incluídas. O último botão de opção especifica se
você deseja gravar o original, bem como os valores atuais no arquivo. Se você selecionar este botão de opção, os DiffGrams
dos valores originais e atuais serão gravados no arquivo.

ÿ Observação DiffGram é um formato XML especial que armazena valores de linha originais e atuais. O SQL Server 2000
introduziu recursos para atualizar o banco de dados via UpdateGrams. DiffGram é um subconjunto de UpdateGram.

Se selecionada, a caixa de seleção abre o arquivo XML salvo em um navegador. O botão Salvar realmente grava o
DataSet no arquivo especificado. O manipulador de eventos Click do botão Salvar contém o código mostrado na Listagem 7-11.

192
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Listagem 7-11. Usando o método WriteXml()

private void button1_Click(remetente do objeto, EventArgs e)


{ DataSet ds = new DataSet(); SqlDataAdapter da = new
SqlDataAdapter("SELECT
employeeid,firstname,lastname,homephone,notes FROM
Employees", @"data source=.\sqlexpress;initial catalog=northwind;

Integrated security=true");
da.Fill(ds, "employees"); if (radioButton1.Checked)
{ ds.WriteXml(textBox1.Text, XmlWriteMode.IgnoreSchema); }
if (radioButton2.Checked) { ds.WriteXml( textBox1.Text,
XmlWriteMode.WriteSchema); } if (radioButton3.Checked) { foreach
(linha DataRow em ds.Tables[0].Rows) { row.SetModified(); }
ds.WriteXml(textBox1.Text, XmlWriteMode.DiffGram ); } if
(checkBox1.Checked) { Process.Start(textBox1.Text); } }

O código cria um novo DataSet e um SqlDataAdapter. Uma das sobrecargas dos construtores SqlDataAdapter
aceita a consulta SELECT e a string de conexão do banco de dados, e é essa sobrecarga que usamos.
O DataSet é então preenchido usando o método Fill() do DataAdapter. O nome da DataTable é especificado
como Employees.
Em seguida, uma série de condições if verifica o status dos botões de opção. Em cada uma das condições
if, o método WriteXml() da classe DataSet é chamado, que grava o conteúdo do DataSet no fluxo ou arquivo de
disco especificado. Observe que, embora cada uma das condições if chame WriteXml(), o segundo parâmetro —
XmlWriteMode — é diferente em cada caso.
A enumeração XmlWriteMode controla duas coisas. Primeiro, ele especifica se as informações do esquema
devem ser gravadas junto com o conteúdo XML. Em segundo lugar, ele decide se os dados XML de saída conterão
apenas os valores atuais ou ambos os valores originais e atuais. Como você viu no exemplo anterior, o último
formato é chamado DiffGram. Os três valores possíveis da enumeração XmlWriteMode são mostrados na Tabela 7-2.

193
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Tabela 7-2. Valores XmlWriteMode

Valor Descrição

Ignorar esquema Grava o conteúdo do DataSet como dados XML. Nenhuma informação do XSD Schema é
escrito.

WriteSchema Grava o conteúdo do DataSet como dados XML. Além disso, grava as informações do
XSD Schema junto com os dados.
DiffGram Grava o conteúdo do DataSet como marcação XML DiffGram. O DiffGram armazena os valores de
coluna atuais e originais.

Observe a condição if para radioButton3. Como não fizemos nenhuma alteração no DataSet como tal, o código marca
deliberadamente cada linha como modificada. Isso é feito usando o método SetModified() da classe DataRow. Dessa forma,
poderemos ver como o formato DiffGram armazena valores antigos e novos.
Finalmente, o arquivo XML salvo é aberto em um navegador usando o método Start() da classe Process.
A Figura 7-7 mostra um exemplo de execução do aplicativo sem salvar nenhuma informação de esquema. Da mesma
forma, as Figuras 7-8 e 7-9 mostram o arquivo XML de saída com informações de esquema e DiffGram, respectivamente.

Figura 7-7. Escrevendo DataSet como XML sem informações de esquema

194
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Figura 7-8. Escrevendo DataSet como XML com informações de esquema

Figura 7-9. Escrevendo DataSet como XML no formato DiffGram

195
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Observe como as informações do esquema são emitidas na Figura 7-8. Além disso, examine cuidadosamente a
Figura 7-9 . Essa saída XML está no formato DiffGram. Veja como a seção <diffgr:before> armazena os valores originais
dos DataRows, enquanto os valores atuais são exibidos na parte superior.

Salvando apenas o esquema O método

WriteXml() grava dados e, opcionalmente, informações do esquema XSD. E se você precisar extrair apenas as informações
do esquema e não os dados em si? O método WriteXmlSchema() faz esse trabalho escrevendo apenas o esquema do
DataSet e não os dados. Para ilustrar o uso de WriteXmlSchema(), você pode modificar o aplicativo anterior para incluir um
botão de opção adicional. A nova interface do aplicativo é mostrada na Figura 7-10.

Figura 7-10. Aplicativo para ilustrar o método WriteXmlSchema()

Se você selecionar o botão de opção recém-adicionado intitulado Salvar somente esquema, apenas o esquema do
DataSet será salvo. A Listagem 7-12 mostra a versão modificada do código.

Listagem 7-12. Usando o método WriteXmlSchema()

private void button1_Click(remetente do objeto, EventArgs e) { DataSet


ds = new DataSet(); SqlDataAdapter da = new SqlDataAdapter("SELECT
employeeid,firstname,lastname,homephone,notes FROM Employees",
@"fonte de dados=.\sqlexpress;catálogo inicial=northwind; segurança
integrada=true");

da.Fill(ds, "funcionários"); if
(radioButton1.Checked)
{ ds.WriteXml(textBox1.Text,
XmlWriteMode.IgnoreSchema); } if (radioButton2.Checked)
{ ds.WriteXml(textBox1.Text, XmlWriteMode.WriteSchema); }

196
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

if (radioButton3.Checked) {

foreach (linha DataRow em ds.Tables[0].Rows) {

linha.SetModified(); }
ds.WriteXml(textBox1.Text,
XmlWriteMode.DiffGram); } if (radioButton4.Checked)
{ ds.WriteXmlSchema(textBox1.Text); } if (checkBox1.Checked) {

Process.Start(textBox1.Text); }

Observe o código marcado em negrito. O código chama o método WriteXmlSchema() passando o arquivo no qual as
informações do esquema serão armazenadas. Você observará que o esquema obtido por este método é o mesmo obtido
pelo método WriteXml() com XmlWriteMode.WriteSchema. No entanto, nenhum dado é gravado no arquivo.

Extraindo o conteúdo do conjunto de dados como uma string XML

Os métodos WriteXml() e WriteXmlSchema() gravam dados XML e esquema em um fluxo ou arquivo,


respectivamente. Às vezes, você pode querer obter os dados XML e o esquema como uma string em vez de gravar em um
arquivo. Isso é feito com dois métodos:

•O método GetXml() retorna apenas o conteúdo do DataSet no formato XML como um


corda. Nenhuma informação de esquema é retornada.

• Da mesma forma, o método GetXmlSchema() retorna o esquema XML do DataSet como uma
string. Como esses métodos retornam cadeias de caracteres, eles incorrem em mais sobrecarga
do que WriteXml() e WriteXmlSchema().

Lendo dados XML no DataSet Nas seções anteriores,

você aprendeu a serializar o conteúdo do DataSet como dados XML. Pode haver casos em que você gostaria de fazer o
oposto, ou seja, pode precisar ler dados XML em um DataSet e processá-los posteriormente. O método ReadXml() da classe
DataSet é a contraparte do método WriteXml() que já discutimos e permite que você leia dados XML em um DataSet.

ÿ Nota Nos exemplos a seguir, você frequentemente precisará de arquivos XML contendo um esquema e dados.
É recomendável executar o exemplo anterior (Figura 7-10) e salvar os arquivos XML resultantes em seu disco para
uso posterior.

Para ilustrar o uso de ReadXml(), você precisa desenvolver um aplicativo como o mostrado na Figura 7-11.

197
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Figura 7-11. Aplicativo que lê dados XML em um DataSet

O aplicativo consiste em uma caixa de texto para aceitar o caminho do arquivo XML de origem. Há uma série
de botões de opção que determinam como o documento XML será lido pelo DataSet. O botão Ler aciona a
operação de leitura. A Listagem 7-13 mostra o código completo que lê os dados XML em um DataSet.

Listagem 7-13. Usando o método ReadXml()

private void button1_Click(remetente do objeto, EventArgs e) {

DataSet ds = new DataSet();


Modo XmlReadMode=XmlReadMode.Auto;
if (radioButton1.Checked) {

modo = XmlReadMode.Auto;

} if (radioButton2.Checked) {

modo = XmlReadMode.DiffGram;

} if (radioButton3.Checked) {

modo = XmlReadMode.Fragment; }
if (radioButton4.Checked) {

modo = XmlReadMode.IgnoreSchema; }
if (radioButton5.Checked) {

modo = XmlReadMode.InferSchema;

} if (radioButton6.Checked) {

modo = XmlReadMode.ReadSchema;
}

198
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

ds.ReadXml(textBox1.Text, modo);
MessageBox.Show("Arquivo XML lido com sucesso!"); }

O código cria um novo objeto DataSet. Em seguida, ele declara uma variável do tipo de enumeração XmlReadMode.
Essa enumeração desempenha um papel importante na decisão de como os dados XML serão carregados no DataSet.
Você pode ver todos os valores possíveis da enumeração XmlReadMode na Tabela 7-3. Em seguida, uma série de
condições if verifica o status dos vários botões de opção e define o valor da variável XmlReadMode.
Finalmente, o método ReadXml() da classe DataSet é chamado.

Tabela 7-3. Valores XmlReadMode

Valor Descrição
Auto Usa o modo de leitura mais apropriado dos valores restantes (o padrão)
DiffGram Carrega um DiffGram e aplica as alterações

Fragmento Carrega fragmentos XML como os criados ao usar a cláusula FOR XML

Ignorar esquema Ignora o esquema embutido presente no documento XML de origem


InferSchema Infere o esquema dos dados presentes e carrega os dados no DataSet
ReadSchema Lê o esquema inline presente no documento XML

O método ReadXml() tem várias sobrecargas. A que usamos aceita o nome do arquivo XML a ser lido e o valor
XmlReadMode. Após a conclusão de ReadXml(), o DataSet preencheu os objetos DataTable, dependendo do documento XML de
origem. Por exemplo, se você usar o arquivo EmployeesTable.xml que criamos anteriormente, seu DataSet conterá uma DataTable
chamada Employees.
As opções de XmlReadMode precisam de mais explicações porque há várias possibilidades durante o
operação de leitura. Essas opções são discutidas a seguir.

Usando a operação de leitura automática A opção Auto da

enumeração XmlReadMode usa o mecanismo mais apropriado ao carregar os dados XML. Se os dados forem um DiffGram,
ele definirá XmlReadMode como DiffGram. Se o DataSet já tiver um esquema ou o documento XML contiver um esquema
embutido, ele definirá XmlReadMode como ReadSchema. Por fim, se o DataSet ainda não tiver um esquema e o documento
XML não contiver um esquema embutido, ele definirá XmlReadMode como InferSchema.

Lendo DiffGrams A opção

DiffGram da enumeração XmlReadMode é usada exclusivamente com DiffGrams. Geralmente, esses DiffGrams serão gerados
usando o método WriteXml() do DataSet. O esquema do DataSet e do DiffGram deve corresponder para que os dados sejam
lidos com êxito. Como o DiffGram armazena os valores originais e atuais de DataRows, as alterações são aplicadas depois que
o DiffGram é carregado no DataSet.

Lendo Fragmentos XML


Nas seções anteriores deste capítulo, você aprendeu que o SQL Server fornece uma extensão para a instrução SELECT
normal na forma da cláusula FOR XML. Você também viu como a cláusula FOR XML retorna dados XML
na forma de fragmentos. Se você deseja carregar esses fragmentos XML em um DataSet, deve definir XmlReadMode como
Fragment.

199
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Ignorando informações do esquema

O documento XML que você deseja carregar em um DataSet pode conter informações de esquema incorporadas a ele.
Se você quiser ignorar esse esquema, deverá usar a opção IgnoreSchema da enumeração XmlReadMode.
Se o DataSet já tiver um esquema e os dados XML que estão sendo carregados não corresponderem a esse esquema, os dados serão
descartados.

Inferindo informações de esquema A opção

InferSchema de XmlReadMode ignora as informações de esquema dos dados XML de origem, se presentes, e
carrega os dados em um DataSet. Se o DataSet já tiver seu esquema, ele será estendido para acomodar os
novos dados. No entanto, se houver alguma incompatibilidade entre o esquema existente e o esquema recém-
inferido, uma exceção será gerada.

Lendo informações do esquema A opção

ReadSchema de XmlReadMode lê o esquema embutido do documento XML de origem e carrega o esquema,


bem como os dados no DataSet. Se o DataSet já contiver um esquema, ele será estendido de acordo com o novo
esquema. No entanto, qualquer incompatibilidade entre o esquema existente e o novo esquema faz com que uma
exceção seja lançada.

Gerando menus dinamicamente com base em um arquivo XML O método ReadXml()


executa muitas operações nos bastidores para facilitar nossa tarefa. Para ter uma ideia do que ele faz, você
desenvolverá um aplicativo do Windows que adiciona itens de menu dinamicamente. O aplicativo será semelhante
ao mostrado na Figura 7-12.

Figura 7-12. Formulário mostrando itens de menu carregados dinamicamente

O formulário consiste em um único controle MenuStrip. Os itens de menu são armazenados em um arquivo XML, conforme mostrado na
Listagem 7-14. Salve este arquivo como menus.xml na pasta Bin\Debug do seu aplicativo.

200
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Listagem 7-14. Arquivo XML Representando a Estrutura do Menu

<?xml version="1.0" encoding="utf-8" ?>


<menus>
<top menu text="Arquivo">
<submenu>Novo</submenu>
<submenu>Abrir</submenu>
<submenu>Fechar</submenu> </
topmenu> <topmenu text="Editar">

<submenu>Cortar</submenu>
<submenu>Copiar</submenu>
<submenu>Colar</submenu> </
topmenu> <topmenu text="Ajuda">

<submenu>Ajuda</submenu>
<submenu>Pesquisar</submenu>
<submenu>Sobre</submenu> </
topmenu> </menus>

O elemento raiz do arquivo XML é <menus>. Dentro pode haver zero ou mais itens <topmenu>, que
representam os itens de menu de nível superior. O atributo de texto de <topmenu> indica o texto desse menu. O elemento
<topmenu> pode conter zero ou mais elementos <submenu>, que indicam submenus dos menus de nível superior. O texto dos
submenus é especificado no valor do elemento <submenu>.
Vamos ver como esse arquivo pode ser carregado em um DataSet e como os dados podem ser acessados. A Listagem 7-15
mostra o manipulador de eventos Load do formulário com o código necessário.

Listagem 7-15. Adicionando itens de menu dinamicamente

private void Form1_Load(remetente do objeto, EventArgs e) { DataSet ds


= new DataSet(); ds.ReadXml($"{Application.StartupPath}\\menus.xml");

foreach (DataRow topmenu em ds.Tables[0].Rows)


{ ToolStripMenuItem item = new
ToolStripMenuItem(topmenu["text"].ToString()); menuStrip1.Items.Add(item); DataRow[] submenus=
topmenu.GetChildRows(ds.Relations[0]); foreach (submenu DataRow em submenus)
{ item.DropDownItems.Add(submenu[0].ToString()); }

}
}

201
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

O código cria um novo DataSet e lê o arquivo menus.xml que criamos anteriormente. Ao ler isso
arquivo, o DataSet faz algumas coisas interessantes:

1. Ele observa o aninhamento dos dados XML no arquivo e cria dois objetos DataTable.
A primeira DataTable armazena todos os menus principais e a segunda DataTable
armazena todos os submenus.

2. Ele cria objetos DataRow no menu superior DataTable e adiciona uma DataColumn a eles.
O valor contido nessas colunas é o valor do atributo text do elemento <topmenu>.

3. Ele faz algo semelhante para os submenus DataTable, mas carrega os valores dos elementos
dos itens <submenu> na coluna.

4. Ele define um DataRelation entre as duas tabelas adicionando automaticamente uma coluna
inteira a ambos os objetos DataTable.

O código itera por todas as linhas do primeiro DataTable (o DataTable que armazena os menus superiores) e
adiciona objetos ToolStripMenuItem ao MenuStrip. O método GetChildRows() é chamado em cada DataRow do menu
superior DataTable. Este método aceita um objeto DataRelation e retorna todos os objetos DataRow da tabela filho
correspondente a esse relacionamento. No nosso caso, o submenu DataTable é o filho DataTable. O valor de retorno de
GetChildRows() é uma matriz de objetos DataRow. O segundo loop foreach percorre todos os elementos dessa matriz e
adiciona subitens à coleção DropDownItems da classe ToolStripMenuItem.

Se você executar o aplicativo, deverá ver algo semelhante à Figura 7-12.

Lendo apenas as informações do esquema O método ReadXml()

permite que você leia dados e, opcionalmente, informações do esquema. No entanto, às vezes pode ser necessário
extrair apenas as informações do esquema do arquivo XML e não os dados. A classe DataSet fornece dois métodos
que permitem extrair informações de esquema do XML de origem. Eles são ReadXmlSchema() e InferXmlSchema().

ReadXmlSchema() aceita o XML com um esquema embutido e lê apenas a parte do esquema dele. O esquema
é então carregado no DataSet. E se o seu documento XML não contiver um esquema embutido?
É aí que o método InferXmlSchema() entra em cena. O método InferXmlSchema() observa a marcação XML
fornecida e, em seguida, cria um esquema correspondente automaticamente. O esquema é então carregado no DataSet.

Para ilustrar esses dois métodos, você precisa desenvolver um aplicativo como o mostrado na Figura 7-13.

Figura 7-13. Aplicativo que lê esquema

202
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

O aplicativo consiste em uma caixa de texto para especificar o arquivo XML de origem. Os dois botões de opção
permitem que você decida se ReadXmlSchema() ou InferXmlSchema() deve ser chamado. O código para o botão Ler
lê o esquema em um DataSet e o exibe em uma caixa de mensagem. O código que lê o esquema é mostrado na
Listagem 7-16.

Listagem 7-16. Usando os métodos ReadXmlSchema() e InferXmlSchema()

private void button1_Click(remetente do objeto, EventArgs e) { DataSet


ds = new DataSet(); if (radioButton1.Checked)
{ ds.ReadXmlSchema(textBox1.Text); } if (radioButton2.Checked)
{ ds.InferXmlSchema(textBox1.Text,null); }

MessageBox.Show(ds.GetXmlSchema()); }

O código cria um novo objeto DataSet. Dependendo do botão de opção selecionado, o código chama
ReadXmlSchema() ou InferXmlSchema(). ReadXmlSchema() aceita o documento XML de origem como um
parâmetro e carrega o esquema embutido do documento no DataSet. Nenhum dado é carregado.
O método InferXmlSchema() aceita o documento XML de origem e um array de namespaces (nulo em nosso
exemplo) e infere o esquema a partir dos dados. Novamente, nenhum dado é carregado. O esquema carregado é
mostrado em uma caixa de mensagem chamando o método GetXmlSchema() do DataSet. A Figura 7-14 mostra o
esquema carregado usando ReadXmlSchema(), enquanto a Figura 7-15 mostra o esquema carregado usando InferXmlSchema().

Figura 7-14. Esquema extraído usando o método ReadXmlSchema()

203
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Figura 7-15. Esquema extraído usando o método InferXmlSchema()

Como você pode ver, o esquema carregado por ambos os métodos é idêntico em nosso exemplo.

Criando um DataSet tipado


Ao discutir DataSet e DataAdapter, desenvolvemos um aplicativo que nos permitiu realizar inserções, atualizações e exclusões em
um DataSet e, em seguida, salvar essas alterações de volta no banco de dados (consulte a Figura 7-6). Nesse aplicativo,
frequentemente usamos coleções como Tabelas e Linhas. Também precisávamos lembrar os nomes das colunas ao acessar seus
valores de um DataRow. Você não acha um pouco tedioso acessar os dados dessa maneira? Para tornar as coisas mais claras, veja
as Listagens 7-17 e 7-18.

Listagem 7-17. Inserindo um DataRow usando um DataSet não digitado

private void button2_Click(remetente do objeto, EventArgs e) {

DataRow linha = ds.Tables["Empregados"].NewRow();


linha["employeeid"] = comboBox1.Text; linha["nome"] =
textBox1.Text; linha["sobrenome"] = textBox2.Text; linha["telefone
residencial"] = textBox3.Text; linha["notas"] = textBox4.Text;
ds.Tables["Empregados"].Rows.Add(linha); PreencheFuncionários(); }

204
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Listagem 7-18. Inserindo um DataRow usando um DataSet digitado

private void button2_Click(remetente do objeto, EventArgs e)


{ EmployeesDataSet.EmployeesRow linha = ds.Employees.NewEmployeesRow();
linha.EmployeeID = int.Parse(comboBox1.Text); linha.FirstName = textBox1.Text; linha.LastName
= textBox2.Text; linha.HomePhone = textBox3.Text; linha.Notas = textBox4.Text;
ds.Employees.AddEmployeesRow(linha); PreencheFuncionários(); }

Ambas as listagens representam o código que insere um novo DataRow em um DataTable. Compare as listas
cuidadosamente. Na Listagem 7-17, acessamos a DataTable Employees e suas colunas especificando-as entre aspas duplas.
Isso significa que você precisa se lembrar desses nomes quando estiver codificando. No entanto, a Listagem 7-18 parece
diferente. Você notará que ele usa a propriedade Employees para criar uma nova linha. Além disso, ele usa nomes de coluna
como FirstName e LastName como se fossem propriedades. Obviamente, a segunda versão é muito mais fácil de codificar e é
muito mais organizada, o que demonstra o que são DataSets digitados.
Um DataSet tipado é uma classe que deriva internamente de DataSet como uma classe base. Ele estende esta classe base
ainda mais e adiciona certas propriedades e métodos que facilitam a vida do desenvolvedor. Ao usar um DataSet tipado, você
pode acessar objetos DataTable e DataColumn usando seus nomes fortemente tipados em vez da sintaxe de coleção. Um
DataSet digitado tem um esquema XSD anexado a ele que define os objetos DataTable e DataColumn do DataSet.

Usando o Visual Studio para criar um DataSet tipado Agora que você sabe o que é um

DataSet tipado, vamos criar um para nossa tabela Employees. Para fazer isso, primeiro você precisa adicionar um DataSet
digitado ao seu projeto. A Figura 7-16 mostra a caixa de diálogo Add New Item do Visual Studio, por meio da qual você pode
adicionar um novo DataSet digitado.

Figura 7-16. Adicionando um novo DataSet digitado ao seu projeto

205
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Depois que estiver no designer do DataSet, você poderá ver a caixa de ferramentas do DataSet, mostrada na Figura 7-17.

Figura 7-17. A caixa de ferramentas DataSet

Como você pode ver, a caixa de ferramentas tem itens como DataTable e Relation que você pode arrastar e soltar
o designer DataSet. Para nosso exemplo, você precisa arrastar e soltar uma DataTable no designer DataSet e definir sua propriedade
Name como Employees. Para adicionar colunas ao DataTable, você pode clicar com o botão direito do mouse e adicionar o número
necessário de colunas (consulte a Figura 7-18).

Figura 7-18. Adicionando colunas a um DataTable

O nome e o tipo de dados de cada coluna podem ser definidos por meio da janela de propriedades. Por exemplo, a Figura
7-19 mostra a janela Propriedades da coluna EmployeeID.

206
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Figura 7-19. Configurando as propriedades da coluna EmployeeID

Depois de projetar a DataTable Employees, ela deve se parecer com a Figura 7-20.

Figura 7-20. A DataTable Employees no designer DataSet

À medida que você projeta o DataSet no designer, o Visual Studio cria uma classe que herda da classe DataSet
e adiciona determinadas propriedades e métodos a ela. Ele também cria certos arquivos de suporte, como você pode ver
no Solution Explorer (consulte a Figura 7-21).

207
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Figura 7-21. EmployeesDataSet e seus arquivos associados

Em seguida, você precisa projetar o formulário principal de seu aplicativo, conforme mostrado na Figura 7-22.

Figura 7-22. Aplicação que consome um DataSet digitado

O aplicativo se comporta exatamente da mesma forma que o mostrado na Figura 7-6 anteriormente, mas desta vez ele usa nosso
DataSet digitado. A Listagem 7-19 mostra as declarações de variáveis no nível do formulário.

Listagem 7-19. Declarando uma variável DataSet tipada

private string strConn = @"fonte de dados=.\sqlexpress; catálogo


inicial=northwind;segurança integrada=true"; EmployeesDataSet ds = new
EmployeesDataSet(); SqlDataAdapter da = new SqlDataAdapter();
SqlConnection cnn;

Observe a linha marcada em negrito. O código declara uma variável do nosso DataSet digitado, que tem o mesmo nome do arquivo
DataSet XSD Schema. Este DataSet digitado é preenchido no evento Load do formulário. O código no evento Load permanece o mesmo
de antes, mas para completar, ele é mostrado na Listagem 7-20.

208
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Listagem 7-20. Preenchendo um DataSet digitado

private void Form1_Load(remetente do objeto, EventArgs e) { cnn


= new SqlConnection(strConn); SqlCommand cmdFuncionários =
new SqlCommand(); cmdEmployees.CommandText =
"SELECIONE * DE funcionários"; cmdEmployees.Connection =
cnn; da.SelectCommand = cmdEmpregados; da.Fill(ds, "Empregados");
PreencheFuncionários(); }

O código usa um SqlDataAdapter e chama seu método Fill() para preencher o DataSet digitado. Uma Coisa
a observar aqui é que o nome do DataTable especificado no método Fill() deve corresponder ao nome do DataTable
que você criou no DataSet digitado. A Listagem 7-21 mostra a versão modificada do código responsável por inserir,
atualizar e deletar objetos DataRow.

Listagem 7-21. Inserindo, atualizando e excluindo dados de um DataSet digitado

private void button2_Click(remetente do objeto, EventArgs e)


{ EmployeesDataSet.EmployeesRow linha =
ds.Employees.NewEmployeesRow(); linha.EmployeeID = int.Parse(comboBox1.Text);
linha.FirstName = textBox1.Text; linha.LastName = textBox2.Text; linha.HomePhone
= textBox3.Text; linha.Notas = textBox4.Text; ds.Employees.AddEmployeesRow(linha);
PreencheFuncionários(); } private void button1_Click(object sender, EventArgs e) { string
id = comboBox1.SelectedItem.ToString(); EmployeesDataSet.EmployeesRow[] linhas
= (EmployeesDataSet.EmployeesRow[])ds.Employees.Select($"EmployeeID={id}");
linhas[0].BeginEdit(); linhas[0].FirstName = textBox1.Text; linhas[0].LastName =
textBox2.Text; linhas[0].HomePhone = textBox3.Text; linhas[0].Notas = textBox4.Text;
linhas[0].EndEdit();

private void button3_Click(remetente do objeto, EventArgs e) { string id


= comboBox1.SelectedItem.ToString(); EmployeesDataSet.EmployeesRow[]
linhas =

(EmployeesDataSet.EmployeesRow[])ds.Employees.Select($"EmployeeID={id}");
linhas[0].Delete();
PreencheFuncionários();
}

209
Machine Translated by Google

Capítulo 7 ÿ XML em ADO.NET

Observe as alterações feitas no código original. No manipulador de eventos Click do botão Inserir, o novo
DataRow é criado chamando NewEmployeesRow(). O DataSet digitado mostra automaticamente os objetos
DataTable disponíveis como propriedades e cada DataTable fornece o método NewEmployeesRow() para criar uma
nova linha. A linha recém-criada é do tipo EmployeesRow, que é uma classe gerada pelo Visual Studio na classe
EmployeesDataSet. EmployeesRow expõe cada coluna da linha como uma propriedade, e essas propriedades podem
receber novos valores. A linha recém-criada é então adicionada à DataTable Employees usando seu método
AddEmployeesRow(). Há modificações semelhantes nos manipuladores de eventos Click dos botões Atualizar e Excluir.

Usando a ferramenta xsd.exe para criar um DataSet digitado Embora o Visual Studio

forneça uma maneira visual de criar DataSets digitados, o .NET Framework também fornece uma ferramenta de linha
de comando chamada xsd.exe que pode gerar DataSets tipados para você. A ferramenta aceita um esquema XML
para um DataSet e gera a classe DataSet digitada. Embora não discutamos a ferramenta xsd.exe em detalhes, aqui está
um exemplo de uso dela:

xsd.exe EmployeesDataSet.xsd /dataset /idioma:CS /namespace:MyTypedDataSets

O comando aceita o nome de um arquivo de esquema XML (EmployeesDataSet.xsd neste caso). A opção /dataset
indica que a ferramenta deve gerar um DataSet digitado com base nesse esquema XML. A opção /idioma especifica o
idioma a ser usado para a classe DataSet digitada. Em nosso exemplo, especificamos a linguagem como C# (CS).
Por fim, a opção /namespace especifica o namespace no qual a classe DataSet digitada será colocada.
A saída do comando anterior será um arquivo de classe chamado EmployeesDataSet.cs. Você pode compilar
este arquivo de classe separadamente em um assembly ou adicioná-lo ao seu projeto existente junto com outras classes.

Resumo
O ADO.NET é uma parte muito importante do .NET Framework geral. Aplicativos orientados a dados modernos tendem
a funcionar com armazenamentos de dados relacionais e hierárquicos. O modelo de objeto ADO.NET, embora
principalmente inclinado para RDBMSs, tem forte integração com XML.
Este capítulo forneceu uma visão completa dos recursos XML do ADO.NET. Você aprendeu a trabalhar com
Dados XML no modo conectado e desconectado. A classe DataSet é a base do modelo desconectado do ADO.NET
e permite que você leia e grave dados XML e trabalhe com esquemas. Além disso, DataSets digitados facilitam seu
desenvolvimento, fornecendo nomes DataTable e DataColumn digitados.

210
Machine Translated by Google

CAPÍTULO 8

Serialização XML

Seus aplicativos .NET consistem em uma ou mais classes. Os objetos dessas classes são usados para armazenar informações de
estado. Enquanto seus objetos estiverem disponíveis na memória de seu aplicativo, essas informações de estado estarão prontamente
disponíveis. Mas e se você quiser manter o estado do objeto durante os desligamentos do aplicativo? A princípio, você pode pensar
em salvar o estado do objeto em um banco de dados relacional. No entanto, os bancos de dados geralmente armazenam informações
em formato relacional, enquanto os objetos costumam ter uma estrutura hierárquica. Além disso, você precisaria criar muitas tabelas
no banco de dados por conta própria. Armazenar dados de objeto em um banco de dados vem com suas próprias despesas gerais.
Não seria bom se todo o estado do objeto pudesse ser armazenado em uma mídia e recuperado posteriormente? Isso é o que a
serialização oferece.
A serialização é um processo pelo qual o estado do objeto é persistido em um meio. O meio pode ser um arquivo de disco físico,
memória ou até mesmo um fluxo de rede. Os objetos serializados podem ser recuperados posteriormente em seu aplicativo por um
processo chamado desserialização. O .NET Framework fornece amplo suporte para serialização e usa serialização em muitos lugares.
Serviços Web, serviços WCF e API Web são alguns locais onde a serialização é muito usada. Neste capítulo, você aprenderá sobre os
seguintes tópicos:

•Entendendo os sabores da serialização

•Usando a classe XmlSerializer para serializar o estado do objeto no formato XML

•Usando a classe DataContractSerializer para serializar o estado do objeto no formato XML

•Usando a classe SoapFormatter para serializar o estado do objeto no formato SOAP

•Personalizando o processo de serialização com a ajuda de certos atributos

Entendendo os sabores da serialização


A serialização pode ser classificada com base no formato de serialização ou na profundidade da serialização. Os três formatos
comumente usados nos quais você pode serializar dados no .NET Framework são os seguintes:

• Binário: Este formato geralmente é melhor em termos de desempenho do que os outros.


No entanto, em termos de extensibilidade e integração entre aplicativos, os outros formatos são
melhores.

• XML: Objetos serializados dessa forma são armazenados como XML simples. Se você estiver falando com
vários sistemas heterogêneos, esse formato será útil. Por exemplo, seus aplicativos .NET podem
serializar objetos como documentos XML, e um aplicativo Java pode ler esses objetos serializados
usando seu analisador XML padrão e trabalhar mais com os dados.

• Simple Object Access Protocol (SOAP): Objetos serializados desta forma armazenam
informações de acordo com os padrões SOAP. SOAP é o pilar central dos serviços da web.
O protocolo SOAP é baseado em XML.

© Bipin Joshi 2017 211


B. Joshi, Beginning XML with C# 7, https://doi.org/10.1007/978-1-4842-3105-0_8
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

A serialização usando o formato binário está além do escopo deste livro. Este capítulo discute a serialização usando
os formatos XML e SOAP.

ÿ Observação Em aplicativos da Web modernos, o JSON está se tornando cada vez mais popular como formato para

transferência de dados. Os aplicativos que usam JSON precisam de um mecanismo para serializar e desserializar objetos no formato
JSON. Embora o WCF e a API da Web possam lidar com dados JSON, não discutiremos isso neste livro.

A outra maneira de classificar a serialização é baseada na profundidade da serialização. Os dois tipos com base na
profundidade da serialização são os seguintes:

• Serialização profunda: Serializa todos os membros públicos, protegidos e privados de


sua classe.

• Serialização rasa: serializa apenas os membros públicos de sua classe.

No .NET Framework, a profundidade da serialização depende da classe do serializador que você usa para
serialização e desserialização de objetos.

Classes envolvidas na serialização XML


Existem três classes principais que são usadas para executar a serialização nos formatos XML e SOAP:

•A classe XmlSerializer serializa objetos no formato XML. Ele reside no Sistema.


Xml.Serialization namespace. O namespace System.Xml.Serialization reside fisicamente
no assembly System.Xml.dll. A serialização feita usando XmlSerializer é superficial por
natureza.

•A classe DataContractSerializer serializa objetos no formato XML. Ele foi adicionado como
parte do Windows Communication Foundation (WCF) e reside no namespace
System.Runtime.Serialization. A serialização feita usando DataContractSerializer é
profunda por natureza.

•A classe SoapFormatter serializa objetos no formato SOAP. Ele reside no Sistema.


Runtime.Serialization.Formatters.Soap namespace. O System.Runtime.
O namespace Serialization.Formatters.Soap reside fisicamente no System.
Montagem Runtime.Serialization.Formatters.Soap.dll. A serialização feita usando SoapFormatter
é profunda por natureza.

Serializando e desserializando objetos usando XmlSerializer


Agora que você tem uma compreensão básica do que é serialização, vamos nos aprofundar na serialização XML.
Você criará um aplicativo que ilustra o processo de serialização e desserialização usando a classe XmlSerializer. A interface
do usuário do aplicativo é mostrada na Figura 8-1.

212
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Figura 8-1. Aplicativo para ilustrar a serialização XML

O aplicativo consiste em uma classe chamada Employee com cinco propriedades públicas: EmployeeID, FirstName,
Sobrenome, Telefone residencial e Notas. Existem cinco caixas de texto que aceitam valores para essas
propriedades. Os dois botões, Serialize e Deserialize, fazem o trabalho de serializar e desserializar o objeto
Employee, respectivamente. A caixa de seleção determina se o documento XML serializado será aberto em um
navegador para exibição.
Antes de poder usar a classe XmlSerializer, você deve criar a classe Employee mostrada na Listagem 8-1.

Listagem 8-1. A Classe Empregado

public class Empregado {

public int EmployeeID { get; definir; } public


string FirstName { get; definir; } public string
Sobrenome { get; definir; } public string
HomePhone { get; definir; } string pública Notas
{ get; definir; }
}

A classe consiste em cinco propriedades públicas para armazenar várias informações sobre um funcionário,
ou seja, EmployeeID, FirstName, LastName, HomePhone e Notes.
O manipulador de eventos Click do botão Serialize contém o código mostrado na Listagem 8-2.

Listagem 8-2. Serializando objetos no formato XML

private void button1_Click(object sender, EventArgs e) { Employee emp


= new Employee(); emp.EmployeeID = int.Parse(textBox1.Text);
emp.FirstName = textBox2.Text; emp.LastName = textBox3.Text;
emp.HomePhone = textBox4.Text; emp.Notes = textBox5.Text;

213
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Fluxo de fluxo de arquivo =


new FileStream($"{Application.StartupPath}\\employee.xml", FileMode.Create);
XmlSerializer serializer = new XmlSerializer(typeof(Employee));
serializer.Serialize(fluxo, emp); stream.Close(); if (checkBox1.Checked)
{ Process.Start($"{Application.StartupPath}\\employee.xml"); }

O código cria uma instância da classe Employee. Em seguida, ele atribui valores de várias caixas de texto às
propriedades correspondentes da classe Employee. Um FileStream é então criado para gravação em um arquivo de
disco físico (Employee.xml). Esse fluxo é usado durante a serialização do objeto. Em seguida, o código cria um objeto da
classe XmlSerializer. Conforme mencionado anteriormente, a classe XmlSerializer permite serializar dados no formato
XML.
Existem várias sobrecargas do construtor XmlSerializer e o código usa aquele que aceita
o tipo de classe cujos objetos devem ser serializados. As informações de tipo sobre a classe Employee são
obtidas usando a palavra-chave typeof. O método Serialize() de XmlSerializer serializa um objeto para um fluxo
especificado, TextWriter ou XmlWriter.
Como nosso exemplo usa um FileStream para serializar o objeto Employee, após a conclusão da serialização,
o fluxo está fechado. Por fim, os dados serializados do arquivo XML são exibidos em um navegador usando o
método Start() da classe Process.
O manipulador de eventos Click do botão Deserialize contém o código mostrado na Listagem 8-3.

Listagem 8-3. Desserializando usando a classe XmlSerializer

private void button2_Click(remetente do objeto, EventArgs e) {

Fluxo de fluxo de arquivo =


new FileStream($"{Application.StartupPath}\\employee.xml", FileMode.Open);
XmlSerializer serializer = new XmlSerializer(typeof(Employee)); Employee
emp = (Employee)serializer.Deserialize(stream); stream.Close(); textBox1.Text
= emp.EmployeeID.ToString(); textBox2.Text = emp.FirstName; textBox3.Text =
emp.LastName; textBox4.Text = emp.HomePhone; textBox5.Text = emp.Notas; }

O código cria um FileStream apontando para o mesmo arquivo que foi criado durante o processo de
serialização. Observe que desta vez o arquivo é aberto no modo Abrir e não no modo Criar. Em seguida, um objeto
de XmlSerializer é criado como antes.
O método Deserialize() da classe XmlSerializer aceita um Stream, um TextReader ou um XmlReader do
qual o objeto deve ser lido para desserialização. Em seguida, ele retorna o objeto desserializado. Os dados desserializados
são sempre retornados como um objeto e precisam ser convertidos para o tipo Funcionário. Em seguida, vários valores
de propriedade do objeto desserializado são atribuídos às respectivas caixas de texto.

214
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Para testar o aplicativo, execute-o, insira alguns valores nas caixas de texto e clique no botão Serializar.
A Figura 8-2 mostra um exemplo de documento XML obtido pela execução do aplicativo anterior.

Figura 8-2. Objeto Employee serializado como um documento XML

Examine cuidadosamente a marcação XML resultante. O nome da classe (Funcionário) tornou-se o nome do
elemento raiz. Os elementos como <EmployeeID>, <FirstName> e <LastName> têm o mesmo nome que as
propriedades correspondentes da classe Employee. Agora feche o aplicativo e execute-o novamente. Desta vez, clique
no botão Deserialize. Você descobrirá que as caixas de texto mostram os valores de propriedade que você especificou
durante a última execução do aplicativo.

Manipulando eventos gerados durante a desserialização


Imagine um caso em que um aplicativo está serializando objetos e o outro está desserializando-os. E se os objetos
serializados contiverem alguns atributos e elementos extras? O aplicativo que está desserializando esses objetos
deve ter alguma forma de sinalizar essa discrepância. Felizmente, a classe XmlSerializer vem com determinados
eventos para lidar com essas situações. Esses eventos são gerados durante o processo de desserialização quando a
estrutura da classe e o XML serializado não correspondem. A Tabela 8-1 lista esses eventos.

215
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Tabela 8-1. Eventos da classe XmlSerializer

Nome do evento Descrição


Atributo desconhecido Este evento é gerado quando os dados que estão sendo desserializados contêm
algum atributo inesperado. O evento recebe um argumento de evento do tipo
XmlAttributeEventArgs que fornece mais informações sobre o evento.
Elemento Desconhecido Este evento é gerado quando os dados que estão sendo desserializados contêm
algum elemento inesperado. O evento recebe um argumento de evento do tipo
XmlElementEventArgs que fornece mais informações sobre o evento.
UnknownNode Este evento é gerado quando os dados que estão sendo desserializados contêm
algum nó inesperado. O evento recebe um argumento de evento do tipo
XmlNodeEventArgs que fornece mais informações sobre o evento.

Objeto não referenciado Esse evento é gerado quando os dados que estão sendo desserializados contêm algum tipo
reconhecido que não é usado ou não é referenciado. O evento recebe um argumento de
evento do tipo UnreferencedObjectEventArgs que fornece mais informações sobre o evento.
Este evento aplica-se apenas ao XML codificado por SOAP.

Para ilustrar o uso desses eventos, você precisa modificar o aplicativo anterior para aquele mostrado na Listagem 8-4.

Listagem 8-4. Eventos da classe XmlSerializer

private void button2_Click(remetente do objeto, EventArgs e) {

Fluxo de fluxo de arquivo =


new FileStream($"{Application.StartupPath}\\employee.xml", FileMode.Open);
XmlSerializer serializer = new XmlSerializer(typeof(Employee));
serializer.UnknownAttribute +=
novo XmlAttributeEventHandler(serializer_UnknownAttribute);
serializer.UnknownElement +=
novo XmlElementEventHandler(serializer_UnknownElement);
serializer.UnknownNode += new XmlNodeEventHandler(serializer_UnknownNode); Employee
emp = (Employee)serializer.Deserialize(stream); stream.Close(); textBox1.Text =
emp.EmployeeID.ToString(); textBox2.Text = emp.FirstName; textBox3.Text = emp.LastName;
textBox4.Text = emp.HomePhone; textBox5.Text = emp.Notas; }

void serializer_UnknownNode(remetente do objeto, XmlNodeEventArgs e)


{ MessageBox.Show($"Nó desconhecido {e.Name} encontrado na linha
{e.LineNumber}"); } void serializer_UnknownElement(remetente do objeto,
XmlElementEventArgs e) { MessageBox.Show($"Elemento desconhecido {e.Element.Name}
encontrado na linha {e.LineNumber}"); }

216
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

void serializer_UnknownAttribute(remetente do objeto, XmlAttributeEventArgs e)


{ MessageBox.Show($"Atributo desconhecido {e.Attr.Name} encontrado na linha
{e.LineNumber}"); }

Observe o código marcado em negrito. Depois de declarar a instância da classe XmlSerializer, ele conecta
três manipuladores de eventos — UnknownAttribute, UnknownElement e UnknownNode — que simplesmente exibem
uma caixa de mensagem mostrando o nome do atributo, elemento ou nó e o número da linha na qual o atributo,
elemento , ou o nó é encontrado. Observe como o parâmetro do argumento do evento é usado para extrair informações
sobre o conteúdo inesperado.
Para testar esses eventos, modifique o arquivo XML serializado manualmente, conforme mostrado na Listagem 8-5.

Listagem 8-5. Modificando o XML serializado manualmente

<?xml versão="1.0"?>
<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://
www.w3.org/2001/XMLSchema" EmpCode="E001"> <EmployeeID >1</EmployeeID>
<FirstName>Nancy</FirstName> <LastName>Davolio</LastName>

<HomePhone>(206) 555-9857</HomePhone>
<Notas>A educação inclui bacharelado em psicologia pela Colorado State University em 1970.
Ela também completou "The Art of the Cold Call". Nancy é membro do Toastmasters International.</Notes>

<OfficePhone>(206) 555-1234</OfficePhone>
</Empregado>

Observe a marcação em negrito. Adicionamos um atributo EmpCode e um elemento <OfficePhone> manualmente ao


arquivo XML. Salve o arquivo e execute o aplicativo. Desta vez, quando você clicar no botão Deserialize, verá caixas de
mensagens informando sobre as discrepâncias. A Figura 8-3 mostra uma dessas caixas de mensagem.

Figura 8-3. Conteúdo inesperado encontrado durante o processo de desserialização

Serializando e desserializando tipos complexos No exemplo anterior, serializamos

tipos simples; os membros da classe Employee eram tipos simples, como um inteiro e uma string. No entanto, as classes
do mundo real costumam ser complexas. Eles podem conter membros que são tipos de classe, tipos enumerados ou até
matrizes. A classe XmlSerializer fornece suporte para esses tipos complexos, e é isso que você verá no próximo exemplo.

A interface do usuário do aplicativo agora muda para se parecer com a mostrada na Figura 8-4.

217
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Figura 8-4. Aplicativo para ilustrar a serialização XML de tipos complexos

As primeiras cinco caixas de texto permanecem as mesmas do exemplo anterior. No entanto, seis caixas de texto e uma
caixa de combinação são novas. As caixas de texto recém-adicionadas capturam as informações de e-mail, rua, cidade,
estado, país e código postal do funcionário. A caixa de combinação captura o tipo de funcionário (permanente ou contratado).
Para armazenar as informações de endereço dos funcionários, você precisa adicionar uma propriedade chamada
Address à classe Employee. A própria propriedade Address é do tipo Address. A classe Address é mostrada na Listagem 8-6.

Listagem 8-6. A classe de endereço

endereço de classe pública


{
public string Rua { get; definir; } public string
Cidade { get; definir; } string pública Estado
{ get; definir; } public string País { get; definir; }
public string PostalCode { get; definir; }

Essa classe tem cinco propriedades públicas para armazenar endereço, cidade, estado, país e código postal,
respectivamente.
Para armazenar o tipo de funcionário, você precisa adicionar uma propriedade chamada Type à classe Employee. A
propriedade Type será uma enumeração do tipo EmployeeType, que contém dois valores: Permanent e Contract.
A enumeração EmployeeType é mostrada na Listagem 8-7.

Listagem 8-7. A enumeração EmployeeType

public enum EmployeeType {

Contrato permanente }

As informações de e-mail são armazenadas em uma propriedade chamada Emails. Um funcionário pode ter mais de
um endereço de e-mail e, portanto, essa propriedade é do tipo string array. A Listagem 8-8 mostra a versão modificada da
classe Employee.

218
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Listagem 8-8. A classe Employee após adicionar propriedades de endereço, tipo e e-mails

public class Empregado {

public int EmployeeID { get; definir; } public


string FirstName { get; definir; } public string
Sobrenome { get; definir; } public string
HomePhone { get; definir; } string pública Notas
{ get; definir; } public string[] E-mails { get;
definir; } public EmployeeType Type { get;
definir; } Endereço público Endereço { get;
definir; } = novo Endereço();
}

Observe as definições de propriedade marcadas em negrito. As três propriedades públicas — Address,


Type e Emails — são do tipo Address, EmployeeType e string array, respectivamente. O código no manipulador
de eventos Click do botão Serialize agora muda para o código mostrado na Listagem 8-9.

Listagem 8-9. Serializando tipos complexos

private void button1_Click(object sender, EventArgs e) { Employee


emp = new Employee(); emp.EmployeeID = int.Parse(textBox1.Text);
emp.FirstName = textBox2.Text; emp.LastName = textBox3.Text;
emp.HomePhone = textBox4.Text; emp.Notes = textBox5.Text;
emp.Type = (comboBox1.SelectedIndex == 0 ?
EmployeeType.Permanent : EmployeeType.Contract);
emp.Address.Street = textBox6.Text; emp.Address.City =
textBox7.Text; emp.Address.State = textBox8.Text;
emp.Address.Country = textBox9.Text; emp.Address.PostalCode
= textBox10.Text; emp.Emails = textBox11.Text.Split(','); Fluxo de
fluxo de arquivo =

new FileStream($"{Application.StartupPath}\\employee.xml", FileMode.Create);


XmlSerializer serializer = new XmlSerializer(typeof(Employee));
serializer.Serialize(fluxo, emp); stream.Close(); if (checkBox1.Checked)
{ Process.Start($"{Application.StartupPath}\\employee.xml"); }

O código é essencialmente o mesmo dos exemplos anteriores. No entanto, ele define as propriedades recém-
adicionadas aos valores correspondentes das caixas de texto e da caixa de combinação. Observe como a propriedade
complexa Address é definida. Além disso, observe como os e-mails separados por vírgulas inseridos na caixa de texto do e-
mail são convertidos em uma matriz de strings usando o método Split(). Depois que o objeto Employee é serializado chamando
Serialize(), o documento XML serializado se parece com o mostrado na Figura 8-5.

219
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Figura 8-5. XML serializado para tipos complexos

Examine cuidadosamente os dados XML serializados. O endereço é representado pelo nó <Address>, cujo
nome é derivado da propriedade Address da classe Employee. O nó <Address> tem cinco nós filhos: <Street>,
<City>, <State>, <Country> e <PostalCode>. Seus nomes são derivados das respectivas propriedades da classe
Address.
O elemento <Type> representa a propriedade Type da classe Employee. O valor de enumeração,
Permanent, é armazenado na marcação XML. Por fim, o nó <Emails> representa a propriedade Emails e seus nós
filhos nada mais são do que elementos individuais da matriz. Como os e-mails são armazenados em uma matriz de
string, os valores individuais são incluídos em elementos <string></string>.
A Listagem 8-10 mostra o código no evento Click do botão Deserialize.

Listagem 8-10. Desserializando tipos complexos

private void button2_Click(object sender, EventArgs e) { FileStream


stream = new FileStream($"{Application.StartupPath}\\employee.xml",
FileMode.
Abrir);
XmlSerializer serializer = new XmlSerializer(typeof(Employee)); Employee
emp=(Employee)serializer.Deserialize(stream); stream.Close(); textBox1.Text =
emp.EmployeeID.ToString();

220
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

textBox2.Text = emp.FirstName;
textBox3.Text = emp.LastName;
textBox4.Text = emp.HomePhone;
textBox5.Text = emp.Notas;
comboBox1.SelectedIndex = (emp.Type == EmployeeType.Permanent?0:1);
textBox6.Text=emp.Address.Street; textBox7.Text=emp.Address.City;
textBox8.Text=emp.Address.State; textBox9.Text=emp.Address.Country;
textBox10.Text=emp.Address.PostalCode; textBox11.Text = string.Join(",", emp.Emails);
stream.Close(); }

O código é praticamente o mesmo dos exemplos anteriores. Ele desserializa o objeto Employee serializado
anteriormente usando a classe XmlSerializer. Os valores de propriedade são atribuídos a vários controles no formulário.
Observe como a propriedade Emails é convertida em uma string separada por vírgulas usando o método Join() da classe
string. Vale a pena observar os seguintes pontos ao serializar tipos complexos:

•Para serializar e desserializar valores enumerados, o aplicativo que serializa o objeto e o aplicativo
que o desserializa devem definir a mesma enumeração em consideração.

•Ao serializar as propriedades do objeto, todos os membros públicos do objeto são


serializados. Os nomes dos membros são atribuídos aos elementos filhos no XML
resultante.

•Durante o processo de desserialização, XmlSerializer instancia a classe principal (Employee)


assim como todas as subclasses (Address) e atribui valores às respectivas propriedades.

•Ao serializar arrays, um elemento XML representa o array. Os elementos individuais da matriz formam
o elemento filho desse elemento. Os elementos individuais da matriz são incluídos em um
elemento, dependendo do tipo de dados da matriz.

•Durante a desserialização, o XmlSerializer cria uma matriz com o mesmo número de elementos
que os elementos serializados. Em seguida, ele atribui os valores dos elementos da matriz
de acordo.

Serialização e Herança
A serialização não se limita a tipos simples e complexos. É igualmente aplicável a classes herdadas. Suponha que você
tenha uma classe chamada Manager que herda de nossa classe Employee. Agora, quando você serializar Manager, todas as
propriedades públicas da classe base Employee e Manager serão serializadas. Isso também é verdade no caso de uma longa
cadeia de herança.
Para demonstrar como as classes herdadas são serializadas, precisamos adicionar uma classe chamada
Manager ao nosso aplicativo. A classe Manager herda da classe Employee (consulte a Listagem 8-1) e a estende adicionando
uma propriedade inteira NoOfSubordinates. A classe Manager é mostrada na Listagem 8-11.

221
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Listagem 8-11. A classe do gerente

public class Gerente: Empregado {

public int NoOfSuborders { get; definir; }


}

O código cria uma classe chamada Manager que herda da classe Employee. Em seguida, adiciona um número inteiro
propriedade—NoOfSubordinates—para armazenar o número de subordinados de um gerente. Para acomodar a
propriedade adicional, a interface do usuário do aplicativo muda, conforme mostrado na Figura 8-6.

Figura 8-6. Aplicativo para demonstrar serialização de classes herdadas

O aplicativo é quase o mesmo da Figura 8-1, mas há uma caixa de texto extra para aceitar o número de
subordinados do gerente. A Listagem 8-12 mostra o manipulador de eventos Click do botão Serialize.

Listagem 8-12. Serializando a classe gerenciadora herdada

private void button1_Click(object sender, EventArgs e) { Manager manager


= new Manager(); gerente.EmployeeID = int.Parse(textBox1.Text);
gerente.FirstName = textBox2.Text; gerente.LastName = textBox3.Text;
gerente.HomePhone = textBox4.Text; gerente.Notas = textBox5.Text;
manager.NoOfSubordinates = int.Parse(textBox6.Text); Fluxo de
fluxo de arquivo =

new FileStream($"{Application.StartupPath}\\employee.xml", FileMode.Create);


XmlSerializer serializer = new XmlSerializer(typeof(Manager));
serializer.Serialize(fluxo, gerenciador); stream.Close();

222
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

if (checkBox1.Checked) {

Process.Start($"{Application.StartupPath}\\employee.xml"); }

O código é essencialmente o mesmo que usamos até agora, mas usa a classe Manager em vez da classe
Employee. Uma instância do Manager é criada e todas as suas propriedades são definidas. Em seguida, uma
instância de XmlSerializer é criada passando as informações de tipo da classe Manager. Por fim, a instância do
Manager é serializada chamando o método Serialize() de XmlSerializer. A Figura 8-7 mostra a saída XML resultante.

Figura 8-7. XML serializado da classe Manager

Observe como todas as propriedades públicas da classe base Employee, bem como a do Manager, são
serializado. O código para desserializar a classe Manager é muito semelhante ao que usamos anteriormente.
A Listagem 8-13 mostra esse código.

Listagem 8-13. Desserializando a classe Manager

private void button2_Click(remetente do objeto, EventArgs e) {

Fluxo de fluxo de arquivo =


new FileStream($"{Application.StartupPath}\\employee.xml", FileMode.Open);
XmlSerializer serializer = new XmlSerializer(typeof(Manager)); Manager
manager = (Manager)serializer.Deserialize(stream); stream.Close();
textBox1.Text = gerente.EmployeeID.ToString(); textBox2.Text = gerente.FirstName;
textBox3.Text = gerente.LastName;

223
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

textBox4.Text = gerente.HomePhone;
textBox5.Text = gerenciador.Notas;
textBox6.Text = manager.NoOfSubordinates.ToString(); }

A única diferença nesse código é que ele usa Manager no processo de desserialização em vez de Employee.

Personalizando o XML serializado A classe XmlSerializer

usa automaticamente o nome dos membros públicos como os nomes dos elementos XML resultantes. Isso é o que é
necessário em muitos casos. No entanto, às vezes pode ser necessário customizar os dados XML serializados para atender
às suas necessidades. No exemplo anterior que ilustra a serialização de tipos complexos, obtivemos o documento XML
mostrado na Listagem 8-14.

Listagem 8-14. Documento XML serializado sem qualquer personalização

<?xml versão="1.0"?>
<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://
www.w3.org/2001/XMLSchema"> <EmployeeID>1</EmployeeID > <FirstName>Nancy</
FirstName> <LastName>Davolio</LastName>

<HomePhone>(206) 555-9857</HomePhone>
<Notas>A educação inclui bacharelado em psicologia pela Colorado State University em 1970.
Ela também completou "The Art of the Cold Call". Nancy é membro do Toastmasters International.</Notes>

<Type>Permanente</Type>
<E-mails>
<string>nancy@localhost</string>
</E-mails>
<Endereço>
<Rua>507 - 20th Ave. E. Apt. 2A</Street> <City>Seattle</
City> <State>Washington</State> <Country>EUA</
Country> <PostalCode>98122</PostalCode> </Address>

</Empregado>

No entanto, e se você quiser que a estrutura XML resultante se assemelhe à Listagem 8-15?

Listagem 8-15. XML serializado após personalização

<?xml version="1.0"?>
<MeuFuncionário xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/ XMLSchema" EmployeeCode="1"> <FName>Nancy</
FName> <LName>Davolio</LName>

224
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

<Observações>

A educação inclui bacharelado em psicologia pela Colorado State University em 1970. Ela também concluiu "The Art of the Cold
Call". Nancy é membro do Toastmasters International.
</Remarks>
<EmployeeType>Funcionário permanente</EmployeeType>
<EmailAddresses>
<Email>nancy@localhost</Email> </
EmailAddresses> <Endereço> <Rua>507 - 20th
Ave. E. Apt. 2A</Street> <City>Seattle</City>
<State>Washington</State> <Country>EUA</Country>
<PostalCode>98122</PostalCode> </Address> </MyEmployee>

Observe a Listagem 8-15 cuidadosamente. Existem algumas mudanças significativas:

•O elemento raiz do documento é <MyEmployee> e não <Employee>.

•Os nomes dos elementos são totalmente diferentes dos nomes das propriedades públicas.

•A ID do funcionário é armazenada como o atributo EmployeeCode.

• O valor da enumeração EmployeeType é diferente da enumeração real


texto do item.

•Os endereços de e-mail são armazenados como elementos <Email> e não como elementos <string>.

• O valor da propriedade HomePhone não é serializado, mesmo que seja um membro público
da classe.

Para obter essa personalização, o namespace System.Xml.Serialization fornece vários atributos.


Você é obrigado a decorar suas classes, enumerações e propriedades com esses atributos para personalizar a maneira como são
serializados. A Listagem 8-16 mostra a classe Employee e a enumeração EmployeeType após a aplicação de muitos desses atributos.

Listagem 8-16. Personalizando a serialização usando atributos

[XmlRoot(ElementName="MeuFuncionário")]
public class Funcionário {

[XmlAttribute(AttributeName = "EmployeeCode")] public int


EmployeeID { get; definir; }

[XmlElement(ElementName = "FName")] public


string FirstName { get; definir; }

[XmlElement(ElementName = "LName")] public


string LastName { get; definir; }

[XmlIgnore]
public string HomePhone { get; definir; }

225
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

[XmlElement(ElementName = "Comentários")]
public string Notas { get; definir; }

[XmlArray(ElementName = "EmailAddresses")]
[XmlArrayItem(ElementName = "Email")]
public string[] E-mails { get; definir; }

[XmlElement(ElementName = "EmployeeType")]
public EmployeeType Type { get; definir; }

[XmlElement(IsNullable = true)]
endereço público Endereço { get; definir; } = novo Endereço();
}

public enum EmployeeType {

[XmlEnum(Name="Funcionário Permanente")]
Permanente,
[XmlEnum(Name = "Empregado com contrato")]
Contrato
}

Vamos dissecar a listagem anterior passo a passo e ver o significado de cada atributo usado.

Alterando a Raiz do Documento XML


Por padrão, a classe XmlSerializer usa o nome da classe como o nome do elemento raiz XML. Para alterar esse
comportamento, você pode decorar sua classe com o atributo [XmlRoot]. O atributo [XmlRoot] possui uma propriedade
chamada ElementName que indica o novo nome do elemento raiz do documento XML. O atributo [XmlRoot] deve ser
aplicado a uma definição de classe e, portanto, o colocamos no topo da classe Employee.

Mudando os Nomes dos Elementos


Por padrão, a classe XmlSerializer usa os nomes dos membros públicos para atribuir aos elementos XML de saída.
Por exemplo, a propriedade FirstName é serializada como o elemento <FirstName>. Esse comportamento padrão
pode ser alterado usando o atributo [XmlElement]. O atributo [XmlElement] tem uma propriedade chamada
ElementName que especifica o nome do elemento XML resultante. O atributo [XmlElement] é aplicado ao membro
público que será serializado e, portanto, as propriedades FirstName, LastName, Notes, Type e Address são decoradas
com o atributo [XmlElement].

Serializando Membros como Atributos


Por padrão, todos os membros públicos de sua classe são serializados como elementos XML no documento de saída.
O atributo [XmlAttribute] permite alterar esse comportamento padrão. A propriedade AttributeName do atributo
[XmlAttribute] indica o nome que será dado ao atributo XML resultante.
[XmlAttribute] é aplicado ao membro público que você deseja serializar como um atributo. Em nosso exemplo,
adicionamos o atributo [XmlAttribute] à propriedade EmployeeID.

226
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Ignorando membros públicos no processo de serialização

Por padrão, todos os membros públicos de uma classe são serializados, mas às vezes isso não é o que você deseja.
Por exemplo, se você estiver armazenando informações de cartão de crédito em uma propriedade pública, talvez não
queira serializá-las por razões óbvias de segurança. Um membro público pode ser ignorado durante o processo de
serialização decorando-o com o atributo [XmlIgnore]. Em nosso exemplo, a propriedade HomePhone está marcada com este atributo.

Mudando os nomes dos arrays e dos elementos dos arrays A

classe Employee tem uma propriedade chamada Emails que é do tipo string array. No esquema de
nomenclatura padrão, quando essa propriedade é serializada, um nó XML é criado com o nome Emails. Este
nó contém ainda nós filhos, cada um contendo o valor do elemento da matriz. Os nomes dos elementos filhos
são iguais ao tipo de dados do array (<string> em nosso exemplo). Você pode alterar esse comportamento com
a ajuda dos atributos [XmlArray] e [XmlArrayElement]. O primeiro marca os membros públicos que são tipos de
matriz e especifica o nome do elemento XML para o membro. O último atributo controla o nome do elemento
XML atribuído aos membros individuais da matriz. Em nosso exemplo, a propriedade Emails será serializada
como <EmailAddresses> e cada elemento da matriz será colocado dentro de um elemento <Email>.

Ignorando objetos nulos no processo de serialização A classe Employee

tem uma propriedade Address que é um tipo de objeto. Se essa propriedade for nula, XmlSerializer ainda emite
um elemento XML vazio para ela, mas você pode usar o atributo [XmlElement] para alterar esse comportamento.
A propriedade booliana IsNullable do atributo [XmlElement] indica se o elemento XML vazio será emitido quando
o membro for nulo. Definir essa propriedade como true não emitirá o elemento XML vazio se a propriedade
Address for nula.

Alterando identificadores de enumeração A

enumeração EmployeeType tem dois valores: Permanent e Contract. Por padrão, quando um membro do tipo
EmployeeType é serializado, o valor desses identificadores de enumeração é emitido no XML serializado.
O atributo [XmlEnum] especifica o valor alternativo para serializar em vez do nome real do identificador e é aplicado em
identificadores de enumeração. A propriedade Name do atributo [XmlEnum] especifica o texto que será serializado em vez
do nome do identificador.

Serializando e desserializando objetos usando


DataContractSerializer
Na seção anterior, você usou a classe XmlSerializer para serializar e desserializar objetos. O XmlSerializer está
disponível desde a versão 1.1 do .NET Framework. Juntamente com o Windows Communication Foundation
(WCF), foi adicionada outra classe, chamada DataContractSerializer, que também pode ser usada para serialização XML. A
classe DataContractSerializer é o serializador padrão para serviços WCF, mas você também pode usá-la para serializar e
desserializar seus objetos como fez com o XmlSerializer.
Para usar o DataContractSerializer, você precisa adicionar alguns atributos às suas classes:

•A classe deve ser decorada com o atributo [DataContract].

•Cada membro da classe que você deseja serializar deve ser marcado com
Atributo [DataMember]. Membros não decorados com [DataMember] serão ignorados durante o
processo de serialização.

227
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

ÿ Nota Vamos revisitar os atributos [DataContract] e [DataMember] no Capítulo 10 quando discutirmos os


serviços WCF. Nesse momento, ficará mais claro para você como esses atributos são usados pelo WCF.
Aqui basta dizer que são necessários para serializar um objeto utilizando a classe DataContractSerializer .

Para ilustrar o uso da classe DataContractSerializer, você desenvolverá um aplicativo semelhante ao


XmlSerializer (consulte a Figura 8-1). Obviamente, você precisará modificar a classe Employee e o código que
serializa e desserializa o objeto Employee, conforme discutido a seguir.
Primeiramente, abra a classe Employee e modifique-a, conforme mostrado na Listagem 8-17.

Listagem 8-17. Marcando uma classe com os atributos [DataContract] e [DataMember]

[DataContract]
public class Funcionário {

[DataMember]
public int EmployeeID { get; definir; }
[DataMember]
public string FirstName { get; definir; }
[DataMember]
public string LastName { get; definir; }
[DataMember]
public string HomePhone { get; definir; }
[DataMember]
string pública Notas { get; definir; }
}

O atributo [DataContract] adicionado à classe Employee indica que a classe Employee é um data
contrato e pode ser serializado por DataContractSerializer. O atributo [DataMember] adicionado às
propriedades da classe Employee indica que o membro em questão faz parte de um contrato de dados e pode ser
serializado pelo DataContractSerializer.
Em seguida, modifique o manipulador de eventos Click do botão Serialize, conforme mostrado na Listagem 8-18.

Listagem 8-18. Serializar dados usando DataContractSerializer

private void button1_Click(remetente do objeto, EventArgs e) {

Funcionário emp = new Funcionário();


emp.EmployeeID = int.Parse(textBox1.Text);
emp.FirstName = textBox2.Text; emp.LastName =
textBox3.Text; emp.HomePhone = textBox4.Text;
emp.Notes = textBox5.Text; FileStream stream = new
FileStream($"{Application.StartupPath}\\employee.xml",
FileMode.
Criar);

DataContractSerializer serializer = new DataContractSerializer(typeof(Employee));


serializer.WriteObject(fluxo, emp);

228
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

stream.Close(); if
(checkBox1.Checked) {

Process.Start($"{Application.StartupPath}\\employee.xml");
}
}

A maior parte do código mostrado na Listagem 8-18 deve parecer familiar para você, exceto as linhas marcadas em negrito. O
a primeira linha marcada em negrito cria uma instância de DataContractSerializer passando as informações de
tipo da classe Employee. A segunda linha chama o método WriteObject() de DataContractSerializer. O método
WriteObject() aceita dois parâmetros — um objeto de fluxo para gravar os dados e o objeto a ser serializado.

Se você executar o aplicativo e clicar no botão Serialize após preencher várias caixas de texto, o resultado
O documento XML deve se parecer com a Figura 8-8.

Figura 8-8. XML serializado por DataContractSerializer

Para desserializar XML previamente serializado usando DataContractSerializer, você pode usar o método
ReadObject(). A Listagem 8-19 mostra o manipulador de eventos Click do botão Deserialize que ilustra o uso de
ReadObject().

Listagem 8-19. Serializar dados usando DataContractSerializer

private void button2_Click(remetente do objeto, EventArgs e) {

FileStream stream = new FileStream($"{Application.StartupPath}\\employee.xml", FileMode.Open);

DataContractSerializer serializer = new DataContractSerializer(typeof(Employee)); Employee emp


= (Employee)serializer.ReadObject(stream);

stream.Close();
textBox1.Text = emp.EmployeeID.ToString();
textBox2.Text = emp.FirstName;

229
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

textBox3.Text = emp.LastName;
textBox4.Text = emp.HomePhone;
textBox5.Text = emp.Notas;
}

Observe o código marcado em negrito. A primeira linha cria uma instância de DataContractSerializer passando as
informações de tipo da classe Employee. A segunda linha então chama o método ReadObject() no DataContractSerializer. O
fluxo a ser lido é fornecido como parâmetro para ReadObject(). O ReadObject() lê um objeto, que é então convertido para o
tipo Employee.
Se você executar o aplicativo novamente e clicar no botão Deserialize, verá que todas as caixas de texto
estão preenchidos com os valores corretos.

Personalizando o XML serializado Como você deve ter

observado, o DataContractSerializer usa o nome de classe e os nomes de propriedade por padrão para os elementos XML
resultantes. Você pode personalizar os nomes dos elementos XML especificando-os nos atributos [DataContract] e [DataMember].
A Listagem 8-20 mostra como isso é feito.

Listagem 8-20. Personalizando nomes de elementos XML

[DataContract(Name ="MeuFuncionário")] public


class Funcionário {

[DataMember(Name ="EmployeeCode")] public


int EmployeeID { get; definir; }
[DataMember(Name = "FName")] public
string FirstName { get; definir; }
[DataMember(Name = "LName")] public
string LastName { get; definir; }
[IgnoreDataMember]
public string HomePhone { get; definir; }
[DataMember(Name = "Comentários")]
public string Notas { get; definir; }
}

O atributo [DataContract] agora especifica sua propriedade Name como MyEmployee. Assim, o nome do elemento raiz será
MyEmployee. Os atributos [DataMember] adicionados às propriedades EmployeeID, FirstName, LastName e Notes definem suas
propriedades Name como EmployeeCode, FName, LName e Remarks, respectivamente. A propriedade HomePhone é decorada
com o atributo [IgnoreDataMember], indicando que HomePhone não será serializado.

A Figura 8-9 mostra um exemplo de XML gerado após a personalização que acabamos de discutir.

230
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Figura 8-9. Os nomes dos elementos XML agora são personalizados

Como você pode ver, os nomes dos elementos XML agora estão de acordo com nossa propriedade Name. Além disso, o
HomePhone não foi serializado. Se você desserializar esse XML, descobrirá que todas as caixas de texto, exceto HomePhone, são
preenchidas conforme o esperado. A caixa de texto HomePhone permanece vazia por razões óbvias.

Serializando e desserializando objetos usando SoapFormatter


No início deste capítulo, você aprendeu que existem três tipos de serialização com base no formato (binário, XML e SOAP).
Serializar objetos em formato binário está fora do escopo deste livro e você já aprendeu como serializar objetos em formato XML
usando as classes XmlSerializer e DataContractSerializer. Agora é hora de aprender como os objetos podem ser serializados no
formato SOAP usando a classe SoapFormatter.

ÿ Observação A classe SoapFormatter usada nesta seção agora é considerada obsoleta. Discutimos isso aqui
porque alguns aplicativos mais antigos ainda podem estar usando. Além disso, dá a você a chance de se familiarizar
com o formato SOAP sem entrar nos detalhes dos serviços da Web ASMX ou dos serviços WCF.

SOAP é um padrão da indústria que forma um dos pilares dos serviços da web. Embora o SOAP seja usado
extensivamente junto com serviços da web, você pode usá-lo como um formato de codificação para serialização de objetos.
Ao serializar objetos usando a classe XmlSerializer, você não precisa fazer nada de especial para o
próprias aulas. No entanto, quando você deseja usar o SOAP como formato de serialização, deve marcar suas classes com o atributo
[Serializable]. Só então suas classes podem ser serializadas.
A classe SoapFormatter cuida de todas as complexidades da serialização de seus objetos no formato SOAP. A classe SoapFormatter
reside no namespace System.Runtime.Serialization.Formatters.Soap, que reside fisicamente no assembly
System.Runtime.Serialization.Formatters.Soap.dll.
Vamos revisitar o aplicativo que desenvolvemos quando começamos este capítulo (consulte a Figura 8-1) e modificá-lo para usar
SoapFormatter em vez de XmlSerializer. A interface do usuário do aplicativo permanece inalterada, mas a maneira como serializamos e
desserializamos os objetos é diferente.
Primeiro, você precisa marcar a classe Employee com o atributo [Serializable]. A classe Employee modificada é mostrada na
Listagem 8-21.

231
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Listagem 8-21. Marcando uma classe com o atributo [Serializable]

[Serializável]
public class Employee {

public int EmployeeID { get; definir; } public


string FirstName { get; definir; } public string
Sobrenome { get; definir; } public string
HomePhone { get; definir; } string pública Notas
{ get; definir; }

Como você pode ver, o atributo [Serializable] é um atributo de nível de classe. Portanto, ele é colocado no
topo da classe Employee e marcado como uma classe serializável. A Listagem 8-22 mostra o manipulador de
eventos Click do botão Serialize. Desta vez, o código usa a classe SoapFormatter.

Listagem 8-22. Serializando objetos usando a classe SoapFormatter

private void button1_Click(object sender, EventArgs e) { Employee


emp = new Employee(); emp.EmployeeID = int.Parse(textBox1.Text);
emp.FirstName = textBox2.Text; emp.LastName = textBox3.Text;
emp.HomePhone = textBox4.Text; emp.Notes = textBox5.Text;
Fluxo de fluxo de arquivo =

new FileStream($"{Application.StartupPath}\\employee.xml", FileMode.Create);

SoapFormatter formatador = new SoapFormatter();


formatador.Serialize(fluxo, emp); stream.Close(); if
(checkBox1.Checked) { Process.Start($"{Application.StartupPath}
\\employee.xml"); }

O código cria uma instância da classe Employee e define suas propriedades com os valores inseridos nas
caixas de texto. Um objeto FileStream é então criado e cria um arquivo no qual os dados serializados devem ser
gravados. Em seguida, um objeto SoapFormatter é criado. O método Serialize() de SoapFormatter aceita dois
parâmetros: um fluxo no qual os dados serializados devem ser gravados e o objeto que deve ser serializado. A
contrapartida dessa operação é executada no manipulador de eventos Click do botão Deserialize e é mostrada na
Listagem 8-23.

232
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Listagem 8-23. Desserialização usando a classe SoapFormatter

private void button2_Click(remetente do objeto, EventArgs e) {

Fluxo de fluxo de arquivo =


new FileStream($"{Application.StartupPath}\\employee.xml", FileMode.Open);
SoapFormatter formatador = new SoapFormatter(); Employee
emp=(Employee)formatter.Deserialize(stream); textBox1.Text =
emp.EmployeeID.ToString(); textBox2.Text = emp.FirstName; textBox3.Text
= emp.LastName; textBox4.Text = emp.HomePhone; textBox5.Text =
emp.Notas; stream.Close(); }

O código abre um stream apontando para o mesmo arquivo para o qual o objeto foi serializado anteriormente. Uma instância
de SoapFormatter é então criado. O método Deserialize() de SoapFormatter lê o fluxo e desserializa o objeto. O valor de
retorno de Deserialize() é do tipo object e, portanto, é o tipo convertido para a classe Employee. Depois que o objeto Employee
é recuperado, seus valores de propriedade são atribuídos às caixas de texto correspondentes. Se você executar o aplicativo e
serializar o objeto Employee, deverá ver uma saída semelhante à Figura 8-10.

Figura 8-10. Objeto serializado no formato SOAP

Como você pode ver, a saída XML agora está no formato SOAP. Também há menção de alguns namespaces
relacionados ao SOAP. Observe que os nomes dos elementos XML não são iguais aos nomes das propriedades; em vez disso,
os nomes dos elementos exclusivos são formados usando os nomes das propriedades. Ao contrário da serialização XML, que é
superficial por natureza, a serialização SOAP feita por meio da classe SoapFormatter é uma serialização profunda. Serializa
membros privados, protegidos e públicos de um objeto.

233
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Personalizando a serialização SOAP


Na seção anterior, você usou o comportamento padrão da serialização SOAP para serializar e desserializar objetos. Você
também pode personalizar o processo de serialização SOAP, se desejar. Existem duas maneiras de conseguir isso:

•Implementar a interface ISerializable.

•Use certos atributos de serialização e desserialização.

O primeiro método está disponível desde o .NET Framework 1.1. Este último método foi introduzido em
.NET 2.0. Em nosso exemplo, usaremos os dois métodos para customizar o processo de serialização.
Usaremos o mesmo aplicativo que desenvolvemos na seção anterior enquanto ilustramos o uso da classe SoapFormatter.
Suponha que você queira customizar os nomes dos elementos na mensagem SOAP resultante. Você também deseja controlar
quais propriedades estão sendo serializadas.
Por fim, suponha também que você deseja proteger os dados XML serializados de usuários casuais. Você deseja implementar
a codificação Base64 para os dados que estão sendo serializados para que leitores casuais não possam ler facilmente o conteúdo
do arquivo. Esses dados precisam ser codificados em um esquema de codificação Base64 e decodificados quando desserializados.
Abra a classe Employee que você criou anteriormente e implemente a interface ISerializable nela. como parte
da implementação, você precisa fazer duas coisas:

•Implemente o método GetObjectData().

•Adicione um construtor que lida com a desserialização.

A Listagem 8-24 mostra o método GetObjectData() adicionado à classe Employee.

Listagem 8-24. GetObjectData() adicionado à classe Employee

public void GetObjectData(informações de SerializationInfo, contexto de StreamingContext) {

info.AddValue("EmpCode", EmployeeID, typeof(int));


info.AddValue("FName", FirstName, typeof(string));
info.AddValue("LName", LastName, typeof(string));
info.AddValue("Comentários", Notas, typeof(string));
}

O método GetObjectData() tem dois parâmetros — SerializationInfo e StreamingContext. O


O método GetObjectData() é chamado depois que você chama o método Serialize() no objeto SoapFormatter.
O código dentro de GetObjectData() usa o método AddValue() do objeto SerializationInfo para decidir quais valores
serão serializados. Por exemplo, a primeira chamada para AddValue() especifica que o valor EmployeeID será serializado
como um elemento EmpCode. O tipo de dados do valor também é especificado usando o terceiro parâmetro. Observe que
não serializamos a propriedade HomePhone da classe Employee. As outras propriedades são serializadas como EmpCode,
FName, LName e Remarks, respectivamente.
Em seguida, adicione um construtor à classe Employee que corresponda à assinatura mostrada na Listagem 8-25. Esse
O construtor será chamado quando você chamar o método Deserialize() do objeto SoapFormatter.

Listagem 8-25. Construtor que lida com a desserialização

Protected Employee(SerializationInfo info, StreamingContext context) {

EmployeeID = (int)info.GetValue("EmpCode", typeof(int)); FirstName =


(string)info.GetValue("FName", typeof(string)); LastName =
(string)info.GetValue("LName", typeof(string)); Notas =
(string)info.GetValue("Comentários", typeof(string)); Telefone Residencial = "";

234
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

O construtor aceita os mesmos dois parâmetros do método GetObjectData(). A finalidade desse construtor é recuperar
valores serializados anteriormente e atribuí-los às propriedades.
Isso é feito usando o método GetValue() da classe SerializationInfo. O método GetValue() aceita o nome
associado ao valor a ser recuperado e seu tipo de dado. Ao recuperar o valor, ele é convertido e atribuído à propriedade
correspondente do objeto Employee que está sendo construído. Observe que a propriedade HomePhone recebe um valor de
string vazio, pois o ignoramos durante o processo de serialização.
Agora adicione duas funções auxiliares chamadas Encode() e Decode() ao aplicativo anterior, conforme mostrado na
Listagem 8-26.

Listagem 8-26. Codificando e decodificando dados usando a codificação Base64

string privada Encode(string str) { byte[] data


= ASCIIEncoding.ASCII.GetBytes(str); return
Convert.ToBase64String(dados); }

string privada Decode(string str) { byte[]


data=Convert.FromBase64String(str); return
ASCIIEncoding.ASCII.GetString(data); }

Certifique-se de adicionar esses métodos auxiliares à classe Employee. A função Encode() aceita uma string que deve
ser codificada no formato Base64. Em seguida, ele converte a string em uma matriz de bytes usando o método GetBytes()
da classe ASCIIEncoding. A matriz de bytes é então alimentada ao método ToBase64String() da classe Convert, que retorna
uma string codificada em Base64 representando a matriz de bytes fornecida.
A função Decode() aceita uma string codificada em Base64 que deve ser decodificada de volta para uma
representação de string simples. Em seguida, ele chama o método FromBase64String() da classe Convert e passa a string
Base64 fornecida para ele. O método FromBase64String() retorna uma matriz de bytes representando a versão decodificada
da string fornecida. A matriz de bytes é convertida em uma string usando o método GetString() da classe ASCIIEncoding.

Agora precisamos adicionar quatro métodos à classe Employee, conforme mostrado na Listagem 8-27.

Listagem 8-27. Personalizando serialização e desserialização SOAP

[OnSerializing]
public void OnSerializing(StreamingContext context) {

PrimeiroNome = Encode(PrimeiroNome);
Sobrenome = Encode(Sobrenome);
HomePhone = Codificar(HomePhone);
Notas = Encode(Notas);
}

[OnSerialized]
public void OnSerialized(StreamingContext context) {

PrimeiroNome = Decode(PrimeiroNome);
Sobrenome = Decode(Sobrenome);
HomePhone = Decode(HomePhone);
Notas = Decodificar(Notas);
}

235
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

[OnDeserializing] public
void OnDeserializing(StreamingContext context) {

//sem código aqui


}

[OnDeserialized]
public void OnDeserialized(StreamingContext context) {

PrimeiroNome = Decode(PrimeiroNome);
Sobrenome = Decode(Sobrenome);
HomePhone = Decode(HomePhone);
Notas = Decodificar(Notas);
}

Os quatro métodos são marcados com os atributos [OnSerializing], [OnSerialized], [OnDeserializing] e [OnDeserialized].
Esses atributos permitem personalizar o processo de serialização e desserialização usando métodos anteriores e posteriores:

•O método marcado com [OnSerializing] é chamado automaticamente pelo


estrutura de serialização antes que os dados sejam serializados.

•O método marcado com [OnSerialized] é chamado quando a serialização é


completo.
• Da mesma forma, os métodos marcados com [OnDeserializing] e [OnDeserialized] são chamados
antes e depois da operação de desserialização.

Todos esses métodos devem aceitar um parâmetro do tipo StreamingContext. O StreamingContext


O parâmetro fornece informações adicionais sobre o processo de serialização ou desserialização.
Em nosso exemplo, o método OnSerializing() chama o método auxiliar Encode() que criamos anteriormente
para codificar os valores de propriedade no formato Base64. Assim, os dados que estão sendo serializados não são uma string
simples, mas uma string Base64. Após a conclusão da serialização, ainda podemos precisar dos mesmos dados no formato de
string simples. É por isso que o método Decode() é chamado no método OnSerialized().
O método OnDeserializing() não inclui nenhum código em nosso exemplo. No entanto, se você deseja executar algum
código antes que a desserialização ocorra, você pode adicionar sua lógica personalizada neste método. Depois que os dados
serializados anteriormente são desserializados, ele deve nos fornecer os valores no formato de string simples e não no formato
Base64. Portanto, o método OnDeserialized() chama Decode() e converte os valores Base64 em texto simples. A Figura 8-11
mostra um exemplo de pós-serialização de dados SOAP.

236
Machine Translated by Google

Capítulo 8 ÿ Serialização XML

Figura 8-11. Dados codificados em Base64 após a serialização

Observe como todos os dados são serializados no formato Base64. Observe também como os nomes dos elementos são
escolhidos a partir dos valores especificados no método GetObjectData().
Se você clicar no botão Deserialize, verá que todas as caixas de texto, exceto HomePhone, estão
preenchido com os valores corretos. Isso ocorre porque ignoramos o HomePhone durante a serialização.

Resumo
Neste capítulo, examinamos o processo de serialização XML em detalhes. O próprio .NET Framework usa a serialização
em muitos lugares, incluindo serviços Web e serviços WCF. As três classes, ou seja, XmlSerializer, DataContractSerializer
e SoapFormatter, permitem que você serialize seus objetos em XML e SOAP. O XmlSerializer é usado pelos serviços Web
XML para realizar a serialização.
DataContractSerializer é usado pelos serviços WCF e API da Web para realizar a serialização.
A serialização XML feita usando XmlSerializer pode ser personalizada com a ajuda de vários atributos
como [XmlRoot] e [XmlElement]. A serialização XML feita usando DataContractSerializer pode ser personalizada com a ajuda de
atributos como [DataContract] e [DataMember]. A serialização SOAP feita usando SoapFormatter pode ser personalizada
implementando a interface ISerializable. Você também pode usar atributos como [OnSerializing] e [OnDeserialized] para controlar
o pré e o pós-processamento dos dados.

No próximo capítulo, você aprenderá sobre os serviços Web XML. Você também aprenderá mais sobre o
Protocolo SOAP e padrões relacionados.

237
Machine Translated by Google

CAPÍTULO 9

XML em Web Services

A ideia de desenvolvimento distribuído de aplicativos não é nova. Tecnologias distribuídas, como Distributed Component Object Model
(DCOM), Remote Method Invocation (RMI) e Common Object Request Broker Architecture (CORBA) existem há anos. No entanto,
nenhuma dessas tecnologias é um padrão inequívoco da indústria. É aí que entram os serviços baseados em padrões. Um serviço
expõe algumas funcionalidades como uma API (Application Programming Interface) que os clientes podem consumir. A transferência
de dados entre o cliente e o serviço geralmente ocorre em um formato conhecido por ambas as partes (como binário, SOAP, XML e
JSON).

O .NET Framework permite que você crie serviços usando três maneiras principais — Web Services (também chamados de
serviços da Web ASMX ou serviços da Web XML), Windows Communication Foundation (WCF) e API da Web.
Essas estruturas foram desenvolvidas em diferentes pontos no tempo com base nas tendências e demandas do setor
predominantes nesses respectivos momentos. Os serviços da Web permitem que você crie serviços baseados em SOAP sobre
HTTP. O WCF é uma estrutura genérica de desenvolvimento de serviços que pode lidar com vários canais de comunicação, como
TCP e HTTP, e formatos como binário, SOAP, XML e JSON. A API da Web é uma estrutura para construir serviços RESTful baseados em
HTTP. Este capítulo discute o primeiro tipo - os serviços da web. Os outros dois sabores são discutidos no próximo capítulo.

Deve-se notar que os serviços da Web agora são considerados uma forma mais antiga de desenvolver serviços. Embora o .NET
Framework e o Visual Studio ainda os suportem, os aplicativos modernos preferem o WCF e a API da Web aos serviços da Web. No
entanto, este capítulo ainda discute os serviços da Web porque isso permitirá que você se familiarize com os protocolos e padrões
baseados em XML, como SOAP e WSDL.
Lembre-se de que este capítulo não visa ensinar o desenvolvimento de serviços da Web em detalhes. Ele se concentra
principalmente na construção de serviços da Web ASMX baseados em dados básicos e no papel desempenhado pelo XML.
Especificamente, você aprende sobre os seguintes tópicos:

•Quais são os serviços da web

•Criação e consumo de serviços da Web usando o .NET Framework

•O que é SOAP e como os serviços da web o utilizam

•O que é WSDL e qual é o seu propósito

O que são Web Services?


O conceito de serviços da Web pode ser melhor compreendido com a ajuda de componentes que você pode ter criado usando o .NET
Framework. O que é um componente? Um componente é uma peça de software reutilizável que fornece determinada funcionalidade ao
seu aplicativo. Por exemplo, um componente desenvolvido para um aplicativo bancário pode fornecer serviços como cálculo de
empréstimo, cálculo de juros e assim por diante. Se você precisar da mesma lógica de negócios em qualquer outro lugar, esse componente
será de grande utilidade. Os componentes também isolam sua lógica de negócios do restante do aplicativo. Esses componentes não
fornecem nenhuma interface de usuário para seu aplicativo. Eles simplesmente fornecem os serviços necessários para você.

© Bipin Joshi 2017 239


B. Joshi, Beginning XML with C# 7, https://doi.org/10.1007/978-1-4842-3105-0_9
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Geralmente, os componentes residem na mesma máquina que seu aplicativo. E se eles estiverem localizados em um servidor
separado? E se a rede envolvida não for uma LAN, mas a Internet? E se você quiser hospedar os componentes em uma caixa Unix e
consumi-los de uma máquina Windows? É aí que os serviços da web entram em cena.

Você pode pensar nos serviços da Web como componentes que residem em um servidor da Web, enquanto os aplicativos os
consomem em uma rede. Mais formalmente, os serviços da Web podem ser definidos como um conjunto de APIs programáveis que
podem ser chamadas em uma rede usando XML, SOAP e HTTP.
Os serviços da Web são um padrão do setor e nenhuma empresa possui serviços da Web. Os três padrões – XML,
SOAP e HTTP – são os pilares da infraestrutura de serviço da web. A seguir estão alguns pontos a serem lembrados sobre os serviços
da web:

•Os padrões de serviço da Web são padrões da indústria independentes de plataforma.

•Os serviços da Web não fornecem nenhuma interface de usuário. Eles fornecem apenas funcionalidade ou
serviços ao seu aplicativo.

•Serviços da Web usam XML, SOAP e HTTP como protocolos de comunicação.

•Os serviços da Web usam o mesmo modelo de solicitação-resposta usado pelos aplicativos da Web.

•Toda a comunicação entre um serviço da Web e seu cliente ocorre em um texto simples
formatar.

•Os serviços da Web podem residir em qualquer servidor da Web, desde que o cliente tenha
conectividade de rede com esse servidor.

•Um serviço web e seu cliente podem ser desenvolvidos usando plataformas completamente diferentes.
Por exemplo, você pode desenvolver um serviço da Web usando o .NET Framework e consumi-
lo em um aplicativo Java.

•Os serviços da Web no .NET Framework também são chamados de serviços da Web ASMX devido à extensão
de arquivo usada pelos arquivos de serviço da Web (*.asmx).

Criando e consumindo Web Services


A construção de serviços da Web requer três etapas essenciais:

•Criando um serviço da web

•Criando um proxy para o serviço da web

•Criar um aplicativo cliente que consome o serviço da web

As plataformas modernas de desenvolvimento de software geralmente introduzem o conceito de proxy durante a comunicação
remota. Um proxy é uma entidade que representa alguma outra entidade e finge para seu aplicativo cliente que o próprio proxy é o
serviço da Web real. Ao fazer isso, o proxy o protege de detalhes de programação de rede de baixo nível (como programação de
soquete, protocolos subjacentes, formatos de comunicação e segurança). Seu aplicativo cliente nunca se comunica diretamente com o
serviço da web. Toda a comunicação (solicitação e resposta) é roteada por meio do proxy.

Se o proxy quiser fingir que o próprio proxy é o serviço da web, ele deve se parecer com o serviço da web.
Isso requer a descrição detalhada ou metadados de um serviço da web. Os padrões de serviços da Web fornecem o que é conhecido
como Web Services Description Language (WSDL). WSDL é um dialeto XML que descreve o serviço da web, listando detalhes como as
funções expostas pelo serviço da web, seus parâmetros, tipos de dados e valores de retorno. O proxy é gerado usando este documento
WSDL de um serviço da web.
A extensão de arquivo usada pelos serviços da Web do .NET Framework é .asmx. Os arquivos .asmx do serviço da Web podem ter
arquivos code-behind como ASP.NET Web Forms.

240
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Criando um WebService
Para criar um serviço Web usando o Visual Studio, você precisa criar um novo aplicativo Web chamado
EmployeeWebService e adicionar um serviço Web ASMX a ele. A Figura 9-1 mostra a caixa de diálogo New Project do Visual Studio.

Figura 9-1. Criando um novo projeto de aplicativo Web no Visual Studio

Depois de selecionar ASP.NET Web Application como o tipo de projeto e clicar em OK, selecione Empty como o projeto
modelo. Certifique-se também de marcar a caixa de seleção Web Forms. Essa caixa de diálogo é mostrada na Figura 9-2.

Figura 9-2. Criando um projeto Web Forms usando o modelo de projeto vazio
241
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Depois de criar o projeto, adicione uma pasta na pasta raiz do projeto chamada Services. Em seguida, clique com o botão
direito do mouse na pasta Serviços e escolha Adicionar &#x27A4; Novo item. Isso abrirá a caixa de diálogo Adicionar novo item,
conforme mostrado na Figura 9-3.

Figura 9-3. Adicionando um novo Web Service (ASMX) ao projeto

Pesquise por serviço da web ou localize uma entrada de serviço da web na lista. Especifique o nome do serviço web como
EmployeeManager.asmx e clique no botão Adicionar.
Agora você deve ver o EmployeeManager.asmx, bem como seu arquivo code-behind — EmployeeManager.
asmx.cs. O arquivo .asmx contém a marcação mostrada na Listagem 9-1.

Listagem 9-1. Diretiva @WebService

<%@ WebService Language="C#" CodeBehind="EmployeeManager.asmx.cs" Class="EmployeeWebService.


Services.EmployeeManager" %>

A diretiva @WebService especifica que este é um serviço da web. Observe que o atributo CodeBehind aponta
para o arquivo EmployeeManager.asmx.cs. O atributo Class especifica a classe do arquivo CodeBehind que contém a
funcionalidade do serviço da web. Se você abrir o arquivo EmployeeManager.asmx.cs, deverá ver algo semelhante à
Listagem 9-2.

Listagem 9-2. A classe de serviço da Web

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class
EmployeeManager: System.Web.Services.WebService {

[WebMethod]
public string HelloWorld() {

return "Olá Mundo";


}
}

242
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Aqui temos uma classe chamada EmployeeManager que herda da classe System.Web.Services.WebService. Na verdade,
herdar da classe WebService não é obrigatório, mas isso lhe dará facilidades adicionais, como manutenção de estado. Dentro
desta classe temos um método público chamado HelloWorld(). O método por si só não contém nada de especial - você deve ter
escrito muitos desses métodos em seus próprios aplicativos. O que o torna especial, no entanto, é o atributo WebMethod, que torna
o método chamável pela Web, ou seja, o aplicativo cliente pode chamar esse método em uma rede. Sua classe pode conter
qualquer número de métodos públicos ou privados. No entanto, apenas os métodos públicos e decorados com o atributo WebMethod
podem ser chamados pela web.

Observe que a classe Service é decorada com os atributos [WebService] e [WebServiceBinding]. O atributo
[WebService] é usado para especificar algumas informações adicionais sobre o serviço da Web, como sua descrição e namespace. A
propriedade Namespace indica o namespace XML padrão a ser usado para o serviço Web XML. Os namespaces XML permitem
identificar exclusivamente elementos e atributos de um documento XML. Cada serviço da Web precisa ter um namespace XML exclusivo
para se identificar, de modo que os aplicativos clientes possam diferenciá-lo de outros serviços da Web. Por padrão, esse namespace é
definido como http://tempuri. org/, mas é recomendável alterá-lo para algum outro URI. Por exemplo, você pode usar o nome de domínio
de sua empresa como namespace. Observe que, embora muitas vezes os namespaces XML sejam URLs, eles não precisam apontar
para recursos reais na Web. O atributo [WebServiceBinding] especifica uma ligação WSDL usada por esse serviço. Você pode pensar
em uma ligação WSDL como uma interface que define um conjunto de operações de serviço da web. O atributo ConformsTo especifica
a especificação WSI (Web Services Interoperability).

A organização WS-I fornece diretrizes para interoperabilidade de serviços da Web em várias plataformas.
Execute o aplicativo e você deverá ver algo semelhante à Figura 9-4.

Figura 9-4. página de ajuda do serviço web

243
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Você pode estar se perguntando por que nosso serviço da Web está exibindo essa interface do usuário quando
sabemos que os serviços da Web não possuem uma interface do usuário. Na verdade, esta não é uma interface de usuário do
serviço da web. Essa interface é chamada de página de ajuda do serviço da web e permite que você teste seus serviços da
web. Como os serviços da Web por si só não possuem uma interface de usuário, como você ou seus clientes os testarão para
ver se funcionam corretamente? Para ajudá-lo nesses casos, o ASP.NET gera essas páginas de ajuda automaticamente. Na
parte superior da página de ajuda, você verá um link intitulado Descrição do serviço. Basta clicar nele e você verá o WSDL do
seu serviço da Web (consulte a Figura 9-5).

Figura 9-5. WSDL de um serviço da web

Dê uma olhada na barra de endereço. Você vê o WSDL na string de consulta? É assim que você pode manualmente
recupere o WSDL de qualquer serviço da Web ASP.NET. Simplesmente anexe o WSDL no final da URL do serviço da
web e você obterá o WSDL. Clique no botão Voltar para retornar à página anterior. Você notará a lista de métodos da web
(operações). Clique no método da web HelloWorld. Você será levado para outra página de ajuda onde poderá executar este
método da web (consulte a Figura 9-6).

244
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Figura 9-6. Chamando um método da web

Antes de clicar no botão Chamar, dê uma olhada abaixo dele. Você deve ver a marcação, conforme mostrado
nas Listagens 9-3 e 9-4.

Listagem 9-3. Solicitação SOAP

POST /EmployeeWebService/EmployeeManager.asmx HTTP/1.1 Host:


localhost
Tipo de conteúdo: texto/xml; charset=utf-8 Content-
Length: comprimento SOAPAction: "http://tempuri.org/
HelloWorld"

<?xml version="1.0" encoding="utf-8"?>


<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:/ /
www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body> <HelloWorld xmlns="http://tempuri.org/ " /> </soap:Body> </soap:Envelope>

245
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Listagem 9-4. Resposta SOAP

HTTP/1.1 200 OK
Tipo de conteúdo: texto/xml; conjunto de caracteres = utf-8
Comprimento do conteúdo: comprimento

<?xml version="1.0" encoding="utf-8"?> <soap:Envelope


xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:/ /www.w3.org/2001/
XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body>
<HelloWorldResponse xmlns="http://tempuri.org/ "> <HelloWorldResult>string</HelloWorldResult>

</HelloWorldResponse> </
soap:Body> </soap:Envelope>

Esses dois blocos representam a solicitação SOAP sendo enviada ao serviço da web e a resposta SOAP sendo recebida do
serviço da web. Como você pode ver, a solicitação e a resposta SOAP consistem em uma tag chamada <soap:Envelope>. Dentro há
uma tag obrigatória chamada <soap:Body>. A tag Body contém os dados XML que estão sendo passados ou retornados. Pode haver
uma tag opcional, <soap:Header>, dentro da tag <soap:Envelope> que pode ser usada para passar dados arbitrários para o serviço da
web.
Agora clique no botão Invocar. A página de ajuda executará o método web e abrirá outra janela para
mostre a resposta do método da web (consulte a Figura 9-7).

Figura 9-7. Olá, mundo, resposta

Agora feche o navegador e retorne ao Visual Studio. Modifique o método HelloWorld(), conforme mostrado na Listagem 9-5.

Listagem 9-5. Método da Web com um parâmetro de string

[WebMethod]
string pública HelloWorld(nome da string) {

retornar "Olá" + nome;


}

246
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Aqui adicionamos um parâmetro de string ao método HelloWorld(). O método agora retorna Hello
concatenado com o nome fornecido. Execute o serviço da Web novamente. Desta vez, você deve ver uma página de ajuda, conforme
mostrado na Figura 9-8, para invocar o método da web.

Figura 9-8. Chamando um método da web com um parâmetro de string

ASP.NET gera automaticamente uma caixa de texto para você inserir o parâmetro. Claro, isso funciona apenas para tipos de
dados primitivos, como strings e inteiros. O ASP.NET não poderá fazer isso para parâmetros de array, objeto ou coleção.

Definindo uma descrição para um método da Web


Você pode definir uma descrição para um método da Web usando a propriedade Description do atributo WebMethod.
Esta descrição será exibida na página de ajuda. O código na Listagem 9-6 ilustra seu uso.

Listagem 9-6. Usando a propriedade Descrição

[WebMethod(Description = "Esta é a descrição do método da web")] public string HelloWorld()


{

return "Olá Mundo"; }

Adicionando métodos da Web para funcionalidade CRUD


Agora vamos adicionar alguns métodos da web que são mais significativos e realizar algumas operações de banco de dados.
Nesta seção, você adicionará cinco métodos da web ao serviço da web EmployeeManager. Esses métodos implementarão a
funcionalidade CRUD (Create, Read, Update and Delete) e executarão as respectivas operações de banco de dados na tabela
Employees do banco de dados Northwind.

247
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Este exemplo usará a abordagem primeiro código do Entity Framework (EF) para executar as operações do banco de dados.
Muitos aplicativos modernos usam Entity Framework para tarefas de banco de dados. Usamos EF aqui apenas para economizar tempo e
simplificar nosso código. Se desejar, você pode usar o código ADO.NET em vez do Entity Framework.

ÿ Observação Embora esta seção não use nenhum conceito avançado do EF, supõe-se que você tenha
familiaridade básica com o código do Entity Framework primeiro. Se não for esse o caso, você pode considerar
aprender o básico do código EF antes de continuar com esta seção.

Certifique-se de que o projeto que você acabou de criar esteja aberto no Solution Explorer. Observe que existe a pasta Models na
raiz do projeto. Usaremos esta pasta para armazenar os modelos do Entity Framework. Clique com o botão direito do mouse na pasta
Models e selecione Add ÿ New Item no menu de atalho. Na caixa de diálogo Add New Item, escolha a entrada ADO.NET Entity Data
Model (consulte a Figura 9-9).

Figura 9-9. Adicionando um modelo de dados EF

Especifique o nome do modelo como Northwind e clique no botão Adicionar. Isso abrirá um assistente, conforme mostrado na Figura
9-10.

248
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Figura 9-10. Gerando um contexto usando a abordagem code-first

Esta caixa de diálogo permite escolher como o contexto será criado. Selecione Code First na opção de banco de dados
e clique em Avançar.

A próxima caixa de diálogo (consulte a Figura 9-11) permite que você especifique as informações da string de conexão do banco de dados.
Vá em frente e insira esses detalhes de acordo com a configuração do banco de dados e as credenciais de segurança. Clique em Avançar
quando estiver pronto.

Figura 9-11. Especificando detalhes da string de conexão do banco de dados

249
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

A próxima etapa do assistente (consulte a Figura 9-12) permite que você selecione uma ou mais tabelas de banco de dados que deseja usar.
Em nosso exemplo, você precisa apenas de uma tabela - Empregados. Selecione essa tabela e conclua o assistente.

Figura 9-12. Selecionando a tabela Empregados

A conclusão bem-sucedida do assistente adicionará dois arquivos à pasta Models — Northwind.cs e Employee.cs. O primeiro
arquivo contém uma classe que herda de DbContext e o último arquivo contém a classe de entidade Employee.

Como estamos interessados apenas nas cinco colunas — EmployeeID, FirstName, LastName, HomePhone e Notes — remova todas as
outras propriedades da classe de entidade Employee. A Listagem 9-7 mostra a classe Employee final.

Listagem 9-7. Classe Final da Entidade Empregada

classe parcial pública Empregado {

public int EmployeeID { get; definir; } public string


Sobrenome { get; definir; } public string FirstName { get;
definir; } public string HomePhone { get; definir; } string
pública Notas { get; definir; }

A classe Employee contém cinco propriedades públicas que mapeiam para as respectivas colunas da classe Employees
mesa. Além disso, modifique a classe de contexto Northwind para corresponder à Listagem 9-8.

250
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Listagem 9-8. Classe de Contexto Northwind

classe parcial pública Northwind: DbContext {

public Northwind() :
base("nome=Northwind")

{}

public virtual DbSet<Empregado> Empregados { get; definir; }


}

O contexto Northwind contém um único DbSet chamado Employees. Isso conclui nosso modelo EF e estamos prontos
para adicionar a funcionalidade CRUD ao serviço da Web EmployeeManager.
Agora adicionaremos cinco métodos da web ao serviço da web EmployeeManager. Esses métodos da web são
SelectAll(), SelectByID(), Insert(), Update() e Delete(). Esses métodos da web fazem a respectiva operação. Vamos
discuti-los um por um.
A Listagem 9-9 mostra o método da web SelectAll().

Listagem 9-9. Método Web SelectAll()

[WebMethod]
public List<Empregado> SelectAll() {

usando (Northwind db = new Northwind()) {

return db.Employees.OrderBy(e => e.EmployeeID).ToList();


}
}

O método da web SelectAll() retorna uma lista de objetos Employee. Dentro, ele cria um objeto de contexto Northwind.
Em seguida, ele seleciona todos os objetos Employee usando o DbSet Employees, ordena-os em EmployeeID e então retorna
a lista para o chamador. Observe que SelectAll() — e todos os outros métodos — é decorado com o atributo [WebMethod].

Em seguida, adicione o método da web SelectByID(), conforme mostrado na Listagem 9-10.

Listagem 9-10. Método Web SelectByID()

[WebMethod]
public Employee SelectByID(int id) {

usando (Northwind db = new Northwind()) {

return db.Employees.Find(id);
}
}

O método da web SelectByID() aceita um parâmetro de ID inteiro que representa um EmployeeID e retorna um
objeto Employee correspondente a esse valor. Dentro, ele usa o método Find() no DbSet Employees para capturar o objeto
Employee com o ID correspondente.
Agora adicione o método da web Insert(), conforme mostrado na Listagem 9-11.

251
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Listagem 9-11. Método Web Insert()

[WebMethod]
public string Insert(Employee obj) {

usando (Northwind db = new Northwind()) {

db.Employees.Add(obj);
db.SaveChanges(); return
"Funcionário adicionado com sucesso!";
}
}

O método web Insert() aceita um objeto Employee contendo os detalhes de um funcionário a ser adicionado ao
banco de dados e retorna uma mensagem de sucesso. Dentro, ele adiciona o obj ao DbSet Employees usando o método
Add(). O método SaveChanges() é chamado para persistir as alterações no banco de dados. Por fim, uma mensagem de
sucesso é retornada ao chamador.
Agora é hora de adicionar o método da web Update(), conforme mostrado na Listagem 9-12.

Listagem 9-12. Método da Web Update()

[WebMethod]
public string Update(Employee emp) {

usando (Northwind db = new Northwind()) {

db.Entry(emp).State = EntityState.Modified;
db.SaveChanges(); return "Empregado modificado com
sucesso!";
}
}

O método web Update() aceita um objeto Employee que precisa ser atualizado no banco de dados. Dentro, muda a
propriedade State da entidade para Modified. Isso é feito usando o método Entry() do contexto e a enumeração EntityState.
Como antes, SaveChanges() é chamado para salvar as alterações no banco de dados físico. Por fim, uma mensagem de
sucesso é retornada ao chamador.
Conclua o serviço da web EmployeeManager adicionando o método da web Delete(), conforme mostrado na Listagem 9-13.

Listagem 9-13. Excluir () método da Web

[WebMethod]
public string Delete(int id) {

usando (Northwind db = new Northwind()) {

Funcionário emp = db.Employees.Find(id);


db.Employees.Remove(emp); db.SaveChanges();
return "Funcionário deletado com sucesso!";

}
}

252
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

O método da web Delete() aceita um EmployeeID de um funcionário a ser excluído. Dentro, ele encontra o funcionário usando o
método Find() do Employees DbSet. Em seguida, ele remove esse funcionário do DbSet Employees usando o método Remove(). SaveChanges()
é chamado para salvar as alterações no banco de dados físico.
Por fim, uma mensagem de sucesso é enviada ao chamador.
Isso conclui o serviço da Web EmployeeManager. Execute-o no navegador para exibir sua página de ajuda. Esse
vez, você verá os cinco métodos da Web recém-adicionados, conforme mostrado na Figura 9-13.

Figura 9-13. Página de ajuda do serviço da Web listando todos os métodos da web CRUD

Embora você não possa invocar todos eles usando a página de ajuda (porque alguns deles precisam do objeto Employee como
parâmetro), você pode tentar executar os métodos da web SelectAll(), SelectByID() e Delete().
Um exemplo de resultado após a execução de SelectByID() para EmployeeID de 1 é mostrado na Figura 9-14.

Figura 9-14. SelectByID() retornando um único funcionário

Como você pode ver, os dados estão sendo enviados pela rede como um documento XML.

253
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Criando um proxy para um serviço da Web


Para criar um proxy para o serviço Web usando o Visual Studio, você deve primeiro criar o aplicativo cliente porque o proxy
sempre reside lá. Embora qualquer tipo de aplicativo possa atuar como um cliente para o serviço Web, como exemplo, criaremos um
aplicativo Windows Forms que consome o serviço Web EmployeeManager.
Adicione um novo projeto Windows Forms à solução atual do Visual Studio. Clique com o botão direito do mouse na
pasta References deste projeto no Solution Explorer e escolha a opção de menu de atalho Add Service Reference (consulte a
Figura 9-15).

Figura 9-15. Adicionar opção de menu Referência de serviço

Fazer isso abrirá a caixa de diálogo Add Service Reference, conforme mostrado na Figura 9-16.

Figura 9-16. Caixa de diálogo Adicionar referência de serviço

Clicar no botão Descobrir listará todos os serviços (incluindo EmployeeManager.asmx) na seção Serviços. Selecionar
EmployeeManager.asmx revelará sua URL na área Endereço. Se seu serviço for externo à solução, você deve ter digitado
manualmente sua URL aqui.
A caixa de texto Namespace permite especificar um nome de namespace para a classe de proxy que está sendo gerada.
Digite EmployeeManagerReference nesta caixa de texto e clique no botão OK. Isso gerará um proxy de “estilo WCF” para seu serviço
da Web e o colocará na pasta Service References (consulte a Figura 9-17).

254
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Figura 9-17. EmployeeManagerReference é colocado dentro da pasta Service References

Existe uma maneira alternativa de gerar o proxy por meio da mesma opção Adicionar referências de serviço - usando
Referência Web. Vamos ver como isso funciona. Abra a mesma caixa de diálogo Adicionar referência de serviço. Há um botão Avançado
localizado na parte inferior da caixa de diálogo. Clicar nele abre a caixa de diálogo Configurações de referência de serviço, conforme mostrado na
Figura 9-18.

Figura 9-18. Diálogo de configurações de referência de serviço

255
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Há um botão Adicionar referência da Web na parte inferior desta caixa de diálogo (na seção Compatibilidade).
Clicar nele abre outra caixa de diálogo—Adicionar referência da Web (consulte a Figura 9-19).

Figura 9-19. Usando a caixa de diálogo Adicionar referência da Web para gerar o proxy

Você pode navegar até o serviço da Web EmployeeManager, fornecer algum nome de referência da Web (nome do namespace)
e clicar no botão Adicionar referência. As referências da Web são armazenadas na pasta Referências da Web. Deve-se notar que a
referência da web é uma forma mais antiga de gerar o proxy. Você deve usá-lo somente quando a compatibilidade com versões anteriores
for necessária.
Independentemente da forma como você gerou o proxy (uma referência de serviço ou uma referência da Web), você sempre
pode atualizar um proxy clicando com o botão direito do mouse no Solution Explorer e selecionando a opção de menu de atalho Atualizar
referência do serviço ou Atualizar referência da Web, respectivamente . Observe também que a geração de um proxy também adiciona
determinadas marcações no arquivo de configuração do aplicativo. Você não deve adulterar essa configuração, a menos que tenha certeza
sobre as alterações que está fazendo.

Criando um formulário que chama os métodos da Web Para demonstrar como chamar

métodos da Web, você precisará criar um formulário que permita adicionar, modificar e excluir funcionários da tabela Empregados. O
formulário deve se parecer com a Figura 9-20.

256
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Figura 9-20. Aplicativo que chama os métodos da web

O formulário exibe uma lista de EmployeeIDs existentes em uma lista suspensa. Se você selecionar um EmployeeID
na lista suspensa, seus detalhes, como nome, sobrenome, telefone residencial e anotações, serão exibidos nas
respectivas caixas de texto. Você pode então modificar os detalhes e clicar no botão Atualizar ou excluir esse funcionário
clicando no botão Excluir. Para adicionar um novo funcionário, você precisa preencher os detalhes nos respectivos campos
(EmployeeID é uma coluna de identidade e, portanto, será gerada automaticamente pelo banco de dados).
Clicar no botão Inserir salva os detalhes do novo funcionário no banco de dados. Uma mensagem de sucesso
retornada pelos métodos da web é exibida em um rótulo abaixo dos botões.
A lista suspensa é preenchida com uma lista de EmployeeIDs existentes no evento Load do formulário. Listagem 9-14
mostra como isso é feito.

Listagem 9-14. Preenchendo EmployeeIDs na lista suspensa

private void Form1_Load(remetente do objeto, EventArgs e) {

Proxy EmployeeManagerSoapClient = new EmployeeManagerSoapClient(); Empregado[]


dados = proxy.SelectAll(); foreach(Funcionário emp em dados) {

comboBox1.Items.Add(emp.EmployeeID);
}
}

O código cria uma instância da classe proxy — EmployeeManagerSoapClient. Esta classe (e


também a classe Employee no aplicativo cliente) vem de EmployeeWebServiceClient.
Namespace EmployeeManagerReference e, portanto, você precisa importar o namespace antes de usar esta classe.
Observe que discutimos duas maneiras de gerar o proxy. Aqui, o código está usando o proxy “estilo WCF” para chamar o
serviço da web. O nome da classe de proxy gerada é formado anexando SoapClient ao final da classe de serviço da web
original (EmployeeManager).

257
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Em seguida, o código chama o método SelectAll() no proxy. O método SelectAll() do serviço web
retorna uma Lista de objetos Employee. Essa lista é recebida como um array no código do cliente. Você pode ajustar esse
comportamento ao gerar o proxy (a caixa de diálogo Configurações avançadas). Aqui vamos nos ater às configurações
padrão, pois são suficientes para demonstrar o funcionamento do serviço da web. Um loop foreach percorre a matriz e
preenche a lista suspensa com todos os valores EmployeeID.
Ao selecionar um EmployeeID, seus detalhes são preenchidos nas demais caixas de texto. Isso é feito no evento
SelectedIndexChanged da lista suspensa (consulte a Listagem 9-15) invocando o método da web SelectByID().

Listagem 9-15. Chamando o método da Web SelectByID()

private void comboBox1_SelectedIndexChanged(remetente do objeto, EventArgs e) {

Proxy EmployeeManagerSoapClient = new EmployeeManagerSoapClient(); Dados do


funcionário = proxy.SelectByID((int)comboBox1.SelectedItem); textBox1.Text =
data.FirstName; textBox2.Text = data.LastName; textBox3.Text = data.HomePhone;
textBox4.Text = dados.Notas; label6.Text = "";

O código cria um objeto proxy como antes. Em seguida, ele chama o método SelectByID() no proxy passando o
EmployeeID selecionado. O método SelectByID() retorna um objeto Employee. Os detalhes do funcionário, como
Nome, Sobrenome, Telefone residencial e Notas, são preenchidos nas respectivas caixas de texto.
Para adicionar um novo funcionário, você precisa preencher todas as caixas de texto com os valores correspondentes e clicar no botão
Botão Inserir. O manipulador de eventos Click do botão Inserir chama o método da Web Insert(). Isso é mostrado na
Listagem 9-16.

Listagem 9-16. Chamando o método Web Insert()

private void button2_Click(remetente do objeto, EventArgs e) {

Proxy EmployeeManagerSoapClient = new EmployeeManagerSoapClient(); Dados do


funcionário = new Funcionário(); data.FirstName = textBox1.Text; data.LastName =
textBox2.Text; dados.HomePhone = textBox3.Text; dados.Notas = textBox4.Text;
label6.Text = proxy.Insert(dados);

O código cria um objeto proxy e um novo objeto Employee. Este objeto Employee representa um novo funcionário
a ser adicionado ao banco de dados. Propriedades como FirstName, LastName, HomePhone e Notes são definidas com os
valores inseridos nas respectivas caixas de texto. Por fim, o método Insert() é chamado no proxy para adicionar o funcionário.
O método Insert() retorna uma mensagem de sucesso (assumindo que não houve exceção) que é exibida em um Label.

Para modificar os detalhes de um funcionário, você precisa selecionar o EmployeeID na lista suspensa e, em seguida,
altere os valores das caixas de texto. Depois de adicionar as alterações, clicar no botão Atualizar tenta salvar os
detalhes modificados chamando o método da Web Update(). Isso é mostrado na Listagem 9-17.

258
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Listagem 9-17. Chamando o método da Web Update()

private void button1_Click(remetente do objeto, EventArgs e) {

Proxy EmployeeManagerSoapClient = new EmployeeManagerSoapClient(); Dados do


funcionário = new Funcionário(); data.EmployeeID = (int)comboBox1.SelectedItem;
data.FirstName = textBox1.Text; data.LastName = textBox2.Text; dados.HomePhone =
textBox3.Text; dados.Notas = textBox4.Text; label6.Text=proxy.Update(data);

O código é quase idêntico à operação de inserção discutida anteriormente. A única diferença é que ele escolhe um
EmployeeID na lista suspensa em vez da caixa de texto (já que um funcionário existente está sendo modificado). E o
método Update() é chamado no proxy após atribuir todas as propriedades do objeto Employee.

Remover um funcionário é simples. Tudo o que você precisa fazer é selecionar um EmployeeID na lista
suspensa e clicar no botão Excluir. O código que remove um funcionário é mostrado na Listagem 9-18.

Listagem 9-18. Chamando o método Web Delete()

private void button3_Click(remetente do objeto, EventArgs e) {

Proxy EmployeeManagerSoapClient = new EmployeeManagerSoapClient(); int id =


(int)comboBox1.SelectedItem; label6.Text = proxy.Delete(id);

O código simplesmente chama o método Delete() no proxy passando um EmployeeID a ser excluído.
Isso conclui o aplicativo cliente. Execute o aplicativo e teste se as operações CRUD estão funcionando conforme
o esperado. Para entender melhor o fluxo de execução, você pode definir pontos de interrupção em vários manipuladores
de eventos, bem como nos métodos da web.

Entendendo o SOAP
Na seção anterior, você aprendeu que SOAP é um protocolo leve baseado em XML que forma um dos blocos de construção
da infraestrutura de serviço da web. Você também aprendeu como as solicitações e respostas de serviços da Web são
codificadas no formato SOAP. Agora é hora de espiar dentro do SOAP com um pouco de detalhe. Dê uma olhada na Listagem 9-19.

Listagem 9-19. Solicitação SOAP

POST /WebServiceDemos/Service.asmx HTTP/1.1 Host:


localhost Tipo de conteúdo: text/xml; charset=utf-8 Content-
Length: length SOAPAction: "http://tempuri.org/HelloWorld"
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope
xmlns:xsi="http ://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"

259
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <HelloWorld


xmlns="http://tempuri.org/" /> </soap:Body> </ sabonete:Envelope>

Como você deve ter adivinhado, a Listagem 9-20 representa uma solicitação SOAP. Se você observar esta marcação cuidadosamente,
você descobrirá que a solicitação consiste em um envelope (<soap:Envelope>) e um corpo (<soap:Body>). Na verdade, uma solicitação
ou resposta SOAP pode conter quatro partes possíveis. Cada uma dessas partes é descrita na Tabela 9-1.

Tabela 9-1. Partes de uma mensagem SOAP

Papel Descrição
Envelope Envolve a solicitação ou resposta SOAP. É o elemento raiz da mensagem SOAP e é representado pela marca
de marcação <soap:Envelope>. Todas as mensagens SOAP devem ter um envelope.

Cabeçalho Partes opcionais de uma mensagem SOAP. Eles são usados para passar dados arbitrários de e para o serviço web e
seu cliente. Por exemplo, você pode usá-los para passar informações de autenticação para o serviço da web. Um
cabeçalho SOAP é representado pela marca de marcação <soap:Header>.

Corpo Uma parte obrigatória de uma mensagem SOAP. Ele inclui os dados reais de solicitação ou resposta em formato
XML. O corpo SOAP é representado pela marca de marcação <soap:Body>.

Falta Uma parte opcional de uma mensagem SOAP. Ele entra em cena sempre que há uma exceção de tempo de execução
no serviço da web. Os detalhes da exceção são incluídos na tag <soap:Fault> e enviados de volta ao aplicativo cliente.

Usando cabeçalhos SOAP Nesta seção,

você modificará o serviço Web EmployeeManager e o aplicativo cliente para usar cabeçalhos SOAP. Vamos supor que, para chamar
com sucesso o serviço web EmployeeManager, o cliente deve enviar uma chave secreta para o serviço web. Com cada chamada para
um método da web, o serviço da web precisa verificar a existência de uma chave válida.

Agora a questão é: como você passaria a chave secreta do cliente para o serviço? Uma opção seria incluí-lo na assinatura de
todos os métodos da web. Mas isso complicará os métodos da web e dificultará futuras alterações. Uma alternativa melhor é passar a chave
secreta por meio de um cabeçalho SOAP personalizado. O serviço da web autenticará uma chamada com base neste cabeçalho SOAP e
retornará os dados solicitados apenas se a chave for validada.

Para iniciar a modificação, adicione uma classe chamada EmployeeManagerHeader na pasta Serviços (você pode colocá-la em
qualquer outra pasta se desejar). Essa classe envolve a chave secreta e consiste em apenas uma única propriedade: ClientKey. A
Listagem 9-20 mostra a classe User concluída.

Listagem 9-20. Criando uma classe que herda da classe SoapHeader

public class EmployeeManagerHeader:SoapHeader {

string pública ClientKey { get; definir; }


}

260
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Observe que a classe EmployeeManagerHeader herda da classe base SoapHeader, que reside no namespace
System.Web.Services.Protocols e representa um cabeçalho SOAP básico. Todas as classes de cabeçalho SOAP
personalizadas devem herdar direta ou indiretamente da classe SoapHeader. Certifique-se de ter importado o
namespace System.Web.Services.Protocols antes de criar a classe User.
A classe User simplesmente contém uma propriedade string — ClientKey. Depois de criar a
classe EmployeeManagerHeader, modifique o serviço da Web EmployeeManager conforme mostrado na Listagem 9-21.

Listagem 9-21. Usando um cabeçalho SOAP

public class EmployeeManager : System.Web.Services.WebService {

público EmployeeManagerHeader Cabeçalho;

[WebMethod]
[SoapHeader("Header", Direction = SoapHeaderDirection.In)] public
List<Funcionário> SelectAll() {

if (Cabeçalho==nulo)
{
throw new SoapHeaderException("O cabeçalho SOAP não foi encontrado!", SoapException.
ClienteFaultCode);
}

if(Header.ClientKey != "KEY001") {

throw new SoapException("Chave de cliente inválida!", SoapException.ClientFaultCode);


}

usando (Northwind db = new Northwind()) {

return db.Employees.OrderBy(e => e.EmployeeID).ToList();


}
}
....
....,
}

Examine a classe de serviço da web com cuidado. No topo declara uma variável do tipo
EmployeeManagerHeader. O método da web SelectAll() agora está decorado com um atributo adicional chamado
[SoapHeader]. É assim que você especifica informações sobre um cabeçalho SOAP.
O atributo [SoapHeader] especifica um parâmetro e uma propriedade. O primeiro parâmetro
especifica o nome do membro do cabeçalho SOAP que queremos usar. Em nosso exemplo, o nome da
variável EmployeeManagerHeader é Header e, portanto, é isso que passamos para o método web. Este
membro deve estar disponível publicamente na classe de serviço da web. A propriedade Direction indica a
direção do SoapHeader e é do tipo de enumeração SoapHeaderDirection. Os valores possíveis da enumeração
SoapHeaderDirection são os seguintes:
• In: A direção de In indica que o cabeçalho SOAP é passado do cliente para o serviço web.

•Out: A direção de Out indica que o cabeçalho SOAP é passado do


serviço web para o cliente.

261
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

•InOut: A direção de InOut indica que o cabeçalho SOAP é passado de e para


o serviço web e seu cliente.

• Falha: A direção da Falha indica que um cabeçalho SOAP deve ser enviado ao cliente
quando o método da web lança uma exceção.

Dentro do método web SelectAll(), verificamos se o cabeçalho SOAP é nulo. Se sim, isso indica que
a chave do cliente não foi enviada e, portanto, o código gera uma SoapHeaderException. A classe SoapHeaderException
é usada para representar um erro no cabeçalho SOAP. O primeiro parâmetro do construtor SoapHeaderException é a
mensagem de erro e o segundo parâmetro é o código de falha SOAP para a chamada do cliente.
O código então verifica a propriedade ClientKey do cabeçalho SOAP. Se a chave do cliente estiver incorreta,
um SoapException é levantado. A classe SoapException é usada para representar um erro no processamento
da solicitação SOAP. O construtor de SoapException usa os mesmos dois parâmetros da classe
SoapHeaderException.

ÿ Observação Para simplificar, o código usa um valor de string embutido em código para uma chave de cliente. Em um caso mais

realista, você verificaria a chave em algum armazenamento de dados. E a chave pode ser criptografada por motivos de segurança.

Além disso, adicionamos aqui o [SoapHeader] atribuído ao método da web SelectAll() . Repita o mesmo processo para todos os

outros métodos web do serviço web EmployeeManager .

Para consumir com êxito o serviço Web EmployeeManager que você acabou de modificar, você também precisa
modificar o aplicativo cliente. A Listagem 9-22 mostra como o cabeçalho SOAP exigido pelo método da web SelectAll()
pode ser transmitido do aplicativo cliente.

Listagem 9-22. Passando um cabeçalho SOAP do aplicativo cliente

private void Form2_Load(remetente do objeto, EventArgs e) {

Proxy EmployeeManagerSoapClient = new EmployeeManagerSoapClient();

Cabeçalho EmployeeManagerHeader = new EmployeeManagerHeader();


header.ClientKey = "KEY001";

tentar

Empregado[] dados = proxy.SelectAll(cabeçalho);


foreach (funcionário emp em dados) {

comboBox1.Items.Add(emp.EmployeeID);
}

} catch(Exceção ex) {

MessageBox.Show(ex.Message);
}
}

Antes de escrever este código, certifique-se de atualizar o proxy clicando com o botão direito nele e
selecionando a opção Atualizar referência do serviço. Isso é necessário porque agora alteramos o serviço da Web
para usar cabeçalhos SOAP. E o proxy deve conhecer esses detalhes.

262
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Observe o código marcado em negrito. A primeira linha em negrito cria um objeto de


EmployeeManagerHeader e define sua propriedade ClientKey. Essa classe é criada no lado do cliente durante a geração do proxy
e colocada no mesmo namespace do proxy.
A segunda linha em negrito chama o método SelectAll() no proxy. Desta vez, o objeto do cabeçalho SOAP — header — é
passado como parâmetro do método. Desta forma, o método web SelectAll() irá recebê-lo do cliente. Embora aqui discutamos
apenas o método SelectAll(), você deve modificar todos os outros manipuladores de eventos no aplicativo cliente para usar o
cabeçalho SOAP.
Para testar o aplicativo cliente, execute-o e verifique se as operações CRUD podem ser executadas como antes.
Agora, interrompa o aplicativo e defina explicitamente o objeto EmployeeManagerHeader como nulo. Em seguida, execute
o aplicativo novamente. Você deve obter uma caixa de mensagem, conforme mostrado na Figura 9-21, que informa sobre
o SoapHeaderException.

Figura 9-21. Capturando uma SoapHeaderException

Pare o aplicativo e reverta o código que define o cabeçalho como nulo. Defina a propriedade ClientKey como alguma chave
inválida. Execute o aplicativo novamente. Desta vez, a caixa de mensagem (consulte a Figura 9.22) deve informá-lo sobre o
lançamento da SoapException.

Figura 9-22. Capturando SoapException

ÿ Observação No exemplo que acabamos de discutir, presumimos que você está usando uma referência de serviço. Se
você estiver usando uma referência da Web, o cabeçalho SOAP será transmitido de maneira um pouco diferente. Não
discutiremos essas diferenças aqui, mas você pode descobrir isso facilmente com a ajuda do Visual Studio IntelliSense.

263
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Personalizando a Serialização XML


No capítulo anterior foi mencionado que os web services utilizam o XmlSerializer para serializar os dados XML envolvidos
na comunicação do web service. Isso também significa que você pode usar atributos como [XmlRoot] e [XmlElement]
para personalizar o resultado serializado. Embora não entremos em muitos detalhes sobre esse processo de
customização, uma simples ilustração lhe dará uma ideia.
Considere a classe Employee mostrada na Listagem 9-23.

Listagem 9-23. Customizando a Serialização XML em Web Services

[XmlRoot(ElementName ="FuncionárioRoot")]
classe parcial pública Funcionário {

[XmlElement(ElementName ="EmpCode")] public


int EmployeeID { get; definir; }

[XmlElement(ElementName = "LName")] public


string LastName { get; definir; }

[XmlElement(ElementName = "FName")] public


string FirstName { get; definir; }

[XmlIgnore]
public string HomePhone { get; definir; }

[XmlElement(ElementName = "Comentários")]
public string Notas { get; definir; }
}

É a mesma classe Employee, mas desta vez foram adicionados atributos como [XmlRoot] e [XmlElement] para
personalizar os nomes dos elementos XML. A propriedade HomePhone é decorada com [XmlIgnore] para ignorá-la do
processo de serialização.
Se você invocar o método da web SelectByID() usando a página de ajuda, deverá ver algo semelhante à Figura 9-23.

Figura 9-23. Personalizando nomes de elementos para serialização

264
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Como você pode ver, o objeto Employee serializado agora usa nomes de elementos como <EmployeeRoot>,
<FName>, <LName> e <Remarks>. Não há entrada HomePhone porque nós a ignoramos usando [XmlIgnore].

Compreendendo o documento WSDL


Ao desenvolver seu primeiro serviço da Web neste capítulo, você aprendeu que WSDL é um vocabulário XML que
descreve o serviço da Web em termos de métodos da Web expostos, seus parâmetros, tipos de dados e valores de retorno.
Embora você raramente modifique ou construa WSDL sozinho (pelo menos quando estiver usando o Visual Studio),
é útil entender a estrutura interna do documento WSDL. Dessa forma, sua compreensão dos metadados do serviço
da Web será ampliada. Você também achará útil o conhecimento de WSDL ao aprender sobre os serviços do
Windows Communication Foundation, abordados no Capítulo 12.
Vamos ver o WSDL de um serviço web Hello World simples que criamos inicialmente neste capítulo. Usaremos
este serviço da Web apenas como uma amostra. A discussão a seguir é aplicável a qualquer outro documento WSDL.
O WSDL do serviço web EmployeeManager anterior, conforme mostrado em um navegador, é mostrado na Figura 9-24.

Figura 9-24. WSDL do serviço web EmployeeManager

265
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Se você observar essa marcação WSDL, poderá identificar seis partes principais do documento. Estas seis partes são
os elementos principais de qualquer documento WSDL e estão listados na Tabela 9-2.

Tabela 9-2. Partes do WSDL

Papel Descrição
tipos O elemento <wsdl:types> inclui todas as definições de tipo do serviço da web.

mensagem Os dados XML que estão sendo transportados entre o serviço da Web e seu cliente. O elemento
<wsdl:message> representa esta mensagem.

portType A seção <wsdl:portType> define um grupo de operações (métodos da web) expostos pelo serviço da web.
Você pode pensar nisso como uma interface que contém uma série de elementos <wsdl:operation>.

vinculativo O protocolo e formato usado pelo portType. Ele é representado pela marca de marcação <wsdl:binding>.

porta Um endpoint de uma comunicação de serviço da web. Ele é representado pela marca de marcação
<wsdl:port>.

serviço A coleção de uma ou mais portas. Ele é representado pela marca de marcação <wsdl:service>.

Vejamos cada uma dessas partes com mais detalhes.

As mensagens
Você aprendeu anteriormente que a comunicação de serviço da Web funciona com base em um modelo de solicitação e resposta.
Uma solicitação de serviço da Web, bem como uma resposta, consiste em dados SOAP. Esses dados SOAP são chamados de
mensagem SOAP. Cada método da web tem uma mensagem que representa uma solicitação para o método da web e uma mensagem
que representa a resposta desse método da web. Assim, nosso método web SelectAll() terá duas mensagens:

•O nome da mensagem de solicitação está no formato XXXXSoapIn, onde XXXX é o nome


do método web.

• Da mesma forma, o nome da mensagem de resposta está no formato XXXXSoapOut, em que XXXX é o
nome do método da web.

Os elementos de mensagem WSDL fornecem uma lista consolidada de todas as mensagens expostas pelo serviço da web.
Os nomes de mensagem fornecidos por esta lista são usados em qualquer outro lugar no documento WSDL.

As definições de tipo
Cada mensagem em um serviço da Web possui uma estrutura ou esquema específico. Esse esquema é especificado pelo elemento
de tipos do documento WSDL. Se você observar a seção de tipos no WSDL mencionado anteriormente, descobrirá que ela especifica
um esquema para todas as mensagens. Você também notará que o tipo de dados dos parâmetros e valores de retorno são especificados
aqui. Esse esquema corresponde de perto ao esquema XSD que você aprendeu nos capítulos anteriores.

266
Machine Translated by Google

Capítulo 9 ÿ XML em Web Services

Os tipos de portas
Um serviço da Web consiste em uma ou mais operações. Em termos simples, uma operação é análoga a uma função ou método.
Cada operação possui uma mensagem de entrada (solicitação) e uma mensagem de saída (resposta). Todas as operações de um
serviço da Web estão listadas na seção portType. O nome da porta está no formato XXXXSoap, em que XXXX é o nome da classe
de serviço da web. O serviço web EmployeeManager tem cinco operações.
Cada operação consiste em duas mensagens — mensagem de entrada e mensagem de saída. Lembre-se de que as mensagens são
definidas na seção de mensagens.

A ligação
Uma ligação especifica o formato da mensagem e o protocolo para cada tipo de porta. Por exemplo, em nosso serviço da Web,
há uma ligação definida para o tipo de porta EmployeeManagerSoap. A ligação entre um tipo de ligação e porta é o atributo type do
elemento <wsdl:binding>. O nome da ligação está no formato XXXXSoap, em que XXXX é o nome da classe de serviço da web.

O serviço
Um serviço é um conjunto de portas e ligações. Uma porta de serviço da web é um terminal lógico para um serviço da web. Um
serviço tem o mesmo nome que a classe de serviço da web. Em nosso exemplo, o elemento service define uma porta chamada
EmployeeManagerSoap e a vincula à ligação EmployeeManagerSoap.

Um resumo do WSDL
Resumindo o que discutimos:

•Um serviço da Web consiste em uma ou mais operações.

•Cada operação normalmente tem uma mensagem de solicitação e uma mensagem de resposta.

•Cada mensagem é listada na seção de mensagens do WSDL.

•O esquema de todas as mensagens é definido pela seção de tipos do WSDL.

•Todas as operações expostas por um serviço da web são listadas na seção portType de
o WSDL.

•Para cada tipo de porta, um formato de transporte e protocolo precisam ser especificados. Isso é chamado
de vinculativo.

•A ligação para um tipo de porta é especificada pela seção de ligação do WSDL.

•A seção de serviço do WSDL define um terminal para o serviço da web chamado porta.

•Uma porta tem um vínculo específico associado a ela.

Resumo
Neste capítulo, você aprendeu sobre um dos recursos poderosos do .NET Framework: serviços da Web. Os serviços da Web são
um conjunto programável de APIs que podem ser chamados em uma rede usando padrões da indústria, como XML, SOAP e HTTP.
Os serviços da Web podem ser muito benéficos em áreas como integração de aplicativos, comunicação entre plataformas e
comunicação distribuída pela Internet.
Você aprendeu a criar serviços da web, um proxy para o serviço da web e um cliente que consome a web
serviço. Por fim, você deu uma espiada na estrutura interna do SOAP e WSDL.

267
Machine Translated by Google

CAPÍTULO 10

XML no WCF e API da Web

O Capítulo 9 apresentou os serviços da Web e você aprendeu a desenvolver aplicativos baseados em serviços. Os
serviços da Web são baseados em determinados padrões e protocolos, como SOAP, XML e WSDL. Eles são ótimos
quando se trata de desenvolver serviços baseados em padrões que são acessados na internet. No entanto, às vezes
suas necessidades são diferentes. Por exemplo, você pode querer desenvolver serviços para um aplicativo de intranet e
usar o formato binário sobre TCP por motivos de desempenho. Ou você deseja usar algum mecanismo de comunicação
altamente seguro que usa algumas estratégias de criptografia personalizadas. Os serviços da Web podem não atender a sua
finalidade nesses casos.
Reconhecendo a necessidade de preencher a lacuna entre várias tecnologias de componentes, a Microsoft desenvolveu
o que é conhecido como Windows Communication Foundation (WCF). O WCF é uma estrutura genérica que fornece
um modelo unificado para o desenvolvimento de componentes de software orientados a serviços. No WCF, você pode empregar
o mesmo software independentemente de estar usando-o na Internet ou em uma intranet. Você pode projetar e desenvolver
seu software inicialmente para redes TCP e depois usá-lo em HTTP. Isso pode ser alcançado sem alterações no código-fonte.
O que é mais interessante para nós é o fato de que, por padrão, o WCF usa XML (SOAP) como formato de transferência de
dados. Isso significa que os dados enviados entre o cliente e o servidor estão no formato XML (SOAP). Você também pode
personalizar como seus objetos são serializados na rede.
O WCF é uma estrutura de propósito geral para o desenvolvimento de serviços. Requer boa quantidade de configuração.
E muitos desenvolvedores acham essa configuração tediosa. Especialmente para aplicativos da Web em que o canal de
comunicação subjacente é HTTP e a transferência de dados está em algum formato baseado em texto (XML, SOAP, JSON
e assim por diante), o amplo conjunto de recursos e a configuração do WCF são desnecessários. Foi assim que a API da
Web surgiu. A API da Web é uma estrutura para criar serviços RESTful. A estrutura da API da Web é desenvolvida
especificamente tendo em mente os aplicativos da Web e o HTTP e oferece um modelo de programação simples para os
desenvolvedores.
Este capítulo apresenta os seguintes tópicos:

• Termos comuns usados em relação ao WCF

•Criando e consumindo um serviço WCF

•O papel do XML nos serviços WCF

•Criando e consumindo um serviço usando API da Web

•Configurar a API da Web para usar o formato XML

ÿ Observação Este capítulo não pretende ensinar os fundamentos do WCF e da API da Web. Ele se concentra mais em como o

XML está relacionado a essas estruturas de desenvolvimento de serviço. Se você não estiver familiarizado com o WCF e a API da

Web, considere aprender seus fundamentos na documentação oficial da Microsoft.

© Bipin Joshi 2017 269


B. Joshi, Beginning XML with C# 7, https://doi.org/10.1007/978-1-4842-3105-0_10
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Serviços Baseados em Operações vs. Serviços Baseados em Recursos


De um modo geral, um serviço pode ser categorizado como um serviço baseado em operação ou um serviço baseado em
recurso. O que isso significa? Considere o serviço web EmployeeManager que você desenvolveu no Capítulo 9.
Você basicamente adicionou métodos ao serviço da Web conforme sua necessidade. Nesse exemplo, você adicionou cinco
métodos para implementar operações CRUD. Mas você poderia ter adicionado quantos quiser, dependendo da sua necessidade.
Posteriormente, você invocou esses métodos chamando-os no proxy. Assim, você invocou explicitamente as operações de um
serviço. Este foi um exemplo de serviço baseado em operações.
Os serviços baseados em recursos são orientados a recursos. Um recurso pode ser qualquer coisa, digamos, objetos de negócios, um
banco de dados ou um arquivo. Um serviço baseado em recursos permite que você execute ações em um recurso, como
adicionar um recurso, atualizar um recurso, remover um recurso e buscar um recurso. Assim, um serviço baseado em recurso
limita-se a um recurso. Isso é diferente do serviço baseado em operações, no qual o serviço é gratuito para fazer qualquer coisa
de sua escolha. Os serviços RESTful são serviços baseados em recursos (você aprenderá mais sobre REST em seções
posteriores).
Cada abordagem de desenvolvimento de um serviço tem seus próprios prós e contras. Não entraremos nesses detalhes
neste livro. Basta dizer que o WCF permite que você desenvolva ambos os tipos de serviços, enquanto a API da Web permite que
você crie serviços RESTful. Nas seções posteriores deste capítulo, você desenvolverá os dois tipos de serviços. Você também
observará onde o XML se encaixa em todo esse quadro.

Entendendo o Vocabulário do WCF


No Capítulo 9, você aprendeu sobre a Web Services Description Language (WSDL). WSDL usa termos como porta, mensagem,
tipo de serviço, ligação e serviço. O vocabulário do WCF é semelhante ao WSDL com algumas diferenças.
Nesta seção, você aprenderá o vocabulário do WCF:
• Modelo de serviço: o modelo fornecido pelo WCF para criar componentes de software.

• Camada de canal: a parte do WCF que lida com a programação de rede de baixo nível.
As classes da camada de canal são usadas por classes de alto nível do WCF.
• Serviço: Um pedaço de software que responde à comunicação através de uma rede. Um
serviço tem um ou mais endpoints. A comunicação com o serviço é redirecionada para um desses
endpoints.

• Endpoint: onde a solicitação real de um serviço é redirecionada. Um endpoint consiste em um endereço,


uma ligação e um contrato.

• Endereço: Nada além do local exclusivo do serviço subjacente em uma rede.


Os clientes usam esse endereço para falar com o serviço. Um endereço assume a forma de
um localizador uniforme de recursos (URL) — por exemplo, http://localhost:8000/ MyService.

• Binding: Um endereço é apenas uma URL onde o serviço pode ser localizado. No entanto, isso não é
suficiente. Você também precisa conhecer o protocolo usado para comunicação, como TCP ou
HTTP. Isso é especificado com a ajuda de uma ligação. A ligação especifica o protocolo para
codificar a solicitação e a resposta, bem como o protocolo para transportá-los pela rede.

• Contrato: Um conjunto de operações que são expostas pelo serviço. Em outras palavras, um
contrato é um conjunto de operações disponíveis em um determinado endpoint. No nível do
código, um contrato é definido com a ajuda de uma interface.
• Tipo de serviço: Uma classe que implementa um contrato.

270
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Criando e consumindo um serviço WCF


Para criar e consumir serviços WCF, você precisa essencialmente desenvolver três softwares:

• Um ou mais tipos de serviço

• Um host que publica os serviços expostos pelos tipos de serviço em uma rede

• Um aplicativo cliente que consome a funcionalidade exposta pelos tipos de serviço

Toda a funcionalidade principal do WCF está disponível no assembly System.ServiceModel.dll.


Os namespaces System.ServiceModel contêm muitas classes e atributos relacionados ao WCF. Portanto, você deve fazer
referência a esse assembly e importar o namespace System.ServiceModel como e quando necessário. Nas próximas seções, você
aprenderá como desenvolver cada uma das três partes.

Criando o serviço
A criação de um serviço WCF requer as seguintes etapas:

1. Defina um contrato para o serviço.

2. Implemente o contrato de serviço.

3. Defina as estruturas de dados (se houver) para transportar dados do serviço para o cliente.

Agora que você tem uma breve ideia sobre o que está envolvido na criação de serviços WCF, vamos criar um serviço.
Comece criando um novo projeto do tipo WCF Service Library denominado EmployeeWCFService (consulte a Figura 10-1).

Figura 10-1. Criando uma nova biblioteca de serviço WCF

Em seguida, adicione uma interface ao projeto usando a caixa de diálogo Adicionar novo item e nomeie-a
como IEmployeeManager.

271
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

ÿ Observação Quando você cria um novo projeto do tipo WCF Service Library, o Visual Studio adiciona um
modelo para criar um novo serviço por padrão. Além disso, a caixa de diálogo Adicionar novo item do Visual Studio
também fornece um modelo para adicionar um novo serviço WCF. No entanto, para facilitar o entendimento, não usarei os
modelos de código padrão.

Importe o namespace System.ServiceModel na parte superior do arquivo de interface e adicione o código mostrado em
Listagem 10-1 na interface.

Listagem 10-1. Criando a interface IEmployeeManager

usando Sistema;
usando System.Collections.Generic; usando
System.Text; usando System.ServiceModel;
usando System.Data;

namespace EmployeeWCFService {

[ServiceContract]
interface pública IEmployeeManager {

[Contrato de Operação]
List<ContratoDadosFuncionário> SelectAll();

[Contrato de Operação]
EmployeeDataContract SelectByID(int id);

[OperationContract]
string Update(EmployeeDataContract emp); }

A interface IEmployeeManager atua como um contrato WCF. A interface IEmployeeManager define três
métodos: SelectAll(), SelectByID() e Update(). O método SelectAll() quando implementado retornará uma lista de todos
os funcionários da tabela Employees do banco de dados Northwind. O método SelectByID() quando implementado
retornará detalhes de um funcionário específico. A classe EmployeeDataContract é definida e explicada posteriormente
nesta seção. Observe duas coisas sobre o contrato WCF IEmployeeManager:

•A interface deve ser decorada com o atributo [ServiceContract]. Este atributo indica que a
interface decorada por ele é um contrato de serviço WCF.

•Cada assinatura de método na interface deve ser marcada com o atributo


[OperationContract], que indica que o método decorado por ele será exposto como parte do
serviço. Os métodos são referidos como operações no WCF.

Depois de definir um contrato, você precisa implementá-lo. Você faz isso criando uma classe que implementa a
interface do contrato. Você não precisa fazer nada de especial com o tipo de serviço além de implementar o contrato de
serviço.

272
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Em nosso exemplo, a classe EmployeeDataContract é usada para transportar os detalhes de um funcionário do


serviço para o cliente. Este é o contrato de dados do serviço. Portanto, adicione a classe EmployeeDataContract ao projeto.
Adicione também uma referência ao assembly System.Runtime.Serialization. Em seguida, importe o arquivo System.
ServiceModel e System.Runtime.Serialization na parte superior da classe EmployeeDataContract. A Listagem 10-2 mostra o
código completo que compõe a classe Employee.

Listagem 10-2. A classe EmployeeDataContract

usando Sistema;
usando System.Collections.Generic; usando
System.Text; usando System.ServiceModel;
usando System.Runtime.Serialization;
usando System.Data;

namespace EmployeeWCFService {

[DataContract]
public class EmployeeDataContract {

[DataMember]
public int EmployeeID { get; definir; }
[DataMember]
public string LastName { get; definir; }
[DataMember]
public string FirstName { get; definir; }
[DataMember]
public string HomePhone { get; definir; }
[DataMember]
string pública Notas { get; definir; }
}
}

A classe EmployeeDataContract consiste em cinco propriedades públicas: EmployeeID, FirstName, LastName,


HomePhone e Notas. Observe como a classe é marcada com o atributo [DataContract] e as propriedades individuais com o
atributo [DataMember]. Dessa forma, a classe e suas informações de estado são serializadas para o cliente. Sempre que
quiser retornar objetos personalizados de seus métodos de serviço, você precisa marcar essas classes com o atributo
[DataContract]. Além disso, cada membro da classe que será transferido para o cliente deve ser marcado com o atributo
[DataMember]. Você pode notar que o uso do atributo [DataContract] é semelhante ao atributo [Serializable].

ÿ Nota Você achará esse código familiar porque usou os atributos [DataContract] e [DataMember] no Capítulo
8 enquanto aprendia a serialização XML.

O EmployeeDataContract simplesmente define o contrato de dados necessário para o serviço EmployeeManager.


No entanto, você precisa escrever um código que busque os dados do banco de dados Northwind. Você pode, é claro,
usar código ADO.NET simples para realizar essa tarefa. Mas, para nos poupar algum tempo e código, usaremos primeiro
o código do Entity Framework para essa finalidade. Para gerar o modelo e o contexto do EF, adicione uma nova pasta à raiz
do projeto chamada Models, clique com o botão direito nela e abra a caixa de diálogo Add New Item. Em seguida, na seção
Data, selecione ADO.NET Entity Data Model (Figura 10-2).

273
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Figura 10-2. Adicionando modelo de dados de entidade ADO.NET

Nomeie e modele Northwind e clique em Adicionar. Isso iniciará um assistente. Escolha o código primeiro do banco de dados
(Figura 10-3) e simplesmente siga o assistente para gerar o contexto e as classes de entidade.

Figura 10-3. Gerando um modelo usando a abordagem code-first

A execução bem-sucedida do assistente adicionará os arquivos Northwind.cs e Employee.cs à pasta Models. O primeiro arquivo
contém a classe de contexto, enquanto o último arquivo contém a classe de entidade Employee. Como essas classes são geradas
automaticamente para você, elas não são discutidas aqui.
A última etapa na criação desse serviço é implementar o contrato de serviço em uma classe chamada
EmployeeManager. A Listagem 10-3 mostra o código completo da classe EmployeeService.

274
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Listagem 10-3. A classe EmployeeService

public class EmployeeManager:IEmployeeManager {

public List<EmployeeDataContract> SelectAll() {

usando (Northwind db = new Northwind()) {

var query = from e in db.Employees


orderby e.EmployeeID ascendente
select new EmployeeDataContract() {

EmployeeID = e.EmployeeID,
FirstName = e.FirstName,
LastName = e.LastName,
HomePhone = e.HomePhone,
Notes = e.Notes
,
return query.ToList();
}

} public EmployeeDataContract SelectByID(int id) {

usando (Northwind db = new Northwind()) {

var query = from e in db.Employees


where e.EmployeeID == id
select new EmployeeDataContract() {

EmployeeID = e.EmployeeID,
FirstName = e.FirstName,
LastName = e.LastName,
HomePhone = e.HomePhone,
Notes = e.Notes
};
return query.SingleOrDefault();
}
}

public string Update(EmployeeDataContract emp) {

usando (Northwind db = new Northwind()) {

Funcionário existente = db.Employees.Find(emp.EmployeeID);


existindo.FirstName = emp.FirstName; existindo.LastName =
emp.LastName; existente.HomePhone = emp.HomePhone;
existentes.Notas = emp.Notas; db.SaveChanges(); return
"Empregado modificado com sucesso!";

}}}

275
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

A classe EmployeeManager implementa a interface IEmployeeManager. O método SelectAll()


preenche uma lista com todos os funcionários e a devolve ao chamador. Observe que o método SelectAll() precisa retornar
objetos EmployeeDataContract. Isso é feito mapeando dados dos objetos Employee para objetos EmployeeDataContract usando
a consulta LINQ to Entities.
O método SelectByID() aceita um ID de funcionário. Em seguida, ele busca os detalhes desse funcionário no
banco de dados usando a consulta LINQ to Entities. O objeto EmployeeDataContract é então retornado ao chamador.
O método Update() aceita um objeto EmployeeDataContract. Dentro, ele encontra um funcionário existente com base em seu
EmployeeID. As propriedades do funcionário existente são modificadas usando os valores do EmployeeDataContract. O método
SaveChanges() salva as alterações no banco de dados. Uma mensagem é enviada de volta ao chamador indicando que o funcionário foi
modificado com sucesso.
Compile o projeto EmployeeWCFService para produzir o assembly EmployeeWCFService.dll.

Hospedando o serviço Agora que você

criou o tipo de serviço, é hora de pensar em hospedá-lo. Hospedar o serviço o tornará disponível em uma rede para que os aplicativos
clientes possam consumi-lo. Para hospedar o serviço, você tem várias opções. Alguns deles incluem:

•Crie um aplicativo de console e use-o como host.

• Hospede o serviço WCF no IIS.

• Hospede o serviço WCF em um aplicativo de serviço do Windows.

Neste exemplo, usamos um aplicativo de console como host. Nas seções posteriores, você aprenderá a usar o IIS para hospedar
Serviços WCF.

Adicione um novo projeto do tipo aplicativo de console na mesma solução do serviço e nomeie-o
EmployeeWCFServiceHost. Consulte a Figura 10-4.

Figura 10-4. Criando um aplicativo de console para hospedar o serviço WCF

Adicione uma referência aos assemblies System.ServiceModel e EmployeeWCFService. O recém-criado


projeto de aplicativo de console já conterá um arquivo App.config. Abra o arquivo App.config no Visual Studio Editor e insira a
marcação mostrada na Listagem 10-4 dentro do elemento raiz <configuration>.

276
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Listagem 10-4. Configurando o serviço

<system.serviceModel>
<serviços>
<service name="EmployeeWCFService.EmployeeManager"
behaviorConfiguration="EmployeeWCFServiceBehavior">
<endpoint address="EmployeeManager"
binding="basicHttpBinding"
contract="EmployeeWCFService.IEmployeeManager"> </
endpoint> </service>

</services>
<behaviors>
<serviceBehaviors>
<behavior name="EmployeeWCFServiceBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="true" /> </behavior>
</serviceBehaviors> </behaviors> </ system.serviceModel>

A seção <system.serviceModel> do arquivo de configuração contém todas as definições de configuração


relacionados à hospedagem de serviços WCF. Existem duas subseções: <services> e <behaviors>.
O primeiro contém informações de configuração sobre um ou mais serviços em termos de nome,
endpoints e endereços. O último contém informações de configuração sobre o comportamento exibido pelos
serviços definidos na seção <services>. Um comportamento é uma classe que modifica ou estende o serviço ou cliente
funcionalidade. Ele também pode modificar as configurações do canal.
Cada serviço da seção <services> é configurado por meio de uma seção <service>:

•O atributo name especifica o nome totalmente qualificado do tipo de serviço


(EmployeeWCFService.EmployeeManager em nosso caso).

•O atributo behaviorConfiguration aponta para o nome do comportamento de serviço como


definido na seção <serviceBehaviors>.

•O elemento <endpoint> detalha um ou mais endpoints onde o serviço está


disponível.

•O atributo address do elemento <endpoint> especifica o endereço do serviço.

•O atributo binding especifica o protocolo a ser usado para comunicação.


As duas ligações comumente usadas são net.tcp para TCP e basicHttpBinding para
HTTP. Existem várias outras associações fornecidas, como netMsmqBinding,
netNamedPipeBinding e assim por diante.
• Por fim, o atributo contract especifica o nome totalmente qualificado da interface que fornece
o contrato de serviço (IEmployeeManager em nosso caso).

277
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Em nosso exemplo, criamos um endpoint para comunicação baseada em HTTP.


A seção <serviceBehaviors> contém um ou mais elementos <behavior>:

•O atributo name do elemento <behavior> especifica o nome desse comportamento.


Esse nome é usado no atributo behaviorConfiguration do elemento <service>.

•O elemento <serviceMetadata> indica que os metadados do serviço podem ser recuperados


usando uma solicitação HTTP GET. Você achará esse recurso análogo aos serviços da Web,
nos quais você recupera WSDL usando uma string de consulta (ou seja, uma solicitação GET).

Agora abra o método Main() do aplicativo de console e digite o código mostrado na Listagem 10-5.

Listagem 10-5. Hospedando o serviço WCF

static void Main(string[] args) {

tentar

Digite t = typeof(EmpregadoGerente); Uri


http = new Uri("http://localhost:8000/EmployeeWCFService"); host ServiceHost =
new ServiceHost(t, tcp, http); host.Open(); Console.WriteLine("Serviço Gerenciador
de Empregados Publicado."); Console.ReadLine(); host.Close();

} catch (Exceção ex) {

Console.WriteLine(ex.Message);
Console.ReadLine();
}
}

O código recupera o Type da classe de tipo de serviço usando a expressão typeof(). Em seguida, ele cria uma
instância da classe Uri apontando para a URL baseada em HTTP na qual o serviço será publicado. Observe como o número
da porta é definido como 8000. Você pode usar qualquer número de porta, mas certifique-se de que não seja usado por
nenhum outro aplicativo e não esteja bloqueado em uma rede.
Em seguida, uma instância da classe ServiceHost é criada. A classe ServiceHost hospeda o serviço
publicando o tipo de serviço nos URIs especificados. Observe que o construtor da classe ServiceHost usa uma matriz
de parâmetros de URIs. No nosso exemplo já passamos de dois, mas você pode passar mais se quiser. A seguinte
assinatura do construtor deixará claro:

public ServiceHost (Tipo serviceType, params Uri[] baseAddresses) {

...
}

O método Open() da classe ServiceHost é então chamado. Este método realmente hospeda o serviço
dependendo das informações de configuração. O serviço permanecerá publicado enquanto o aplicativo host
estiver ativo. É por isso que o método ReadLine() da classe Console é chamado. Ele mantém o aplicativo ativo até que o
usuário pressione a tecla Enter. Finalmente, o método Close() da classe ServiceHost é chamado. Isso conclui o aplicativo
host.

278
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

ÿ Observação Durante o desenvolvimento, você também pode usar o host de serviço WCF interno do Visual Studio,
ignorando completamente o aplicativo host. No entanto, para uma compreensão clara de todas as etapas envolvidas, não
usaremos o host integrado neste exemplo.

Consumindo o serviço Nesta seção,


você cria um aplicativo cliente que consome o EmployeeWCFService que criamos anteriormente.
Para começar, você precisa criar um aplicativo do Windows chamado EmployeeWCFServiceClient, como o
mostrado na Figura 10-5.

Figura 10-5. O cliente consumindo o serviço WCF

O aplicativo consiste em uma caixa de listagem contendo uma lista de todos os funcionários. Clicar em um
funcionário específico exibirá seus detalhes nas caixas de texto colocadas abaixo. Você pode alterar os detalhes como nome,
sobrenome, telefone residencial e notas. Clicar no botão Atualizar salvará as alterações no banco de dados. Uma mensagem
de sucesso também é exibida para o usuário usando um rótulo.
Antes de consumir o serviço, você deve criar um proxy para ele. Execute o aplicativo de host de serviço que você
desenvolveu anteriormente (certifique-se de executar o aplicativo de fora do Visual Studio para poder concluir a próxima
etapa). Em seguida, clique com o botão direito do mouse no projeto do aplicativo cliente e escolha a opção de menu
Incluir referência de serviço. Isso abrirá uma caixa de diálogo como a mostrada na Figura 10-6.

279
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Figura 10-6. Adicionando uma referência de serviço

Insira o endereço de ponto de extremidade HTTP do serviço WCF (ou seja, http://localhost:8000/
EmployeeWCFService) e EmployeeWCFServiceReference como o nome do namespace para a classe proxy.
Isso criará uma referência de serviço para o serviço. Todas as referências de serviço adicionadas a um projeto são listadas
no Solution Explorer, conforme mostrado na Figura 10-7.

Figura 10-7. Referência de serviço conforme mostrado no Solution Explorer

Adicionar uma referência de serviço também adicionará alguma marcação no arquivo de configuração do aplicativo do cliente. Agora
você pode chamar o serviço a partir do seu código de cliente. A Listagem 10-6 mostra o manipulador de eventos Load do formulário.

Listagem 10-6. Recuperando a lista de funcionários

private string endPoint = "BasicHttpBinding_IEmployeeManager";

private void Form1_Load(remetente do objeto, EventArgs e) {

Proxy EmployeeManagerClient = new EmployeeManagerClient(endPoint); EmployeeDataContract[]


dados = proxy.SelectAll();

280
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

foreach(EmployeeDataContract emp in data) {

comboBox1.Items.Add(emp.EmployeeID);

} proxy.Close(); }

O código declara uma variável local—endpoint—para armazenar o nome do terminal de serviço. Você pode descobrir
o nome da configuração do terminal no arquivo de configuração do aplicativo do cliente.
O código então cria uma nova instância da classe proxy. Observe como a classe proxy é denominada
XXXXClient (EmployeeManagerClient em nosso caso), onde XXXX é o nome da classe de serviço original. No construtor
da classe proxy, você passa o nome da configuração do terminal desejado.
O código então chama o método SelectAll() no objeto proxy para recuperar os detalhes de todos os funcionários.
Observe que o método SelectAll() chamado no proxy retorna uma matriz de objetos EmployeeDataContract.
Um loop foreach então preenche os EmployeeIDs na caixa de combinação. Por fim, o canal de comunicação
subjacente é fechado chamando o método Close() do proxy.
Quando um usuário clica em qualquer um dos funcionários listados na caixa de combinação, os detalhes desse funcionário
devem ser exibidos nas outras caixas de texto. Isso é feito no manipulador de eventos SelectedIndexChanged da caixa de combinação
(consulte a Listagem 10-7).

Listagem 10-7. Recuperando os detalhes de um funcionário

private void comboBox1_SelectedIndexChanged(remetente do objeto, EventArgs e) {

Proxy EmployeeManagerClient = new EmployeeManagerClient(endPoint); dados


EmployeeDataContract = proxy.SelectByID((int)comboBox1.SelectedItem); textBox1.Text =
data.FirstName; textBox2.Text = data.LastName; textBox3.Text = data.HomePhone; textBox4.Text
= dados.Notas; label6.Text = ""; proxy.Close();

O código é muito parecido com o que você viu anteriormente. Desta vez, ele chama o método SelectByID() no
proxy passando o EmployeeID selecionado. O método SelectByID() retorna um objeto EmployeeDataContract preenchido
com os detalhes necessários. Os detalhes como EmployeeID, FirstName, LastName, HomePhone e Notes são exibidos
nas respectivas caixas de texto.
Depois que os detalhes de um funcionário são exibidos nas caixas de texto, você pode alterá-los e clicar em
Atualizar para salvá-los no banco de dados. O manipulador de eventos Click do botão Update é mostrado na Listagem 10-8.

Listagem 10-8. Salvando as modificações no banco de dados

private void button1_Click(remetente do objeto, EventArgs e) {

Proxy EmployeeManagerClient = new EmployeeManagerClient(endPoint); dados


EmployeeDataContract = new EmployeeDataContract(); data.EmployeeID =
(int)comboBox1.SelectedItem; data.FirstName = textBox1.Text; data.LastName =
textBox2.Text; dados.HomePhone = textBox3.Text;

281
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

dados.Notas = textBox4.Text;
label6.Text=proxy.Update(data);
proxy.Close();
}

O código é direto — ele cria um objeto proxy como antes. Desta vez, ele também cria um novo objeto
EmployeeDataContract e o preenche com os detalhes modificados de um funcionário. O método Update() do proxy
é então chamado em uma tentativa de salvar as alterações no banco de dados. Um controle Label exibe a
mensagem de sucesso retornada pelo método Update().

Testando o host e o cliente Agora que você

codificou todas as três partes (serviço, host e cliente), vamos testá-las. Primeiro, compile todos os projetos da
solução. Em seguida, execute o aplicativo de console do host. Se tudo correr bem, você deverá ver um prompt de
comando como o mostrado na Figura 10-8.

Figura 10-8. Executando o aplicativo host

Em seguida, abra um navegador e insira a URL do terminal de serviço (http:// localhost:8000/


EmployeeWCFService) na barra de endereços. Você deve obter uma página da Web como a mostrada na Figura 10-9.

282
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Figura 10-9. Testando para ver se o serviço está hospedado corretamente

Se você obtiver esta página, o serviço está hospedado corretamente. Clique no URL especificado na parte superior da página da web
e você deverá ver o WSDL do serviço, conforme mostrado na Figura 10-10.

Figura 10-10. WSDL do serviço

283
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Você achará esse mecanismo muito semelhante aos serviços da web, onde você recuperou o WSDL de um serviço
da web apenas especificando o parâmetro wsdl query string.
Agora execute o aplicativo cliente e você verá a caixa de combinação preenchida com a lista de todos os
funcionários. Clique nos nomes dos funcionários individuais e seus detalhes serão exibidos em várias caixas de texto. Teste
também se quaisquer modificações nos detalhes do funcionário são salvas no banco de dados após clicar no botão Atualizar.

Hospedando um serviço WCF no IIS No exemplo

anterior, usamos um aplicativo de console para hospedar nosso serviço. No entanto, você também pode usar o IIS (ou o
servidor Web de desenvolvimento do Visual Studio) para hospedar serviços WCF. Dessa forma, seu serviço é iniciado
automaticamente quando o aplicativo IIS é iniciado e você também obtém todos os recursos de segurança do IIS para seu serviço.
Para começar, adicione um novo projeto ASP.NET Web Application à solução atual (consulte a Figura 10-11).

Figura 10-11. Adicionando um novo aplicativo da Web ASP.NET

Ao criar o projeto, selecione o modelo de projeto Vazio, conforme mostrado na Figura 10-12.

284
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Figura 10-12. Projeto baseado no template Vazio

Dessa forma, você terá um projeto apenas com os itens básicos do projeto, como web.config. Em seguida, adicione
uma referência ao assembly EmployeeWCFService como você fez nos exemplos anteriores. Agora, adicione um novo
serviço WCF ao projeto usando a caixa de diálogo Add New Item (consulte a Figura 10-13).

Figura 10-13. Adicionando um novo serviço WCF

285
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Isso adicionará um novo arquivo .svc ao projeto e também seu code-behind. O arquivo .svc contém informações
sobre o serviço WCF sendo exposto. Você pode excluir o arquivo code-behind porque nosso serviço já está empacotado
em um assembly.
Em seguida, abra o arquivo EmployeeManager.svc no editor de texto do Visual Studio. Você descobrirá que o
arquivo .svc contém a diretiva @ServiceHost. Modifique esta diretiva conforme mostrado na Listagem 10-9.

Listagem 10-9. Adicionando a Diretiva ServiceHost

<%@ServiceHost Service="EmployeeWCFService.EmployeeManager" %>

A diretiva @ServiceHost indica que o arquivo é um host de serviço WCF. O atributo Service especifica
o nome totalmente qualificado do tipo de serviço. Agora adicione um arquivo web.config ao site e digite a marcação
mostrada na Listagem 10-10.

Listagem 10-10. Configurando o host

<system.serviceModel>
<services> <service
name="EmployeeWCFService.EmployeeManager"
behaviorConfiguration="EmployeeWCFServiceBehavior"> <endpoint
address="" binding="basicHttpBinding"
contract="EmployeeWCFService.IEmployeeManager"> </
endpoint> </service> </services> <behaviors> <serviceBehaviors>
<behavior name="EmployeeWCFServiceBehavior" > <serviceMetadata
httpGetEnabled="True"/> <serviceDebug includeExceptionDetailInFaults="true" />
</behavior> </serviceBehaviors> </behaviors> </system.serviceModel>

É essencialmente a mesma marcação que você especificou para o host do console. A única diferença é que o atributo
de endereço do elemento <endpoint> é uma string vazia. Isso ocorre porque, para um serviço hospedado no IIS, o endereço é
o mesmo que o URI do arquivo .svc que hospeda o serviço.
Para acessar o EmployeeService hospedado no IIS, a URL do terminal será http://localhost:49303/
EmployeeManager.svc (altere o número da porta para corresponder ao seu). Observe que a URL aponta para o arquivo .svc.
Você também pode anexar o parâmetro de string de consulta wsdl para extrair seu WSDL. A Figura 10-14 mostra um exemplo
de execução do arquivo .svc em um navegador.

286
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Figura 10-14. Navegando para o arquivo .svc em um navegador

Você pode alimentar este URL para o gerador de proxy e o proxy será gerado para você. o geral
processo de criação de proxy e chamada do serviço permanece o mesmo do exemplo anterior.

Compreendendo o papel do XML nos serviços WCF


Pelo que você aprendeu até agora, está claro que o WCF usa XML para configurar vários serviços. No entanto, há mais na
história.
No exemplo anterior que você acabou de desenvolver, os dados do funcionário do serviço EmployeeManager
apareceu magicamente no aplicativo cliente. No entanto, como isso aconteceu? Como os dados foram enviados no fio? No que
diz respeito à ligação HTTP básica, os dados são serializados na rede como uma mensagem SOAP. Isso significa que os dados
XML na forma de um envelope SOAP estão sendo enviados ao cliente.

ÿ Observação A comunicação baseada em SOAP pode causar uma penalidade de desempenho devido à sua natureza

volumosa. No entanto, SOAP é uma escolha natural quando a interoperabilidade é mais importante para seu aplicativo. O WCF

também permite que você use comunicação binária sobre TCP. Essa comunicação pode oferecer benefícios de desempenho ao seu aplicativo.

A discussão desses detalhes está além do escopo deste livro.

Apenas para entender o que acabou de ser dito, vá para o projeto EmployeeWCFService e pressione F5. Fazê-lo vai abrir o
WCF Test Client, conforme mostrado na Figura 10-15.

287
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Figura 10-15. Observando uma mensagem no cliente de teste WCF

O cliente de teste WCF permite invocar métodos de serviço. No lado esquerdo, clique duas vezes no método SelectAll().
Em seguida, clique no botão Chamar no lado direito do painel. Isso invocará o método e todos os objetos EmployeeDataContract
serão retornados. Alterne para a guia XML e você verá a solicitação SOAP e as mensagens de resposta SOAP.

No caso de serviços WCF RESTful (você aprenderá sobre eles em seções posteriores), os dados são serializados como XML
simples em vez de uma mensagem SOAP.

Usando XmlSerializer em vez de DataContractSerializer


A estrutura WCF pode usar duas abordagens ao serializar os dados XML:

• Por padrão, o WCF usa a classe DataContractSerializer (uma implementação de


XmlObjectSerializer) para serializar os dados XML. Você já aprendeu sobre a classe DataContractSerializer
no Capítulo 8.

•Você também pode instruir o WCF a usar XmlSerializer para a serialização XML. Isso lhe dará mais controle
sobre como os dados são serializados. Você pode personalizar o processo de serialização usando
vários atributos como [XmlAttribute] e [XmlElement]. Você já aprendeu sobre a classe XmlSerializer no
Capítulo 8.

Para instruir a estrutura WCF de que deve usar XmlSerializer em vez de DataContractSerializer, tudo o que você precisa fazer é
decorar a interface do contrato de serviço com o atributo [XmlSerializerFormat]. A interface IEmployeeManager após a aplicação do
atributo [XmlSerializerFormat] é mostrada na Listagem 10-11.

288
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Listagem 10-11. Aplicando o atributo [XmlSerializerFormat]

[Contrato de serviço]
[XmlSerializerFormat]
interface pública IEmployeeManager {

....
....
}

Entendendo os serviços REST


Os serviços WCF que discutimos até agora eram baseados em operações, pois você sempre invocava explicitamente um
método ou operação específica do serviço. Aplicações web modernas geralmente criam e consomem serviços RESTful.
De nossa discussão anterior, você sabe que os serviços REST são serviços baseados em recursos. Embora este
capítulo não tente ensinar a você os fundamentos dos serviços REST como tal, vale a pena uma breve discussão sobre
REST.
REST significa REpresentational State Transfer. REST não é um padrão; é uma forma de arquitetar seu
Serviços. Ao contrário dos serviços Web ASMX ou serviços baseados em operações WCF que usam protocolos e
padrões como Simple Object Access Protocol (SOAP) e Web Services Description Language (WSDL), os serviços
RESTful aproveitam a simplicidade e o poder do HTTP.
Aqui estão algumas características fundamentais dos serviços RESTful:

•Serviços REST usam o protocolo HTTP.

•Os serviços REST fazem solicitações HTTP (usando verbos HTTP significativos, como GET, POST,
PUT e DELETE) para buscar e enviar dados.

•Serviços REST não têm estado por natureza.

•REST expõe serviços como recursos acessíveis e detectáveis por meio de


URLs.

•Serviços REST normalmente transferem dados no formato JSON ou XML.

No que diz respeito ao .NET Framework, há duas maneiras de criar serviços RESTful — serviços WCF REST e
Web API. Hoje em dia é mais comum que aplicações web utilizem JSON (JavaScript Object Notation) como formato
para transferência de dados entre um serviço e seu cliente, mas você também pode utilizar XML. Nosso interesse, é claro, é
ver como o XML pode ser usado junto com essas estruturas.
Os verbos HTTP como GET, POST, PUT e DELETE indicam a ação desejada a ser executada em um recurso. Se
um serviço executa operações de criação, leitura, atualização e exclusão (CRUD) em um banco de dados, você pode usar
POST para indicar uma operação INSERT, GET para indicar uma operação SELECT, PUT para indicar uma operação
UPDATE e DELETE para indicar uma operação DELETE Operação. No entanto, apenas usar um verbo específico não
imporá um tipo específico de operação. Cabe a você implementar esses verbos em um serviço dependendo dos requisitos
do aplicativo.
Agora que você tem uma ideia do que são serviços REST, verá como criá-los, primeiro usando WCF e depois usando
Web API.

289
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Criando um serviço REST usando o WCF


A criação de um serviço RESTful usando o WCF requer as mesmas etapas com as quais você está familiarizado. No entanto, duas grandes
mudanças são:

•Como os serviços REST usam verbos HTTP para invocar métodos, você usa webHttpBinding.
Também é comum hospedar tal serviço no IIS ou em um servidor web (conforme discutido anteriormente).

•O mapeamento entre verbos HTTP e métodos de serviço é especificado no serviço


contrato.

Além dessas mudanças, as partes restantes de um serviço são mais ou menos as mesmas de antes.
Comece criando um novo aplicativo Web ASP.NET com base no modelo de projeto vazio. Assim como você fez
com o projeto EmployeeWCFService, adicione uma pasta Models e gere o contexto Entity Framework e a classe de
entidade (Employee). Adicione também a classe EmployeeDataContract como antes.
Em seguida, adicione uma interface — IEmployeeManager — ao projeto. A interface IEmployeeManager concluída é
mostrada na Listagem 10-12.

Listagem 10-12. Interface IEmployeeManager

[ServiceContract]
interface pública IEmployeeManager {

[Contrato de Operação]
[WebGet(UriTemplate = "/funcionários",
ResponseFormat =WebMessageFormat.Xml)]
List<ContratoDadosFuncionário> SelectAll();

[Contrato de Operação]
[WebGet(UriTemplate = "/funcionários/{id}",
ResponseFormat = WebMessageFormat.Xml)]
EmployeeDataContract SelectByID(string id);

[Contrato de Operação]
[WebInvoke(Método = "POST",
UriTemplate = "/funcionários",
RequestFormat =WebMessageFormat.Xml ,
ResponseFormat = WebMessageFormat.Xml)]
string Insert(EmployeeDataContract emp);

[Contrato de Operação]
[WebInvoke(Method = "PUT",
UriTemplate = "/employees/{id}",
RequestFormat =WebMessageFormat.Xml,
ResponseFormat = WebMessageFormat.Xml)]
string Update(string id,EmployeeDataContract emp);

[Contrato de Operação]
[WebInvoke(Método = "EXCLUIR",
UriTemplate = "/employees/{id}",
ResponseFormat = WebMessageFormat.Xml)]
string Delete(string id);
}

290
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Observe a interface IEmployeeManager cuidadosamente. Parece bastante semelhante ao exemplo anterior,


mas com mais algumas operações. Agora ele tem um total de cinco operações—SelectAll(), SelectByID(), Insert(),
Update() e Delete().
As operações são decoradas com o atributo [OperationContract]. Além disso, eles têm o atributo [WebGet] ou
[WebInvoke] neles. Esses atributos vêm do namespace System.ServiceModel.Web e indicam que o serviço WCF está
seguindo o modelo de programação REST.
O atributo [WebGet] indica que SelectAll() e SelectByID() são operações de recuperação. O
O atributo [WebInvoke] indica que Insert(), Update() e Delete() são operações de chamada.
As propriedades de [WebGet] e [WebInvoke] configuram o serviço REST. Essas propriedades são
discutidas a seguir:

•Method: A propriedade Method indica o verbo HTTP que invoca uma operação.
Os verbos comuns são GET, POST, PUT e DELETE.

•UriTemplate: indica o modelo da URL do endpoint que é usado para invocar um método. Por
exemplo, http://localhost/funcionários ou http://localhost/funcionários/1. Observe como o
EmployeeID é parametrizado usando a sintaxe {id}.
Este valor de id é considerado como uma string e, portanto, todas as operações subjacentes
especificam o parâmetro id como um tipo de string.

•RequestFormat: Indica o formato dos dados que acompanham um pedido. Os valores possíveis
são Xml e Json (enumeração WebMessageFormat).

•ResponseFormat: Indica o formato dos dados que acompanham uma resposta. Possível
os valores são Xml e Json (enumeração WebMessageFormat).

Agora que a interface IEmployeeManager está pronta, adicione um serviço WCF ao projeto chamado
EmployeeManager.svc. O arquivo code-behind do arquivo .svc abrigará a classe EmployeeManager. Essa classe
implementa a interface IEmployeeManager como antes. O código completo da classe EmployeeManager é mostrado na
Listagem 10-13.

Listagem 10-13. A classe EmployeeManager implementa IEmployeeManager

public class EmployeeManager:IEmployeeManager {

public List<EmployeeDataContract> SelectAll() {

usando (Northwind db = new Northwind()) {

var query = from e in db.Employees orderby


e.EmployeeID ascendente select new
EmployeeDataContract() {

EmployeeID = e.EmployeeID,
FirstName = e.FirstName,
LastName = e.LastName,
HomePhone = e.HomePhone,
Notes = e.Notes
,
return query.ToList();
}
}

291
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

public EmployeeDataContract SelectByID(string id) {

usando (Northwind db = new Northwind()) {

int empId = int.Parse(id); var


query = from e in db.Employees where
e.EmployeeID == empId select
new EmployeeDataContract() {

EmployeeID = e.EmployeeID,
FirstName = e.FirstName,
LastName = e.LastName,
HomePhone = e.HomePhone,
Notes = e.Notes
};
return query.SingleOrDefault();
}
}

public string Insert(EmployeeDataContract obj) {

usando (Northwind db = new Northwind()) {

Funcionário emp = new Funcionário();


Emp.EmployeeID = obj.EmployeeID;
emp.FirstName = emp.FirstName;
emp.LastName = emp.LastName;
emp.HomePhone = emp.HomePhone;
emp.Notas = emp.Notas;
db.Employees.Add(emp);
db.SaveChanges(); return "Funcionário
adicionado com sucesso!";
}
}

public string Update(string id,EmployeeDataContract obj) {

usando (Northwind db = new Northwind()) {

Employee emp = db.Employees.Find(int.Parse(id));


emp.FirstName = obj.FirstName; emp.LastName =
obj.LastName; emp.HomePhone = obj.HomePhone;
emp.Notas = obj.Notas; db.SaveChanges(); return
"Empregado modificado com sucesso!";

}
}

string pública Excluir (id da string) {

usando (Northwind db = new Northwind ())

292
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

{
Employee emp = db.Employees.Find(int.Parse(id));
db.Employees.Remove(emp); db.SaveChanges(); return
"Funcionário deletado com sucesso!";

}
}
}

Esta implementação deve ser familiar para você porque SelectAll(), SelectByID() e Update() são
bastante semelhante ao exemplo anterior.
O método Insert() aceita um novo objeto EmployeeDataContract e constrói um novo funcionário a partir dele.
Em seguida, adiciona o funcionário recém-criado ao DbSet Employees. O método SaveChanges() salva os dados no
banco de dados.
O método Delete() aceita um EmployeeID a ser excluído. Lá dentro, ele encontra esse funcionário e o remove do
DbSet Employees usando o método Remove(). O método SaveChanges() salva as alterações no banco de dados.

Agora, abra o arquivo web.config e adicione a configuração mostrada na Listagem 10-14.

Listagem 10-14. Configuração do serviço EmployeeManager

<system.serviceModel>
<serviços>
<service name="EmployeeWCFServiceREST.EmployeeManager"
behaviorConfiguration="EmployeeWCFServiceRESTBehavior">
<endpoint address="" binding="webHttpBinding"
contract="EmployeeWCFServiceREST.IEmployeeManager"
behaviorConfiguration="web"> </endpoint> </service>

</services>

<comportamentos>
<serviceBehaviors>
<behavior name="EmployeeWCFServiceRESTBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug
includeExceptionDetailInFaults="false" /> </behavior>

<comportamento>

<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug


includeExceptionDetailInFaults="false" /> </behavior>

</serviceBehaviors>
<endpointBehaviors>
<nome do comportamento="web">

<webHttp/>
</behavior>
</endpointBehaviors> </
behaviors>
</system.serviceModel>

293
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Essa configuração é bastante semelhante à usada nos aplicativos de host anteriores. A principal diferença é que ele usa
a associação webHttpBinding em vez de basicHttpBinding.
Isso conclui o serviço REST EmployeeManager. E agora é hora de consumi-lo de um aplicativo cliente.

Criando um cliente que consome o EmployeeManager REST


Serviço
O aplicativo cliente que consome o serviço REST EmployeeManager será um aplicativo Windows Forms. Esta aplicação é
mostrada na Figura 10-16.

Figura 10-16. Aplicativo cliente consumindo o serviço REST

O aplicativo cliente é bastante semelhante ao que você desenvolveu no exemplo anterior. Mas agora ele tem mais dois botões
- Inserir e Excluir - para invocar as respectivas operações. Inserir detalhes de um novo funcionário, exceto EmployeeID, e clicar no
botão Inserir adiciona esse funcionário. Como EmployeeID é uma coluna de identidade, não é necessário especificá-la na interface do
usuário. Selecionar um EmployeeID na caixa de combinação e clicar no botão Excluir exclui esse funcionário. A mensagem de sucesso
retornada das operações de inserção, atualização e exclusão é exibida em um controle Label colocado abaixo dos botões.

Embora a interface do usuário do aplicativo cliente seja semelhante ao exemplo anterior, o funcionamento interno
são totalmente diferentes. Anteriormente, você criava um proxy no aplicativo cliente e, em seguida, chamava os métodos desejados
no proxy. Tudo bem, já que o serviço era baseado em operações. Agora, esse aplicativo cliente deseja consumir um serviço REST.
Aqui, os verbos HTTP decidem quais métodos do serviço devem ser invocados.
Portanto, nenhuma geração de proxy é necessária. Em vez disso, esse aplicativo usará o componente HttpClient do namespace
System.Net.Http. Vamos ver como.
O manipulador de eventos Load do formulário é mostrado na Listagem 10-15.

294
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Listagem 10-15. Obtendo dados do funcionário quando o formulário é carregado

cliente HttpClient privado;

private void Form1_Load(remetente do objeto, EventArgs e) {

cliente = new HttpClient();


cliente.BaseAddress = new Uri("http://localhost:49833");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue ("aplicativo/xml"));
HttpResponseMessage response = client.GetAsync("/EmployeeManager.svc/employees").Result;
string xmlData = response.Content.ReadAsStringAsync().Result; XmlDocumento doc = new XmlDocumento();
doc.LoadXml(xmlData); XmlNodeList list = doc.GetElementsByTagName("EmployeeID"); foreach(item XmlNode na lista) {

comboBox1.Items.Add(item.InnerText);
}
}

O código declara uma variável HttpClient — cliente — para chamar o serviço REST. Como queremos chamar os serviços
de vários locais, esse objeto é criado no nível do formulário, em vez de uma variável local do manipulador de eventos.

O manipulador de eventos Load instancia o HttpClient e define sua propriedade BaseAddress para a URL base,
onde o serviço REST está localizado. No nosso caso, esta é a URL do aplicativo da web onde adicionamos o arquivo
EmployeeManager.svc. Certifique-se de alterar o número da porta conforme sua configuração. Você pode obter facilmente
esse URL visualizando o arquivo .svc em um navegador.
A próxima linha adiciona o cabeçalho Accept à coleção DefaultRequestHeaders. O valor do cabeçalho Accept de
application/xml significa que o serviço deve retornar dados no formato XML.
Em seguida, o código chama o SelectAll() do serviço. Observe que em nenhum lugar o nome da operação é
mencionado. O código usa o método GetAsync() do objeto HttpClient e especifica a URL do ponto de extremidade do serviço.
Lembre-se de que IEmployeeManager especifica este UriTemplate. O método GetAsync() usa o verbo GET para fazer a
solicitação. Todos os métodos de HttpClient são assíncronos por natureza. Acessar a propriedade Result bloqueia o thread atual
até que o método seja concluído.
O método GetAsync() retorna HttpResponseMessage — um objeto que envolve o valor de retorno real.
Para pegar o valor real, você usa o método ReadAsStringAsync(). O método ReadAsStringAsync() retorna a representação
XML dos dados retornados pelo serviço REST. Esse fragmento XML é então carregado em um XmlDocument. Como queremos
preencher a caixa de combinação com valores EmployeeID, todos os elementos <EmployeeID> são recuperados usando o
método GetElementsByTagName(). Um loop foreach percorre os elementos recuperados e preenche a caixa de combinação
conforme desejado.
Se você observar a variável de string xmlData na janela Quick Watch do Visual Studio, verá XML
conforme mostrado na Listagem 10-16.

Listagem 10-16. O serviço REST retorna dados no formato XML

<ArrayOfEmployeeDataContract xmlns="http://schemas.datacontract.org/2004/07/ EmployeeWCFServiceREST"


xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <EmployeeDataContract> <EmployeeID>1 </
EmployeeID> <FirstName>Nancy</FirstName> <HomePhone>(206) 555-9857</HomePhone>

295
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

<LastName>Davolio</LastName>
<Notes>A educação inclui bacharelado em psicologia pela Colorado State University em 1970. Ela
também concluiu "The Art of the Cold Call". Nancy é membro da Toastmasters International.</Notes> </
EmployeeDataContract>

....
....
</ArrayOfEmployeeDataContract>

ÿ Observação Aqui usamos o XML bruto retornado pelo serviço WCF REST. Se desejar, você pode carregar esses
dados XML em objetos Employee/EmployeeDataContract no aplicativo cliente (você precisará criar essas classes no
aplicativo cliente). Dessa forma, o aplicativo cliente pode processar esses objetos conforme necessário. Não faremos
isso aqui porque nosso interesse principal é ver como o formato de dados XML é usado por essas estruturas.

Ok, agora vamos ver como SelectByID() pode ser chamado no manipulador de eventos SelectedIndexChanged da caixa de
combinação (consulte a Listagem 10-17).

Listagem 10-17. Chamando o método SelectByID()

private void comboBox1_SelectedIndexChanged(remetente do objeto, EventArgs e) {

Resposta HttpResponseMessage = client.GetAsync("/EmployeeManager.svc/employees/" +


comboBox1.SelectedItem).Result; string xmlData = response.Content.ReadAsStringAsync().Result;
XmlDocumento doc = new XmlDocumento(); doc.LoadXml(xmlData); textBox1.Text =
doc.GetElementsByTagName("FirstName")[0].InnerText; textBox2.Text =
doc.GetElementsByTagName("LastName")[0].InnerText; textBox3.Text =
doc.GetElementsByTagName("HomePhone")[0].InnerText; textBox4.Text =
doc.GetElementsByTagName("Notas")[0].InnerText; label6.Text = "";

Este código é semelhante à listagem anterior. No entanto, o método GetAsync() também passa
um EmployeeID na URL. Desta forma, o método SelectByID() do serviço REST é chamado. O código restante
simplesmente lê os dados XML retornados e preenche várias caixas de texto com valores FirstName, LastName,
HomePhone e Notes.
O manipulador de eventos Click do botão Insert é responsável por chamar o método Insert() do REST
serviço. Esse manipulador de eventos é mostrado na Listagem 10-18.

296
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Listagem 10-18. Chamando o método Insert() do serviço REST

private void button1_Click(remetente do objeto, EventArgs e) {

string xmlEmp = $"<EmployeeDataContract xmlns='http://schemas.datacontract. org/2004/07/


EmployeeWCFServiceREST' xmlns:i='http://www.w3.org/2001/XMLSchema instance'><FirstName>
{textBox1.Text}</FirstName><HomePhone>{textBox3.Text}</ HomePhone><LastName>{textBox2.Text}
</LastName><Notes>{textBox4.Text}</Notes></ EmployeeDataContract>" ;

conteúdo HttpContent = new StringContent(xmlEmp, Encoding.UTF8, "aplicativo/xml");

HttpResponseMessage response = client.PostAsync("/EmployeeManager.svc/employees",


content).Result;

string xmlMsg = response.Content.ReadAsStringAsync().Result;

XmlDocumento doc = new XmlDocumento();


doc.LoadXml(xmlMsg); label6.Text =
doc.DocumentElement.InnerText;

Configuramos o serviço REST para usar o formato XML para a solicitação e também para a resposta. Enquanto
adicionando um funcionário, primeiro precisamos formar um fragmento XML que representa um objeto
EmployeeDataContract. Isso é feito criando um fragmento XML conforme mostrado. O fragmento XML basicamente
cria um elemento <EmployeeDataContract></EmployeeDataContract> com quatro elementos filhos — <FirstName>,
<LastName>, <HomePhone> e <Notes>. Os valores desses elementos filho são definidos nas respectivas caixas de
texto. Como você pode ver, o nome do elemento raiz corresponde ao nome da classe de contrato de dados e os nomes
dos elementos filhos correspondem aos nomes dos membros de dados.
Em seguida, o fragmento XML é agrupado em um objeto StringContent. E então o método PostAsync ()
de HttpClient é chamado. O método PostAsync() faz uma solicitação POST para o serviço especificado. O objeto
StringContent contendo os detalhes do funcionário também acompanha a solicitação.
O método Insert() do serviço retorna uma mensagem de sucesso. Esta mensagem é descompactada de
HttpResponseMessage usando o método ReadAsStringAsync(). Essa mensagem é então exibida em um controle
Label.
O manipulador de eventos Client do botão Update é semelhante e é mostrado na Listagem 10-19.

Listagem 10-19. Chamando o método Update() do serviço REST

private void button2_Click(remetente do objeto, EventArgs e) {

string xmlEmp = $"<EmployeeDataContract xmlns='http://schemas.datacontract. org/2004/07/


EmployeeWCFServiceREST' xmlns:i='http://www.w3.org/2001/XMLSchema-in stance'><
EmployeeID>{comboBox1.SelectedItem}</EmployeeID><FirstName>{textBox1.
Text}</FirstName><HomePhone>{textBox3.Text}</HomePhone><LastName>{textBox2.Text}</
LastName><Notes>{textBox4.Text}</Notes></EmployeeDataContract>";

conteúdo HttpContent = new StringContent(xmlEmp, Encoding.UTF8, "aplicativo/xml");

HttpResponseMessage response = client.PutAsync("/EmployeeManager.svc/employees/" +


comboBox1.SelectedItem, content).Result;

297
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

string xmlMsg = response.Content.ReadAsStringAsync().Result;

XmlDocumento doc = new XmlDocumento();


doc.LoadXml(xmlMsg); label6.Text =
doc.DocumentElement.InnerText;
}

A principal diferença aqui é que o método PutAsync() de HttpClient foi chamado. Fazer isso faz um
Solicitação PUT ao serviço. A URL de solicitação também inclui o EmployeeID a ser modificado.
Finalmente, o manipulador de eventos Click do botão Delete faz o trabalho de chamar o método Delete() do
serviço e é mostrado na Listagem 10-20.

Listagem 10-20. Chamando o método Delete() do serviço REST

private void button3_Click(remetente do objeto, EventArgs e) {

Resposta HttpResponseMessage = client.DeleteAsync("/EmployeeManager.svc/employees/" +


comboBox1.SelectedItem).Result; string xmlMsg = response.Content.ReadAsStringAsync().Result; XmlDocumento
doc = new XmlDocumento(); doc.LoadXml(xmlMsg); label6.Text = doc.DocumentElement.InnerText;

Aqui, o código chama o método DeleteAsync() de HttpClient. O EmployeeID a ser excluído é passado como parte da URL.

Isso conclui o aplicativo cliente. Execute o cliente e veja se a caixa de combinação é preenchida com os valores EmployeeID
existentes. Tente também adicionar, modificar e excluir funcionários para confirmar o funcionamento do serviço REST
EmployeeManager.

Criando um serviço REST usando API Web


Agora que você sabe o que é necessário para criar um serviço REST usando o WCF, vamos ver como a API da Web pode ser
usada para esse fim. Nesta seção, você cria um serviço de API da Web que expõe a funcionalidade CRUD exatamente como o
serviço WCF criado na seção anterior. Um aplicativo cliente baseado em Windows Forms consome o serviço.

Comece criando um novo aplicativo da Web ASP.NET com base no modelo de projeto da API da Web (consulte
a Figura 10-17).

298
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Figura 10-17. Criando um projeto com base no modelo de API da Web

O projeto recém-criado contém um serviço de API da Web. Renomeie esse serviço (arquivo e classe) para
EmployeeManagerController. Em seguida, gere o modelo Entity Framework para a tabela Employees do banco de dados
Northwind, como você fez nos exemplos anteriores.
Abra o EmployeeManagerController no Editor do Visual Studio. Você notará que a classe
EmployeeManagerController herda de ApiController. Em seguida, escreva as ações que executam as operações CRUD
no EmployeeManagerController. O esqueleto dessas ações é mostrado na Listagem 10-21.

Listagem 10-21. Esqueleto do serviço de API da Web

public class EmployeeManagerController : ApiController {

[HttpGet]
public List<Funcionário> SelectAll() { }

[HttpGet]
public Employee SelectByID(int id) { }

[HttpPost]
public string Insert(Employee obj) { }

299
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

[HttpPut]
public string Update(int id, Employee obj) { }

[HttpDelete]
public string Delete(int id) { }

Existem cinco métodos ao todo—SelectAll(), SelectByID(), Insert(), Update() e Delete(). Esses


Os métodos devem parecer familiares para você porque o serviço WCF REST também os tinha. Mas existem
algumas diferenças:

•O serviço Web API usa objetos Employee. Não criamos um contrato formal de dados aqui. Você
poderia ter feito isso se necessário, mas na API da Web você não precisa de atributos como
[DataContract] e [DataMember].

•As ações da API da Web são decoradas com determinados atributos que as mapeiam para um verbo
HTTP. Por exemplo, o [HttpPost] adicionado à ação Insert() indica que as solicitações POST
serão mapeadas para a ação Insert(). Você pode evitar decorar as ações com esses atributos se
seguir a convenção de nomenclatura padrão — o nome da ação começa com o verbo HTTP com
o qual ela lida. Por exemplo, Get() é mapeado automaticamente para o verbo GET, Post() é
mapeado automaticamente para o verbo POST e assim por diante.

O código interno dessas ações é muito semelhante ao serviço EmployeeManager WCF. Então, não vamos
discutir todos eles aqui. Por uma questão de clareza, apenas SelectAll() e Post() são mostrados na Listagem 10-22.

Listagem 10-22. As ações de API da Web SelectAll() e Post()

[HttpGet]
public List<Empregado> SelectAll() {

usando (Northwind db = new Northwind()) {

var query = from e in db.Employees orderby


e.EmployeeID ascendente select e;
return query.ToList();

}
}

[HttpPost]
public string Insert(Employee obj) {

usando (Northwind db = new Northwind()) {

Employee emp = db.Employees.Find(obj.EmployeeID);


emp.FirstName = emp.FirstName; emp.LastName = emp.LastName;
emp.HomePhone = emp.HomePhone; emp.Notas = emp.Notas;

300
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

db.Employees.Add(emp);
db.SaveChanges(); return
"Funcionário adicionado com sucesso!";
}
}

Ao contrário do WCF, a API da Web não exige que nenhuma configuração de host seja adicionada ao web.config. esse é o
benefício do modelo de programação simples oferecido pela API da Web.

Criando um cliente que consome a API Web EmployeeManager


Serviço
O aplicativo cliente que consome a API da Web é bastante semelhante ao aplicativo cliente WCF que você criou anteriormente
e é mostrado na Figura 10-18.

Figura 10-18. Aplicativo cliente que chama a API Web

Embora o aplicativo cliente seja bastante semelhante ao que você desenvolveu anteriormente, há duas diferenças:

• A URL do terminal para o serviço REST agora mudará e seguirá a API da Web
convenções.

•Como a API da Web agora está retornando e aceitando objetos Employee, os dados XML recebidos
e enviados do cliente serão agrupados em um elemento <Employee>.

Para entender como essas alterações são refletidas no código, a Listagem 10-23 mostra o manipulador de eventos Load
do formulário.

301
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Listagem 10-23. O manipulador de eventos Load recebe objetos de funcionários no formato XML

private void Form1_Load(remetente do objeto, EventArgs e) {

cliente = new HttpClient();


cliente.BaseAddress = new Uri("http://localhost:49443");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("aplicativo/xml"));

HttpResponseMessage response = client.GetAsync("/api/EmployeeManager").Result; string xmlData


= response.Content.ReadAsStringAsync().Result;

XmlDocumento doc = new XmlDocumento();


doc.LoadXml(xmlData);

XmlNodeList list = doc.GetElementsByTagName("EmployeeID"); foreach(item


XmlNode na lista) {

comboBox1.Items.Add(item.InnerText);
}

Observe as linhas marcadas em negrito. A propriedade BaseAddress agora aponta para o endereço da API Web
aplicativo. Você deve alterar o número da porta conforme sua configuração.
A segunda linha em negrito especifica a URL da API da Web. Observe que a URL assume o formato /api/
<api_controller>. Esse padrão vem da rota padrão da API da Web e você pode alterá-lo no arquivo WebApiConfig.cs do
projeto da API da Web.
Se você observar o xmlData retornado da API da Web, deverá ver algo como isto:

<ArrayOfEmployee xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas. datacontract.org/2004/07/


EmployeeWebAPIService.Models"> <Employee> <EmployeeID >1</EmployeeID> <FirstName>Nancy</FirstName>
<HomePhone>(206) 555-9857</HomePhone> <LastName>Davolio</LastName> <Notes>A educação inclui bacharelado em
psicologia pela Colorado State University em 1970 .

Ela também completou "The Art of the Cold Call". Nancy é membro da Toastmasters International.</Notes> </
Employee>

....
</ArrayOfEmployee>

Como você pode ver, os detalhes do funcionário agora estão agrupados dentro de um elemento <Employee>.
Isso também significa que durante as operações POST e PUT, você deve enviar os dados agrupados dentro de
um elemento <Employee>. Por exemplo, considere o código mostrado na Listagem 10-24, que mostra o manipulador de
eventos Click do botão Atualizar.

302
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Listagem 10-24. Chamando PutAsync() para modificar um funcionário

private void button2_Click(remetente do objeto, EventArgs e) {

string xmlEmp = $"<Employee xmlns='http://schemas.datacontract.org/2004/07/


EmployeeWebAPIService.Models' xmlns:i='http://www.w3.org/2001/XMLSchema-instance'>
<EmployeeID>{comboBox1.SelectedItem}</EmployeeID><FirstName>{textBox1.Text}
</FirstName><HomePhone>{textBox3.Text}</HomePhone><LastName>{textBox2.Text}
</LastName><Notes>{textBox4.Text}</Notes></Employee>";

conteúdo HttpContent = new StringContent(xmlEmp, Encoding.UTF8, "aplicativo/xml"); HttpResponseMessage


response = client.PutAsync("/api/EmployeeManager/" + comboBox1.
SelectedItem, conteúdo).Result; string
xmlMsg = response.Content.ReadAsStringAsync().Result; XmlDocumento doc =
new XmlDocumento(); doc.LoadXml(xmlMsg); label6.Text =
doc.DocumentElement.InnerText;

Os detalhes modificados do funcionário agora estão agrupados dentro do elemento <Employee> junto com os
detalhes do namespace.
Outros manipuladores de eventos não são discutidos aqui, pois são bastante semelhantes aos exemplos anteriores.
Apenas tome cuidado com essas duas diferenças e complete-as por conta própria (ou pegue-as no código-fonte do livro).

Depois que o aplicativo cliente estiver pronto, execute-o e teste se as operações CRUD funcionam conforme o
esperado.

Usando XmlSerializer em vez de DataContractSerializer Assim como os serviços WCF, a API da

Web também usa DataContractSerializer para executar a serialização XML. Se você quiser usar o XmlSerializer,
precisará configurar a API da Web de acordo.
Para configurar a API da Web para usar XmlSerializer em vez de DataContractSerializer, abra o
Arquivo WebApiConfig.cs da pasta App_Start. Em seguida, adicione o código mostrado na Listagem 10-25.

Listagem 10-25. Configurando a API da Web para usar o XmlSerializer

public static void Register(HttpConfiguration config) {

config.MapHttpAttributeRoutes();
config.Formatters.XmlFormatter.UseXmlSerializer = verdadeiro;
config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/
{controller}/{id}", defaults: new { id = RouteParameter.Optional }

);
}

Observe o código mostrado em negrito. A propriedade UseXmlSerializer do XmlFormatter (XmlFormatter


é um objeto de XmlMediaTypeFormatter) é definido como true. Como a API da Web agora está configurada para
usar XmlSerializer, você não precisa mais especificar esses namespaces XML de contrato de dados ao enviar dados para a
API da Web.

303
Machine Translated by Google

Capítulo 10 ÿ XML no WCF e API da Web

Resumo
Este capítulo apresentou a você duas estruturas de desenvolvimento de serviço — Windows Communication
Foundation (WCF) e Web API. O WCF permite que você desenvolva serviços REST baseados em operações e em
recursos. Por outro lado, a API da Web permite que você crie serviços REST baseados em recursos. WCF e Web API
podem lidar com vários formatos de dados, embora JSON e XML sejam bastante populares atualmente. Nosso interesse
era ver como os dados podem ser enviados e recebidos no formato XML usando esses frameworks.
A criação de um serviço WCF baseado em operações envolve três partes: serviço, host e aplicativo cliente.
O aplicativo cliente gera um proxy para o serviço invocar suas operações. WCF usa configuração XML
para configurar o serviço. Além disso, o WCF usa mensagens SOAP para passar os dados por HTTP.
A criação de um serviço REST usando WCF e Web API envolve duas partes: o serviço e o aplicativo
cliente. Ambas as estruturas podem usar o formato XML para transferir os dados pela rede. O aplicativo cliente não cria
nenhum proxy, mas chama o serviço diretamente usando HttpClient. Uma maneira simples de acessar os dados XML
retornados por um serviço REST é carregá-los em XmlDocument.

304
Machine Translated by Google

CAPÍTULO 11

XML no SQLServer

A maioria dos aplicativos de negócios armazena dados em algum tipo de armazenamento de dados, que geralmente é um banco
de dados relacional. Para esse fim, o SQL Server é um dos principais produtos da Microsoft. Como muitos aplicativos dependem de
dados XML, a Microsoft achou necessário incorporar um forte suporte para XML em seu mecanismo de banco de dados.
Como o SQL Server é um banco de dados tão popular, vale a pena aprender seus recursos XML. Além disso, seria
seria interessante ver como esses recursos podem ser consumidos a partir dos aplicativos construídos sobre o .NET Framework.
Neste capítulo, você aprenderá sobre o seguinte:

•Usando extensões XML para a instrução SELECT do SQL Server

•Usando classes gerenciadas SQLXML

•Trabalhando com o novo tipo de dados XML

Você deve observar, no entanto, que um exame extensivo de todos os recursos XML do SQL Server está fora de cogitação.
o escopo deste livro. A intenção aqui é familiarizá-lo com os recursos XML do SQL Server.

ÿ Observação Usei o SQL Server 2012 Developer Edition para desenvolver os exemplos deste capítulo. Muitos dos
exemplos devem funcionar com versões anteriores (como SQL Server 2010) do produto também, mas sugiro que você
instale as versões mais recentes para evitar incompatibilidades. Você também precisará instalar as classes gerenciadas
SQLXML. Você pode baixar o SQLXML do site oficial da Microsoft.

Usando extensões XML para a instrução SELECT


No SQL Server, você pode executar instruções SELECT que retornam os resultados no formato XML. No Capítulo 7, você
experimentou esse recurso ao usar o método ExecuteXmlReader() da classe SqlCommand. Agora é hora de examinar essas
extensões em detalhes.

A Cláusula FOR XML


Para buscar os dados do SQL Server da cláusula FOR XML no formato XML, você precisa usar a cláusula FOR XML com a
instrução SELECT. A cláusula FOR XML possui quatro modos que permitem retornar os resultados XML em diferentes formatos.
Os modos da cláusula FOR XML estão listados na Tabela 11-1.

© Bipin Joshi 2017 305


B. Joshi, Beginning XML with C# 7, https://doi.org/10.1007/978-1-4842-3105-0_11
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Tabela 11-1. Modos da cláusula FOR XML

Modo Descrição
AUTO Retorna os resultados da consulta SELECT como fragmentos XML. Por padrão, ele retorna os dados como elementos
XML. O nome do elemento XML é igual ao nome da tabela e os valores da coluna são retornados como atributos
XML. Você tem a opção de retornar todas as colunas como elementos em vez de atributos.

CRU Retorna os resultados como um elemento <row>. Os valores da coluna são retornados como atributos XML.

CAMINHO
Permite definir o aninhamento do XML retornado usando a sintaxe XPath simples.

EXPLÍCITO Define um esquema para os resultados retornados explicitamente na consulta SELECT.

Para testar esses modos da cláusula FOR XML, você executará algumas consultas SELECT no Northwind
base de dados. Para executar as consultas, você pode usar o SQL Server Management Studio ou o Server Explorer do Visual Studio
(mostrado na Figura 11-1).

Figura 11-1. O Server Explorer do Visual Studio com a janela Query

O modo AUTOMÁTICO

Abra o SQL Server Management Studio e emita a instrução SELECT mostrada na Listagem 11-1.

Listagem 11-1. Usando o modo AUTO da cláusula FOR XML

SELECIONE EMPLOYEEID,FIRSTNAME,LASTNAME FROM EMPLOYEES FOR XML AUTO

<EMPLOYEES EMPLOYEEID="1" FIRSTNAME="Nancy" LASTNAME="Davolio"/>


<EMPLOYEES EMPLOYEEID="2" FIRSTNAME="Andrew" LASTNAME="Fuller"/>

<EMPLOYEES EMPLOYEEID="3" FIRSTNAME="Janet" LASTNAME="Leverling"/>


....

306
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

A instrução SELECT da Listagem 11-1 seleciona três colunas — EmployeeID, FirstName e LastName — da tabela
Employees. A Listagem 11-1 também mostra os dados retornados no formato XML. Observe como o nome da tabela é usado para
os nomes dos elementos XML (<EMPLOYEES>) e os nomes das colunas são usados para o atributo
nomes.
Você notou algo sobre a capitalização de caracteres do XML retornado? Depende totalmente do nome da tabela e das
colunas usadas na instrução SELECT. Por exemplo, se você especificar nomes de coluna em maiúsculas, os atributos XML
estarão em maiúsculas. Talvez seja necessário ter isso em mente ao analisar os dados XML em seu aplicativo. Por padrão, o
modo AUTO retorna todos os valores de coluna como atributos XML. Se desejar, você pode retorná-los como elementos. Isso é
obtido usando a cláusula ELEMENTS com o modo AUTO. A Listagem 11-2 mostra como a cláusula ELEMENTS funciona.

Listagem 11-2. Usando a cláusula ELEMENTS do modo AUTO

SELECIONE EMPLOYEEID,FIRSTNAME,LASTNAME FROM EMPLOYEES FOR XML AUTO,ELEMENTS

<FUNCIONÁRIOS>
<EMPLOYEEID>1</EMPLOYEEID>
<FIRSTNAME>Nancy</FIRSTNAME>
<LASTNAME>Davolio</LASTNAME>
</EMPLOYEES>
....

Como você pode ver, especificamos a cláusula ELEMENTS após o modo AUTO. Observe como os valores da coluna são
retornaram como elementos desta vez. Os nomes dos elementos são iguais aos nomes das colunas.

ÿ Observação Os dados XML retornados pela cláusula FOR XML no código anterior não são bem formados por padrão.
Não inclui o elemento raiz. No entanto, como você verá mais tarde, você mesmo pode especificar o elemento raiz.

O modo RAW
O modo RAW da cláusula FOR XML retorna os dados XML como zero ou mais elementos XML. Por padrão, o nome dos
elementos é <linha>. Você pode alterar esse comportamento padrão especificando você mesmo um nome de elemento. Os
valores da coluna são retornados como atributos XML. A Listagem 11-3 mostra o uso do modo RAW.

Listagem 11-3. Usando o modo RAW da cláusula FOR XML

SELECT EmployeeID,FirstName,LastName FROM Employees FOR XML RAW

<row EmployeeID="1" FirstName="Nancy" LastName="Davolio"/> <row


EmployeeID="2" FirstName="Andrew" LastName="Fuller"/> <row EmployeeID="3"
FirstName="Janet" LastName="Leverling"/> <row EmployeeID="4" FirstName="Margaret"
LastName="Peacock"/>
....

Como você pode ver, a cláusula FOR XML é seguida pelo modo RAW. O XML retornado contém elementos <row> com
atributos contendo os valores da coluna. Se quiser alterar o nome do elemento padrão, você pode especificar seu próprio nome
de elemento, conforme mostrado na Listagem 11-4.

307
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Listagem 11-4. Atribuindo um nome de elemento personalizado à saída do modo RAW

SELECT EmployeeID,FirstName,LastName FROM Employees FOR XML RAW ('Employee')

<Employee EmployeeID="1" FirstName="Nancy" LastName="Davolio"/>


<Employee EmployeeID="2" FirstName="Andrew" LastName="Fuller"/>
<Employee EmployeeID="3" FirstName="Janet" LastName="Leverling"/>
<Employee EmployeeID="4" FirstName="Margaret" LastName="Peacock"/>
....

Como você pode ver, agora especificamos Employee como o nome do elemento entre parênteses. Este nome de elemento
é dado a todas as linhas retornadas.

Retornando o Esquema do XML


A cláusula XMLSCHEMA da cláusula FOR XML permite retornar o esquema XSD dos dados XML que estão sendo
retornados. Você pode usar esse esquema para validar seus dados ainda mais em seu aplicativo. A Listagem 11-5 mostra como
a cláusula XMLSCHEMA é usada.

Listagem 11-5. Retornando um esquema XML

SELECT EmployeeID,FirstName,LastName FROM Employees FOR XML AUTO, XMLSCHEMA

<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1"
xmlns:schema="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org /2001/
XMLSchema" xmlns:sqltypes="http://schemas.microsoft.com/sqlserver/2004/sqltypes"
elementFormDefault="qualified"> <xsd:import namespace="http://schemas.microsoft.com/sqlserver /
2004/sqltypes" schemaLocation="http://schemas.microsoft.com/sqlserver/2004/sqltypes/
sqltypes.xsd"/> <xsd:element name="Employees"> <xsd:complexType> <xsd:attribute name ="EmployeeID"
type="sqltypes:int" use="required"/> <xsd:attribute name="FirstName" use="required"> <xsd:simpleType><xsd:restriction
base="sqltypes:nvarchar" sqltypes :localeId="1033" sqltypes:sqlCompareOptions="IgnoreCase IgnoreKanaType
IgnoreWidth" sqltypes:sqlSortId="52"> <xsd:maxLength value="10"/> </xsd:restriction> </xsd:simpleType> </xsd:
attribute> <xsd:attribute name="LastName" use="required"> <xsd:simpleType> <xsd:restriction base="sqltypes:nvarchar"
sqltypes:localeId="1033" sqltypes: sqlCompareOptions="IgnoreCase IgnoreKanaType IgnoreWidth"
sqltypes:sqlSortId="52"> <xsd:maxLength value="20"/> </xsd:restriction> </xsd:simpleType> </xsd:attribute>

308
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

</xsd:complexType> </
xsd:element> </
xsd:schema>
<Employees xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" EmployeeID="1"
FirstName="Nancy" LastName="Davolio"/>
<Employees xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" EmployeeID="2"
FirstName="Andrew" LastName="Fuller"/>
....

Como você pode ver, a cláusula XMLSCHEMA retorna o esquema XML junto com os dados.

O modo PATH
Embora os modos AUTO e RAW retornem dados no formato XML, você tem muito pouco controle sobre as convenções de
aninhamento e nomenclatura dos dados retornados. O modo PATH, por outro lado, permite que você especifique a estrutura de
aninhamento, bem como os nomes de elementos e atributos usando a sintaxe XPath simples. Suponha que você queira recuperar
registros da tabela Employees no formato mostrado na Listagem 11-6.

Listagem 11-6. Aninhamento e nomeação personalizados

<ID do funcionário="1">
<Nome>
<FirstName>Nancy</FirstName>
<LastName>Davolio</LastName>
</Nome>
</Empregado>

Cada registro deve ser retornado como um elemento <Employee>. O valor da coluna EmployeeID deve ser
retornado como o atributo ID do elemento <Employee>. Deve haver um elemento chamado <Name> com dois subelementos:
<FirstName> e <LastName>. Os elementos <FirstName> e <LastName> devem conter
dados das colunas FirstName e LastName, respectivamente. Para recuperar dados XML neste formato, você pode usar o modo
PATH, conforme mostrado na Listagem 11-7.

Listagem 11-7. Usando o modo PATH da cláusula FOR XML

SELECT EmployeeID "@ID",FirstName "Name/FirstName",LastName "Name/LastName"


FROM Funcionários FOR XML PATH ('Funcionário')

Como você pode ver, a consulta SELECT agora especifica certas informações extras junto com os nomes das colunas.
Queremos retornar o valor da coluna EmployeeID como o atributo ID e, portanto, a consulta adiciona @ID após a coluna
EmployeeID. Da mesma forma, as colunas FirstName e LastName são seguidas pelo aninhamento desejado e pelos nomes dos
elementos, ou seja, Name/FirstName e Name/LastName, respectivamente. O nome do elemento gerado é especificado após o modo
PATH entre parênteses. Observe como a sintaxe XPath (@, /) é usada para especificar os atributos e o aninhamento de elementos.

O modo EXPLÍCITO
O modo EXPLICIT é possivelmente o modo mais confuso da cláusula FOR XML. Por um lado, aumenta a complexidade
da instrução SELECT, mas, por outro, fornece um controle muito mais refinado sobre a saída resultante.

309
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

ÿ Observação Discuto o modo EXPLICIT apenas para dar uma ideia de como ele renderiza a saída XML.
De forma alguma este livro tenta ensinar a você o modo EXPLÍCITO além do básico. Se você quiser aprender sobre
o modo EXPLICIT em detalhes, considere a leitura da documentação oficial do produto.

Suponha que você queira retornar o conteúdo XML conforme mostrado na Listagem 11-8.

Listagem 11-8. Saída XML personalizada usando o modo EXPLICIT

<Empregado ID="1">
<FirstName>Nancy</FirstName>
<LastName>Davolio</LastName>
</Empregado>

Você pode identificar dois níveis nesta marcação. O nível 1 consiste no elemento <Employee> e o nível 2 consiste nos
elementos <FirstName> e <LastName>. A coluna EmployeeID é gerada como o atributo EmpID do elemento <Employee> e,
portanto, pertence ao nível 1.
Ao usar o modo EXPLICIT para gerar essa saída XML, precisamos escrever duas consultas SELECT:

•A primeira consulta descreverá a estrutura, o aninhamento e os nomes dos elementos das várias colunas
envolvidas.

•A segunda consulta realmente buscará os dados. Os resultados das duas consultas serão
mesclado com uma cláusula UNION.

Vejamos a primeira consulta SELECT (consulte a Listagem 11-9).

Listagem 11-9. Definindo a estrutura da saída XML

SELECIONE

1 AS Tag,
NULL AS Parent,
EmployeeID AS [Employee!1!EmpID], FirstName
AS [Employee!1!FirstName!element], LastName AS [Employee!1!
LastName!element]
DE Funcionários

A consulta seleciona cinco colunas: 1, NULL, EmployeeID, FirstName e LastName. As três últimas colunas
são óbvios, mas o que são 1 e NULL? As colunas Tag e Parent são colunas implícitas na tabela resultante que são criadas
internamente pelo SQL Server:

•A coluna Tag especifica o nível de aninhamento do elemento atual. Um valor de Tag de 1 indica que esta
consulta está definindo a estrutura para o nível 1 da saída XML.

•A coluna Pai especifica o nível pai do tag atual. Um valor pai de


NULL indica que este é o elemento de nível superior.

Cada coluna especificada após a coluna Pai tem algumas especificações de metadados dentro de um quadrado
colchetes. Múltiplos pedaços de metadados são separados por um caractere de exclamação (!):

• A primeira parte indica o nome do elemento pai do elemento atual ou


atributo.

•A segunda parte indica o número do tag do elemento.

310
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

•A terceira parte indica o nome do elemento ou atributo atual.

• Se você especificar apenas essas três partes, o valor da coluna será gerado como um atributo. Para
especificar que deve ser gerado como um elemento, você deve especificar a quarta parte. A quarta parte é
uma palavra-chave predefinida chamada elemento.

Em nosso exemplo, o elemento de nível superior é <Employee>. Este nome de elemento de nível superior é decidido pelo
primeira coluna real na lista SELECT (no nosso caso, EmployeeID). O nome do elemento de nível superior é obtido da primeira
informação de metadados especificada após a coluna EmployeeID. Queremos gerar o valor da coluna EmployeeID como um atributo
chamado EmpID. Assim, o nível pai do atributo EmpID é tag 1.
Por fim, a terceira informação de metadados especifica que o nome do atributo é EmpID.
Os metadados para as colunas FirstName e LastName especificam que seu pai é o elemento de nível 1
e eles devem ser gerados como elementos <FirstName> e <LastName>, respectivamente.
A segunda consulta SELECT é mostrada na Listagem 11-10.

Listagem 11-10. Buscando os Dados para a Estrutura Definida na Consulta Anterior

SELECIONE

1,
NULO,
ID do Empregado,
Primeiro nome,
Sobrenome

DE Funcionários
ORDENAR POR

[Employee!1!EmpID],
[Employee!1!FirstName!element], [Employee!1!
LastName!element]
PARA XML EXPLÍCITO

A consulta seleciona dados para tag 1 e seleciona as colunas EmployeeID, FirstName e LastName. A cláusula ORDER BY indica a
sequência em que os elementos aparecerão no XML resultante. Finalmente, a consulta adiciona uma cláusula FOR XML EXPLICIT.

Agora que você entende as duas consultas, pode usar a cláusula UNION ALL, conforme mostrado na Listagem 11-11.

Listagem 11-11. Usando a cláusula UNION ALL

SELECT

1 AS Tag,
NULL AS Parent,
EmployeeID AS [Employee!1!EmpID], FirstName
AS [Employee!1!FirstName!element], LastName AS [Employee!1!
LastName!element]
FROM Empregados
UNION Todos
SELECT 1, NULL,
EmployeeID,
FirstName, LastName
FROM Employees
ORDER BY [Employee!

1!EmpID],

311
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

[Funcionário!1!Nome!elemento], [Funcionário!
1!Sobrenome!elemento]
PARA XML EXPLÍCITO

A cláusula UNION ALL combina os resultados dessas duas consultas e você obtém a saída XML mostrada na Listagem
11-8. Vamos um pouco mais longe e vamos supor que você deseja recuperar o XML no formato mostrado na Listagem 11-12.

Listagem 11-12. Saída XML com aninhamento mais profundo

<Funcionário empid="1">
<Nome>
<FName>Nancy</FName>
<LName>Davolio</LName>
</Nome>
</Empregado>

A saída XML tem mais um nível de aninhamento. Os elementos <FName> e <LName> estão incluídos no elemento <Name>,
que por sua vez está incluído no elemento <Employee>. A coluna EmployeeID é gerada como um atributo empid. As consultas
SELECT necessárias para gerar essa saída são fornecidas na Listagem 11-13.

Listagem 11-13. Consultas SELECT para geração de saída, conforme mostrado na Listagem 11-12

SELECIONE

1 AS Tag,
NULL AS Parent,
EmployeeID AS [employee!1!empid], FirstName
AS [Name!2!FName!element], LastName AS [Name!
2!LName!element]
DE Funcionários

UNIÃO TODOS

SELECT 2 AS Tag, 1
AS Parent, EmployeeID,
FirstName, LastName
FROM Employees
ORDER BY [Employee!
1!empid], [Name!2!
FName!element],
[Name!2!LName!element]

PARA XML EXPLÍCITO

A primeira instrução SELECT define a estrutura da saída XML resultante. Observe que, desta vez, as colunas FirstName e
LastName definem seu elemento pai como <Name> e têm um nível de tag de 2. Elas também definem nomes de elemento para as
colunas FirstName e LastName como <FName> e <LName>, respectivamente. A segunda consulta define a tag 2. Ela especifica que
o pai da tag 2 é a tag 1 por meio da coluna Parent. Ele ordena o conjunto de resultados usando a cláusula ORDER BY como antes.
Se você executar esse script no Management Studio, deverá ver a saída XML mostrada na Listagem 11-12.

312
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Especificando o nome do elemento raiz


Em todas as consultas anteriores, você obteve marcação XML para uma linha de tabela individual, mas não havia nenhum
elemento raiz especificado para a marcação. Se desejar, você pode especificar o elemento raiz adicionando a cláusula ROOT,
conforme mostrado na Listagem 11-14.

Listagem 11-14. Usando a Cláusula ROOT

SELECT EmployeeID,FirstName,LastName FROM Employees FOR XML AUTO, ROOT('MyRoot')

<MyRoot>
<Employees EmployeeID="1" FirstName="Nancy" LastName="Davolio"/>
<Employees EmployeeID="2" FirstName="Andrew" LastName="Fuller"/>
....
</MyRoot>

Como você pode ver, a cláusula ROOT é anexada ao final da consulta com o nome do elemento raiz em
parênteses. O XML retornado agora está agrupado dentro desse elemento raiz.

Usando OPENXML
Como você viu, a cláusula FOR XML do SQL Server permite que você recupere dados relacionais no formato XML.
No entanto, há outra maneira de fazer isso — a função OPENXML, que permite ler dados XML de maneira relacional.
Suponha que você tenha marcação XML que contém uma lista de funcionários e seu objetivo é importar essa lista para a tabela
Funcionários. Na ausência de algo como OPENXML, realizar essa tarefa seria tedioso. Como você verá em breve, a função
OPENXML torna seu trabalho muito mais fácil. A Listagem 11-15 mostra a marcação XML de origem contendo a listagem de
funcionários.

Listagem 11-15. A marcação XML de origem

<Funcionários>
<Employee EmployeeID="10" FirstName="John" LastName="Moore" />
<Employee EmployeeID="11" FirstName="Bill" LastName="Short" />
</Empregados>

Como você pode ver, o elemento raiz da marcação é <Employees>. Além disso, ele contém elementos <Employee>
que representam registros de funcionários. O EmployeeID, FirstName e LastName aparecem como atributos do elemento
<Employee>. Para ler qualquer marcação XML usando a função OPENXML, você precisa executar as seguintes etapas:

1. Prepare e carregue o documento XML para processamento.

2. Chame a função OPENXML conforme sua necessidade.

3. Remova o documento XML carregado da memória.

Essas três etapas são ilustradas na Listagem 11-16.

313
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Listagem 11-16. Usando a função OPENXML

SET IDENTITY_INSERT Empregados ON


DECLARE @hDoc INT
DECLARE @xml VARCHAR(1000)

SET @xml=
'<Funcionários>
<Employee EmployeeID="10" FirstName="John" LastName="Gates" />
<Employee EmployeeID="11" FirstName="Bill" LastName="Short" />
</Empregados>'

EXEC sp_xml:preparedocument @hDoc OUTPUT, @xml

INSERIR EM FUNCIONÁRIOS (EMPLOYEEID,FIRSTNAME,LASTNAME) (

SELECIONE DE
OPENXML(@hDoc,'Funcionários/ Funcionário',0)
WITH (EmployeeID int,FirstName varchar(50),LastName varchar(50)) )

EXEC sp_xml:removedocument @hDoc

O script na Listagem 11-16 declara duas variáveis denominadas hDoc e xml. A variável inteira hDoc é usada
posteriormente para armazenar um identificador para o documento XML carregado. A variável VARCHAR xml é usada para
armazenar a marcação XML mostrada na Listagem 11-15 como uma string. A instrução SET atribui a marcação XML à variável xml.
Em seguida, chamamos o procedimento armazenado do sistema sp_xml:preparedocument, que analisa e carrega a marcação
XML fornecida na memória. Ele retorna um identificador para o documento carregado na forma de um inteiro.
Em seguida, esse identificador é coletado na variável hDoc que declaramos anteriormente. Em seguida, uma instrução
INSERT é executada, fazendo uso da função OPENXML. Observe a chamada para OPENXML cuidadosamente. A função
OPENXML é usada em uma instrução SELECT como se fosse uma tabela. Aceita três parâmetros:

•O primeiro parâmetro é um identificador para os dados XML carregados usando sp_


xml:preparardocumento.

•O segundo parâmetro é um padrão XPath apontando para o nó dos dados XML que devem ser tratados
como uma linha. Em nosso exemplo, esse caminho base é Employees/Employee.

•O terceiro parâmetro é um sinalizador que indica o mapeamento entre os dados XML e o conjunto de
linhas relacional. O terceiro parâmetro pode assumir os valores mostrados na Tabela 11-2.

314
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Tabela 11-2. Mapeamento entre dados XML e um conjunto de linhas relacional

Descrição do valor do sinalizador

0 Especifica que os atributos dos elementos XML estão fornecendo valores de coluna para o conjunto de linhas
relacional. Este é o padrão.

1 Especifica que os atributos dos elementos XML estão fornecendo valores de coluna para o conjunto de linhas
relacional. Quando combinados com um valor de sinalizador de 2, os atributos são selecionados como valores de
coluna e, em seguida, os valores de elemento são atribuídos às colunas restantes.
2 Especifica que os elementos do XML de origem estão fornecendo valores de coluna para o relacional
conjunto de linhas.

8 Este sinalizador pode ser combinado com 1 ou 2 e indica que os dados consumidos não devem ser copiados
para a propriedade de estouro @mp:xmltext.

Além disso, a cláusula WITH de OPENXML especifica a estrutura do conjunto de linhas resultante. A estrutura pode
ser especificado como uma lista separada por vírgulas de nomes de colunas e seus tipos de dados. Em nosso exemplo, temos
três colunas: EmployeeID, FirstName e LastName. Observe que esses nomes de coluna são iguais aos nomes de atributo na
marcação XML de origem.
Assim, o conjunto de linhas retornado da instrução SELECT e OPENXML é alimentado na instrução INSERT. O
A instrução INSERT adiciona os dados à tabela Employees. Em nosso exemplo, ele adicionará duas linhas.
Após a conclusão da operação INSERT, o documento XML é removido da memória usando outro procedimento armazenado
do sistema: sp_xml:removedocument. Isso aceita o identificador de um documento XML carregado anteriormente usando
sp_xml:preparedocument e limpa a memória consumida pelo documento.
Chamar sp_xml:removedocument é muito importante porque deixar de fazer isso pode desperdiçar memória valiosa do seu aplicativo.

ÿ Nota Uma discussão completa da cláusula OPENXML está fora do escopo deste livro. O objetivo aqui é fornecer uma

compreensão básica da funcionalidade XML do SQL Server.

Usando recursos SQLXML


O SQLXML fornece um conjunto de classes gerenciadas, que você pode usar em seus aplicativos .NET para consultar o banco de
dados do SQL Server e ler os resultados retornados no formato XML. Você também pode enviar atualizações do aplicativo cliente em
formatos XML especiais e o SQL Server pode atualizar o banco de dados.

ÿ Observação Certifique-se de baixar e instalar o SQLXML 4.0 SP1 antes de continuar com os exemplos restantes deste

capítulo.

As classes gerenciadas SQLXML


O SQLXML fornece um conjunto de classes gerenciadas que podem ser usadas para executar consultas no banco de dados
e retornar resultados em formato XML. As classes fornecidas pelo SQLXML residem fisicamente em um assembly chamado
Microsoft.Data.SqlXml. As três classes principais expostas pelo SQLXML estão listadas na Tabela 11-3.

315
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Tabela 11-3. Classes gerenciadas SQLXML

Nome da classe Descrição


SqlXmlComando Permite executar consultas e não consultas no banco de dados. Essa classe expõe métodos
como ExecuteNonQuery(), ExecuteStream() e ExecuteXmlReader(). Essa classe é análoga à
classe ADO.NET SqlCommand.

SqlXmlParameter Representa parâmetros para as consultas executadas usando a classe SqlXmlCommand.


Essa classe é análoga à classe ADO.NET SqlParameter.

SqlXmlAdapterGenericName Usado para interagir com a classe ADO.NET DataSet. Essa classe é análoga à classe
ADO.NET SqlDataAdapter.

Todas as classes anteriores podem usar o provedor SQL Server OLEDB (SQLOLEDB) ou o SQL Native Client
para se comunicar com o banco de dados subjacente. Nas próximas seções, você aprenderá como as classes SQLXML podem
ser usadas em seus aplicativos .NET.

Executando consultas SELECT


Vamos começar desenvolvendo um aplicativo que permitirá executar consultas SELECT no banco de dados do SQL Server. A
interface do usuário do aplicativo é mostrada na Figura 11-2.

Figura 11-2. Aplicativo para execução de consultas SELECT via SqlXmlCommand

O aplicativo consiste em uma caixa de texto para inserir consultas SELECT. Observe que essas consultas SELECT devem
use algum modo da cláusula FOR XML que você aprendeu anteriormente. O botão Executar executa a consulta e exibe
os resultados em um controle de navegador da Web. O manipulador de eventos Click do botão Execute é mostrado na
Listagem 11-17.

316
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Listagem 11-17. Usando a classe SqlXmlCommand

private void button1_Click(remetente do objeto, EventArgs e) {

string strConn =
@"Provider=SQLOLEDB;servidor=.\sqlexpress;database=northwind;integrated ÿ
security=SSPI"; SqlXmlCommand cmd = new SqlXmlCommand(strConn); cmd.CommandText =
textBox1.Text; Stream stream= cmd.ExecuteStream(); Leitor StreamReader=novo
StreamReader(fluxo); Escritor StreamWriter =

File.CreateText($"{Application.StartupPath}\\sqlxmlresults.xml");
escritor.Write(leitor.ReadToEnd());
escritor.Close();
webBrowser1.Navigate($"{Application.StartupPath}\\sqlxmlresults.xml"); }

ÿ Observação Certifique-se de alterar a string de conexão do banco de dados para corresponder ao seu ambiente de desenvolvimento

antes de executar o código anterior.

O código declara uma variável de string para armazenar a string de conexão do banco de dados. Observe
o parâmetro Provider da string de conexão, que especifica o provedor SQLOLEDB. Em seguida, o código cria
uma instância da classe SqlXmlCommand passando a string de conexão em seu construtor. A propriedade
CommandText de SqlXmlCommand é definida como a consulta SELECT inserida na caixa de texto e a consulta é
executada chamando o método ExecuteStream() da classe SqlXmlCommand.
O método ExecuteStream() executa sua consulta e retorna um objeto Stream contendo os resultados XML.
Este Stream pode então ser usado ainda mais para ler os dados. No código anterior, o Stream é alimentado para
uma classe StreamReader. Poderíamos ter lido o Stream byte por byte, mas a classe StreamReader facilita nosso
trabalho.
O método CreateText() da classe File cria um novo arquivo XML no local especificado e retorna
um StreamWriter apontando para ele. O XML retornado do banco de dados é lido usando o método
ReadToEnd() da classe StreamReader e, em seguida, gravado no arquivo XML. Finalmente, o método Navigate() do
controle Web Browser é chamado para mostrar ao usuário o arquivo XML.
Existe uma maneira alternativa de fazer a mesma tarefa. Dê uma olhada na Listagem 11-18.

Listagem 11-18. Usando o método ExecuteToStream()

private void button1_Click(remetente do objeto, EventArgs e) { string


strConn =

@"Provider=SQLOLEDB;servidor=.\sqlexpress;database=northwind;integrado ÿ
security=SSPI";
SqlXmlCommand cmd = new SqlXmlCommand(strConn);
cmd.CommandText = textBox1.Text; StreamWriter writer =

File.CreateText($"{Application.StartupPath}\\sqlxmlresults.xml");
cmd.ExecuteToStream(writer.BaseStream); escritor.Close();
webBrowser1.Navigate($"{Application.StartupPath}\\sqlxmlresults.xml"); }

317
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

O código na Listagem 11-18 é muito semelhante ao da Listagem 11-17. A diferença é que ele chama o método
ExecuteToStream() em vez de ExecuteStream() e, ao fazer isso, emite a saída XML para um Stream existente. A
propriedade BaseStream da classe StreamWriter retorna o Stream subjacente, que é então fornecido ao método
ExecuteToStream().

ÿ Observação Você também pode usar o método ExecuteXmlReader() da classe SqlXmlCommand . Esse
método é idêntico ao método ExecuteXmlReader() da classe SqlCommand que você aprendeu no Capítulo 7.

Executando consultas SELECT parametrizadas


É bastante comum que suas consultas SELECT tenham alguns parâmetros, e a técnica para executar consultas
parametrizadas é semelhante ao ADO.NET. No entanto, existem algumas diferenças. Primeiro, um parâmetro é
representado pela classe SqlXmlParameter. Segundo, a classe SqlXmlCommand não tem uma coleção de parâmetros
como a classe SqlCommand, então você precisa chamar o método CreateParameter() da classe SqlXmlCommand
para criar um novo parâmetro que pertença ao comando. O valor do parâmetro pode então ser definido. Para ilustrar o
uso da classe SqlXmlParameter, criaremos um aplicativo como o mostrado na Figura 11-3.

Figura 11-3. Aplicativo para execução de consultas parametrizadas

O aplicativo permite que você busque detalhes de apenas um funcionário cujo EmployeeID esteja especificado na
caixa de texto. Os dados XML retornados são exibidos no controle Web Browser como antes. A Listagem 11-19 mostra
o manipulador de eventos Click do botão Execute.

Listagem 11-19. Usando a classe SqlXmlParameter

private void button1_Click(remetente do objeto, EventArgs e) { string


strConn =

@"Provider=SQLOLEDB;servidor=.\sqlexpress;database=northwind;integrated ÿ security=SSPI";

string sql = "SELECIONE id do funcionário, nome, sobrenome FROM funcionários


WHERE id do funcionário=? FOR XML AUTO,ROOT('MinhaRoot')";
SqlXmlCommand cmd = new SqlXmlCommand(strConn);
cmd.CommandText = sql;

318
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Parâmetro SqlXmlParameter = cmd.CreateParameter();


param.Value = textBox1.Text; Escritor StreamWriter =

File.CreateText($"{Application.StartupPath}\\sqlxmlresults.xml");
cmd.ExecuteToStream(writer.BaseStream); escritor.Close();
webBrowser1.Navigate($"{Application.StartupPath}\\sqlxmlresults.xml"); }

Examine a consulta SELECT cuidadosamente. Possui uma cláusula WHERE com um parâmetro
marcado com um ponto de interrogação (?). Além disso, o método CreateParameter() é chamado na classe
SqlXmlCommand. O método CreateParameter() cria e retorna um novo SqlXmlParameter. Você pode definir a
propriedade Value dessa classe SqlXmlParameter. Se sua consulta tiver mais de um parâmetro, você precisará
chamar o método CreateParameter() uma vez para cada parâmetro. Observe que a sequência de parâmetros na
consulta e a sequência na qual você cria objetos SqlXmlParameter devem ser iguais. Depois de criar o parâmetro
necessário, a saída XML é salva em um FileStream usando o método ExecuteToStream() de SqlXmlCommand.

Preenchendo um DataSet

Um DataSet é um dos objetos mais comumente usados para vinculação de dados e processamento desconectado. É
óbvio que o modelo de objeto SQLXML deve fornecer algum mecanismo para preencher objetos DataSet, e o SqlXmlAdapter
se encaixa no projeto. Ele permite que você preencha um DataSet e reflita as alterações feitas no DataSet no banco de
dados. Para ilustrar o uso de SqlXmlAdapter no preenchimento de um DataSet, você precisa criar um aplicativo como o
mostrado na Figura 11-4.

Figura 11-4. Aplicativo que preenche um DataSet usando SqlXmlAdapter

O aplicativo consiste em um controle DataGridView. Quando o formulário é carregado, um DataSet é preenchido com
todos os registros da tabela Employees e o DataSet resultante é vinculado ao controle DataGridView. O manipulador de
eventos Load que faz esse trabalho é mostrado na Listagem 11-20.

319
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Listagem 11-20. Preenchendo um DataSet com SqlXmlAdapter

private void Form1_Load(remetente do objeto, EventArgs e) {

string strConn =
@"Provider=SQLOLEDB;servidor=.\sqlexpress;database=northwind;integrated ÿ security=SSPI";

string sql = "SELECT employeeid,firstname,lastname FROM Employees FOR XML AUTO"; SqlXmlCommand
cmd = new SqlXmlCommand(strConn); cmd.CommandText = sql; DataSet ds = new DataSet(); SqlXmlAdapter
da = new SqlXmlAdapter(cmd); da.Fill(ds); dataGridView1.DataSource = ds.Tables[0].DefaultView; }

O código cria um objeto SqlXmlCommand como antes. Em seguida, ele cria uma nova instância do DataSet e
Classes SqlXmlAdapter. O SqlXmlAdapter aceita o objeto SqlXmlCommand como parâmetro e, portanto, a consulta
SELECT (ou procedimento armazenado) é passado para ele. O método Fill() de SqlXmlAdapter é então chamado passando
um objeto DataSet como parâmetro. O método Fill() preenche o DataSet com os resultados retornados da consulta. Por fim, o
DataSet é vinculado ao controle DataGridView.

Atualizando um DataSet usando SqlXmlAdapter No exemplo anterior,

simplesmente preenchemos um DataSet com a ajuda da classe SqlXmlAdapter. E se você fizer alterações nos dados do DataSet
e quiser salvar essas alterações no banco de dados? O SqlXmlAdapter fornece o método Update() que atualiza o banco de
dados com quaisquer alterações em seu DataSet. No entanto, você precisa fazer um pouco mais de trabalho do que isso. Ao
preencher o DataSet, você precisa especificar o XSD Schema para o DataTable que está sendo criado. Esse esquema fornece
mapeamento entre os nomes de coluna DataTable e os nomes de coluna da tabela real. Em nosso exemplo, recuperamos três
colunas da tabela Employee: EmployeeID, FirstName e LastName. O esquema para esta estrutura de dados é mostrado na
Listagem 11-21.

Listagem 11-21. Esquema—Employees.xsd—para nossos dados

<?xml version="1.0" encoding="utf-8" ?> <xs:schema


xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Employees">
<xs:complexType> <xs:sequence>
<xs:element name="EmployeeID"
type="xs:integer"/> <xs:element name="FirstName" type=" xs:string"/
> <xs:element name="LastName" type="xs:string"/> </xs:sequence>
</xs:complexType> </xs:element> </xs:schema>

O esquema define um elemento raiz chamado <Employees>, que possui três elementos filhos: <EmployeeID>, <FirstName>
e <LastName>. Observe que o esquema define as colunas como elementos e não como atributos.
Para ver a classe SqlXmlAdapter em ação, você precisa desenvolver um aplicativo conforme mostrado na Figura 11-5.

320
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Figura 11-5. Aplicativo para ilustrar o método Update() do SqlXmlAdapter

O aplicativo consiste em um controle DataGridView que exibe todos os funcionários da tabela Employees.
Você pode alterar os dados no DataGridView e clicar no botão Atualizar para salvar as alterações no banco de
dados. O código completo que faz esse aplicativo funcionar é mostrado na Listagem 11-22.

Listagem 11-22. Salvando alterações feitas em um conjunto de dados

DataSet ds = new DataSet();


SqlXmlAdapter da;
SqlXmlCommand
cmd; string strConn =
@"Provider=SQLOLEDB;servidor=.\sqlexpress;database=northwind;integrado ÿ
segurança=SSPI";

private void Form1_Load(remetente do objeto, EventArgs e)


{ cmd = new SqlXmlCommand(strConn); cmd.RootTag =
"RAIZ"; cmd.CommandText = "Empregados";
cmd.CommandType = SqlXmlCommandType.XPath;
cmd.SchemaPath = $"{Application.StartupPath}\
\employees.xsd"; ds = novo DataSet(); da = new
SqlXmlAdapter(cmd); da.Fill(ds); dataGridView1.DataSource =
ds.Tables[0].DefaultView; }

private void button1_Click(remetente do objeto, EventArgs e)


{ da.Update(ds); }

321
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

O código na Listagem 11-22 mostra várias coisas interessantes. As variáveis SqlXmlCommand, DataSet e
SqlXmlAdapter são declaradas no nível do formulário porque as usaremos em mais de um local. Observe o código
marcado em negrito. Ele define a propriedade RootTag da propriedade SqlXmlCommand. O modo AUTO da cláusula FOR
XML não retorna dados junto com um elemento raiz por padrão, então esta propriedade é usada para indicar o nome do
elemento raiz dentro do qual o resto dos dados XML serão agrupados.
A propriedade CommandType é definida como XPath, indicando que a propriedade CommandText é uma
expressão XPath. Isso significa que, desta vez, a propriedade CommandText não é uma consulta SELECT, mas a
expressão XPath Employees, que retornará vários elementos <Employees>.
A propriedade CommandType da classe SqlXmlCommand é do tipo SqlXmlCommandType. Os valores possíveis
da enumeração SqlXmlCommandType estão listados na Tabela 11-4.

Tabela 11-4. Valores da enumeração SqlXmlCommandType

Valor Descrição
DiffGram Indica que CommandText é um DiffGram

SQL Indica que CommandText é uma instrução SQL (padrão)

Modelo Indica que CommandText é um modelo

TemplateFile Indica que CommandText é um arquivo de modelo

UpdateGram Indica que CommandText é um UpdateGram


XPathName Indica que CommandText é uma expressão XPath válida

Além disso, a propriedade SchemaPath especifica o caminho do arquivo de esquema que criamos anteriormente. Em
seguida, o SqlXmlAdapter preenche um DataSet, que está vinculado ao DataGridView.
Depois que os dados são exibidos no DataGridView, você pode modificá-los. Depois de concluídas as modificações,
você precisa clicar no botão Atualizar. O evento Click do botão Update chama o método Update() do SqlXmlAdapter, que
aceita o DataSet cujas alterações devem ser refletidas no banco de dados. No Capítulo 7, você aprendeu que a classe
DataSet rastreia internamente as alterações feitas nos dados usando o formato DiffGram. O mesmo DiffGram é usado pela
classe SqlXmlAdapter para propagar as alterações de volta ao banco de dados.

Aplicando Modelos XSLT


No Capítulo 6, você aprendeu a aplicar folhas de estilo XSLT a dados XML. Você viu que XSLT permite transformar dados
XML de um formulário para outro. O mesmo conceito também pode ser aplicado em SQLXML, onde você pode querer aplicar
modelos XSLT a quaisquer dados recebidos em seu aplicativo cliente. Isso é feito usando a propriedade XslPath da classe
SqlXmlCommand. Para demonstrar o uso de XslPath, você precisa desenvolver um aplicativo como o mostrado na Figura
11-6.

322
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Figura 11-6. Aplicativo para ilustrar o uso da propriedade XslPath

O aplicativo consiste em um controle de navegador da Web. Quando o formulário é carregado, uma consulta SELECT é
executada usando SqlXmlCommand. Uma folha de estilo XSLT é então aplicada aos dados XML retornados para transformá-los em
HTML. O documento HTML resultante é então exibido no controle Web Browser.
Antes de escrever qualquer código, você deve criar uma folha de estilo XSLT chamada Employees.xslt, conforme mostrado em
Listagem 11-23.

Listagem 11-23. Marcação Employees.xslt

<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet


version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl :template match="/"> <html> <body> <h1>Lista
de funcionários</h1> <table border="1"> <tr> <th>ID do funcionário</th> <th>Nome</ th> <th>Sobrenome</th> </
tr> <xsl:for-each select="root/employees">

<tr>
<td>
<xsl:value-of select="@EmployeeID"/> </td> <td>
<xsl:value-of select="@FirstName"/> </td>

323
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

<td>
<xsl:value-of select="@LastName"/> </td> </
tr> </xsl:for-each> </table> </body> </html> </
xsl:template > </xsl:folha de estilo>

A folha de estilo itera por todos os elementos <Employees> e renderiza uma tabela HTML. A tabela HTML exibe os
valores de atributo em várias células. Observe que usaremos o modo AUTO da cláusula FOR XML, que retorna valores de
coluna como atributos XML. É por isso que a folha de estilo usa nomes de atributos (@ EmployeeID, @FirstName e
@LastName). O código que realmente executa a consulta SELECT e realiza a transformação é mostrado na Listagem 11-24.

Listagem 11-24. Aplicando uma folha de estilo XSLT

private void Form1_Load(remetente do objeto, EventArgs e) { string


strConn =

@"Provider=SQLOLEDB;servidor=.\sqlexpress;database=northwind;integrado ÿ
security=SSPI";
SqlXmlCommand cmd = new SqlXmlCommand(strConn);
cmd.CommandText = "SELECT EmployeeID,FirstName,LastName
FROM Employees FOR XML AUTO";
cmd.RootTag = "root"; cmd.XslPath = $"{Application.StartupPath }\
\employees.xslt"; Gravador StreamWriter =

File.CreateText($"{Application.StartupPath}\\sqlxmlresults.htm");
cmd.ExecuteToStream(writer.BaseStream); escritor.Close();
webBrowser1.Navigate($"{Application.StartupPath}\\sqlxmlresults.htm"); }

Observe o código marcado em negrito. Desta vez, a instrução SELECT não contém uma cláusula ROOT. Nós
poderia realmente tê-lo usado, mas o código consegue a mesma coisa com a ajuda da propriedade RootTag da classe
SqlXmlCommand. Lembre-se de que, na ausência de uma cláusula ROOT na consulta FOR XML, os dados XML retornados
não contêm um elemento raiz. A propriedade RootTag de SqlXmlCommand especifica o nome da marca raiz dentro da qual a
saída da consulta SELECT será agrupada.
A folha de estilo XSLT a ser usada para transformação é especificada por meio da propriedade XslPath da
classe SqlXmlCommand. Dessa forma, a classe SqlXmlCommand sabe qual folha de estilo aplicar aos dados XML
retornados. O restante do código deve ser familiar para você, conforme discutimos nos exemplos anteriores. Ele simplesmente
salva os dados XML transformados em um arquivo de disco e exibe esse arquivo no controle do navegador da Web.

324
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Escrever consultas de modelo


No exemplo anterior, especificamos a consulta SELECT diretamente no código. Existe uma alternativa para isso também:
você pode armazenar as consultas em um arquivo XML e especificar o caminho desse arquivo XML como o CommandText da
classe SqlXmlCommand. Esses arquivos XML são chamados de modelos XML. A estrutura desse arquivo XML pode ser vista na
Listagem 11-25.

Listagem 11-25. Criando um modelo XML

<?xml version="1.0" encoding="utf-8" ?> <ROOT


xmlns:sql="urn:schemas-microsoft-com:xml-sql"> <sql:header> <sql:param
name='EmpID '>1</sql:param> </sql:header> <sql:query> SELECT
EmployeeID,FirstName,LastName FROM Employees WHERE
employeeid>@Empid FOR XML AUTO </sql:query> </ROOT>

O elemento raiz <ROOT> é um elemento definido pelo usuário, mas o namespace urn:schemas-microsoft com:xml-
sql é necessário. O elemento <ROOT> contém uma seção opcional chamada <sql:header>, que é usada para definir
parâmetros usados por sua consulta (se houver). Cada parâmetro é especificado usando um elemento <sql:param>. O atributo
name do elemento <sql:param> indica o nome do parâmetro, enquanto o valor do parâmetro é armazenado nas tags
<sql:param> e </sql:param>. A consulta real é armazenada na seção <sql:query>. A consulta usa o parâmetro prefixando seu
nome com o símbolo @.
Para usar esse arquivo de modelo XML, você precisa criar um aplicativo como o mostrado na Figura 11-7.

Figura 11-7. Aplicativo que ilustra o uso de modelos XML

A aplicação consiste em um DataGridView que exibe todos os registros da tabela Employees. O


O evento Load do formulário contém todo o código necessário para usar o modelo XML (consulte a Listagem 11-26).

325
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Listagem 11-26. Usando modelos XML

private void Form1_Load(remetente do objeto, EventArgs e) {

string strConn =
@"Provider=SQLOLEDB;servidor=.\sqlexpress;database=northwind;integrated ÿ security=SSPI";
SqlXmlCommand cmd = new SqlXmlCommand(strConn); cmd.CommandType =
SqlXmlCommandType.TemplateFile; cmd.CommandText = $"{Application.StartupPath}\
\querytemplate.xml"; DataSet ds = new DataSet(); SqlXmlAdapter da = new SqlXmlAdapter(cmd);
da.Fill(ds); dataGridView1.DataSource = ds.Tables[0].DefaultView; }

Observe o código marcado em negrito. Desta vez, a propriedade CommandType da classe SqlXmlCommand é definida
como TemplateFile. Isso indica que a propriedade CommandText especificará o caminho do arquivo de modelo XML. Em
seguida, a propriedade CommandText é definida como o caminho do arquivo de modelo XML que acabamos de criar na Listagem 11-25.
A instância de SqlXmlAdapter é usada como antes para preencher um DataSet. O DataSet é finalmente vinculado ao controle
DataGridView.

Atualizando dados com DiffGrams


No Capítulo 7, você aprendeu que os objetos DataSet podem ser serializados como documentos XML. Ao serializar um
objeto DataSet, usamos uma enumeração XmlWriteMode para especificar como os dados devem ser gravados. Agora, uma
das opções do XmlWriteMode era o DiffGram, que persistia o conteúdo do DataSet no formato DiffGram. Imagine que você tem
um DiffGram contendo inserções, atualizações e exclusões e deseja salvar essas alterações de volta no banco de dados. Uma
maneira de fazer isso é usar DataSet e SqlXmlAdapter, que você já viu.
Existe outra técnica que também envolve a classe SqlXmlCommand. A classe SqlXmlCommand pode ser útil se você
tiver um DiffGram bruto que não seja necessariamente carregado em um DataSet. Para ilustrar o uso da classe SqlXmlCommand,
você precisa desenvolver um aplicativo como o mostrado na Figura 11-8.

Figura 11-8. Aplicativo para atualizar DiffGrams 326


Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

O aplicativo consiste em um controle DataGridView para exibir todos os registros da tabela Employees. Existem dois botões: Salvar
DiffGram e Atualizar DiffGram. O primeiro salva o conteúdo do DataSet em um arquivo de disco no formato DiffGram. O último botão lê o
DiffGram salvo anteriormente e atualiza o banco de dados.

O manipulador de eventos Load do formulário contém o código mostrado na Listagem 11-27.

Listagem 11-27. Preenchendo um DataSet

string strConn = @"Provider=SQLOLEDB;servidor=.; ÿ banco de


dados=northwind;id do usuário=sa;senha=sa"; DataSet ds = new DataSet();

private void Form1_Load(remetente do objeto, EventArgs e) { SqlXmlCommand


cmd = new SqlXmlCommand(strConn); cmd.RootTag = "RAIZ"; cmd.CommandText
= "Empregados"; cmd.CommandType = SqlXmlCommandType.XPath;
cmd.SchemaPath = $"{Application.StartupPath}\\employees.xsd";
SqlXmlAdapter da = new SqlXmlAdapter(cmd); da.Fill(ds);
dataGridView1.DataSource = ds.Tables[0].DefaultView; }

O código deve ser familiar para você, pois você o usou nos exemplos anteriores. Ele simplesmente preenche um DataSet
usando a classe SqlXmlAdapter. O DataSet atua como o DataSource para o controle DataGridView. O código que salva este DataSet como
um DiffGram vai no evento Click do botão Save DiffGram e é mostrado na Listagem 11-28.

Listagem 11-28. Salvando um DataSet como um DiffGram

private void button2_Click(remetente do objeto, EventArgs e) {

StreamWriter Writer=File.CreateText($"{Application.StartupPath}\\employees.xml"); ds.WriteXml(escritor,


XmlWriteMode.DiffGram); escritor.Close(); }

O código chama o método WriteXml() do DataSet para salvar seu conteúdo em um arquivo XML (Employees.xml).
O parâmetro XmlWriteMode de WriteXml() indica que o formato DiffGram deve ser usado durante a gravação dos dados. Este DiffGram é
executado no banco de dados quando você clica no botão Atualizar DiffGram. O manipulador de eventos Click do botão Update DiffGram é
mostrado na Listagem 11-29.

Listagem 11-29. Atualizando um DiffGram em um banco de dados

private void button1_Click(object sender, EventArgs e) { StreamReader reader =


File.OpenText($"{Application.StartupPath}\\employees.xml"); SqlXmlCommand cmd
= new SqlXmlCommand(strConn); cmd.CommandType = SqlXmlCommandType.DiffGram; cmd.CommandText =
leitor.ReadToEnd(); cmd.SchemaPath = $"{Application.StartupPath}\\employees.xsd";

327
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

cmd.ExecuteNonQuery();
MessageBox.Show("DiffGram atualizado para o banco de dados com sucesso!"); }

O código abre o arquivo Employees.xml em um objeto StreamReader. Em seguida, ele cria uma instância da classe
SqlXmlCommand e define a propriedade CommandType da instância SqlXmlCommand como DiffGram. É assim que você informa ao
SqlXmlCommand sobre sua intenção de atualizar um DiffGram. Quando o CommandType é DiffGram, a propriedade CommandText deve
conter o próprio DiffGram. O método ReadToEnd() de StreamReader lê o DiffGram completo e o atribui à propriedade CommandText.

Se você deseja atualizar o banco de dados usando o método DiffGram, deve especificar o SchemaPath
propriedade também. Nesse caso, o esquema é o mesmo que criamos na Listagem 11-21 anteriormente. Finalmente, o método
ExecuteNonQuery() do SqlXmlCommand é chamado para salvar todas as alterações no banco de dados. O método ExecuteNonQuery()
é usado para executar consultas que não retornam nada. Em nosso exemplo, simplesmente queríamos atualizar o DiffGram no banco de
dados e, portanto, usamos o método ExecuteNonQuery().

ÿ Observação Assim como DiffGram, o objeto SqlXmlCommand também permite atualizar UpdateGrams. O formato
UpdateGram é semelhante ao DiffGram, pois mantém as versões diferenciais dos dados. No entanto, a classe DataSet não
possui nenhum método para serializar a si mesma no formato UpdateGram. Você pode pensar no DiffGram como um subconjunto
do UpdateGram.

O tipo de dados XML


Até agora, você viu vários recursos do SQL Server que fornecem uma forte integração entre dados relacionais e XML. Mas isso não é tudo.
Esta seção fornece a próxima parcela dos recursos XML do SQL Server: o tipo de dados XML. Antes do SQL Server 2005, armazenar dados
XML em uma tabela basicamente significava que você tinha que usar uma coluna VARCHAR ou TEXT para os dados. Do ponto de vista do
armazenamento, tudo bem; mas do ponto de vista da manipulação de dados, era tedioso. Os dados XML foram tratados como qualquer outro
dado de texto. O novo tipo de dados XML introduzido no SQL Server 2005 é exclusivamente para armazenar documentos e fragmentos XML.

ÿ Observação Um documento XML é uma marcação que contém o elemento raiz, enquanto um fragmento XML é uma
marcação sem nenhum elemento raiz. Lembre-se que a cláusula FOR XML por padrão retorna fragmentos XML e não
documentos.

Além de armazenar dados XML, você também pode executar operações XQuery e instruções especiais de manipulação
de dados XML nos dados. Os dados XML podem ter um Esquema XSD anexado a eles para que as validações de dados possam ser
executadas. Você também pode indexar tabelas com base em uma coluna XML.

ÿ Observação XQuery é uma recomendação do W3C que lida com a consulta de documentos XML. Você pode pensar em XQuery
como SQL para dados XML. A sintaxe XQuery é baseada na sintaxe de expressão XPath.

Para começar, vamos ver como adicionar uma coluna do tipo XML a uma tabela do SQL Server.

328
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Criando uma tabela com uma coluna XML


Para ver como uma coluna do tipo XML pode ser adicionada a uma tabela do SQL Server, você criará uma nova tabela no banco
de dados Northwind chamada XMLDocs. A Figura 11-9 mostra a tabela XMLDocs no modo de design.

Figura 11-9. Criando uma tabela com uma coluna XML

A tabela XMLDocs consiste em duas colunas: Id e XmlData. A primeira coluna é a primária e está marcada como uma coluna de
identidade, e a última é do tipo XML.

Inserção, modificação e exclusão de dados XML A inserção, modificação ou exclusão de

dados XML é semelhante a qualquer outro tipo de dados. No entanto, há alguns pontos a serem lembrados. A Listagem 11-30 mostra como
usar as instruções INSERT e UPDATE em uma coluna do tipo XML.

Listagem 11-30. Inserindo e atualizando colunas XML

-- Aqui vai INSERIR


INSERIR EM xmldocs(xmldata)
VALORES(
'<Employee EmployeeID="1">
<FirstName>Nancy</FirstName>
<LastName>Davolio</LastName>
</Empregado>')

-- Aqui vai ATUALIZAÇÃO


ATUALIZAR xmldocs
SET xmldata='
<Employee EmployeeID="1">
<FirstName>Nancy</FirstName>
<LastName>Davolio</LastName>
</Empregado>'
ONDE Id=1

Como você pode ver, para um INSERT ou um UPDATE em uma coluna do tipo de dados XML, você pode usar dados XML no formato
de string. Você também pode declarar uma variável do tipo XML em seus scripts Transact-SQL (T-SQL), conforme mostrado na Listagem
11-31.

Listagem 11-31. Declarando uma Variável do Tipo XML

DECLARE @xmldata xml


SET @xmldata='
<Employee EmployeeID="2">
<FirstName>Nancy</FirstName>

329
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

<LastName> Davolio</LastName>
</Empregado>'

ATUALIZAR xmldocs
SET xmldata=@xmldata
ONDE Id=1

O script declara uma variável chamada xmldata do tipo XML e armazena algumas marcações XML nela. A variável xmldata
é então usada na instrução UPDATE. Se você deseja converter explicitamente um valor de string no tipo de dados XML, pode usar a função
CONVERT conforme mostrado na Listagem 11-32.

Listagem 11-32. Convertendo valores de string em um tipo de dados XML

DECLARE @xmldata VARCHAR(255)


SET @xmldata='
<Employee EmployeeID="2">
<FirstName>Nancy</FirstName>
<LastName> Davolio</LastName>
</Empregado>'

ATUALIZAR xmldocs
SET xmldata=CONVERT(xml,@xmldata,0)
ONDE Id=1

O primeiro parâmetro para a função CONVERT é o tipo de dados de destino. O segundo parâmetro são os dados de
origem a serem convertidos e o terceiro parâmetro é o estilo. O valor 0 indica que espaços em branco insignificantes serão descartados.
Você deve estar se perguntando: se os dados XML podem ser representados como uma string, por que desejaríamos usar variáveis
XML? A resposta é, usar o tipo de dados XML é recomendado porque o tipo de dados XML verifica se os dados XML estão bem formados.

Métodos do tipo de dados XML


O tipo de dados XML fornece alguns métodos para consultar colunas ou variáveis XML. Alguns desses métodos estão listados na Tabela
11-5.

Tabela 11-5. Métodos do tipo de dados XML

Método Descrição
consulta() Consulta uma coluna ou variável XML com base em alguma expressão XQuery e retorna os resultados da
consulta.

valor() Consulta uma coluna ou variável XML e retorna um valor escalar do tipo de dados SQL.

existe() Informa se a expressão XQuery fornecida retorna algum resultado.

modificar() Modifica o conteúdo de uma coluna ou variável de tipo de dados XML com a ajuda da linguagem de modificação
de dados XML, discutida posteriormente neste capítulo.

nós() Retorna dados XML como dados relacionais.

Alguns desses métodos são discutidos nas seções a seguir.

330
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Usando o Método query() O método

query() é usado para consultar dados XML usando expressões XQuery. A Listagem 11-33 ilustra como esse método é
usado.

Listagem 11-33. Usando o método query()

SELECT xmldata.query('/Employee[@EmployeeID=2]') FROM xmldocs

<Employee EmployeeID="2">
<FirstName>Nancy</FirstName>
<LastName> Davolio</LastName>
</Empregado>

A consulta SELECT usa o método query() na coluna XmlData. O método query() aceita uma expressão XQuery
válida e retorna os nós correspondentes. Em nosso exemplo, buscamos o elemento <Employee> cujo valor do atributo
EmployeeID é 1. Como você pode ver, a sintaxe XQuery é baseada na sintaxe XPath.

Usando o Método value() O método

value() aceita uma expressão XQuery e retorna um valor escalar (único). A Listagem 11-34 mostra o uso desse método.

Listagem 11-34. Usando o método value()

SELECT xmldata.value('(/Employee/@EmployeeID)[1]','int') FROM xmldocs WHERE id=1

-----------
1

O método value() aceita dois parâmetros:

•O primeiro parâmetro é um XQuery.

•O segundo parâmetro é o tipo de dados do SQL Server de destino.

Neste exemplo, estamos tentando selecionar o valor do atributo EmployeeID do primeiro funcionário. Observe que
a expressão /Employee/@EmployeeID retorna várias linhas e, portanto, especificamos o índice de linha a ser acessado
usando a notação de matriz. O valor escalar EmployeeID deve ser representado como um número inteiro e, portanto, o
segundo parâmetro é int. Observe que o nome do tipo de dados deve ser colocado entre aspas.

Usando o Método exist() O


método exist() verifica se há algum nó correspondente à expressão XQuery fornecida.
A Listagem 11-35 ilustra o uso do método exist().

Listagem 11-35. Usando o método exist()

SELECT xmldata.exist('/Employee[@EmployeeID=1]') FROM xmldocs

O método exist() retorna 1 se a expressão XQuery retornar pelo menos um nó, 0 se a expressão XQuery
A expressão retorna zero nós e NULL se a coluna XML for nula.
331
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Linguagem de modificação de dados XML (XML DML)


Como o SQL fornece instruções de manipulação de dados para dados relacionais, o XML DML do SQL Server
permite inserir, substituir e excluir dados de uma coluna XML. As instruções XML DML são usadas junto com o
método modify() mencionado na Tabela 11-5. A Listagem 11-36 mostra um script que insere, substitui e exclui dados
XML da coluna XmlData da tabela XMLDocs.

Listagem 11-36. Inserção, substituição e exclusão de conteúdo de uma coluna XML

-- Aqui vai inserir


UPDATE xmldocs SET
xmldata.modify (' insert
<Employee
EmployeeID="3"> <FirstName>Janet</
FirstName> <LastName>Leverling</
LastName> </Employee> after (/Employee)
[2] ')

-- Aqui vai substituir

UPDATE xmldocs
SET xmldata.modify ('
substitua o valor de (/
Employee/@EmployeeID)
[1] por "10"')

-- Aqui vai deletar

UPDATE xmldocs
SET xmldata.modify ('
delete (/
Employee[@EmployeeID=3]) ')

A primeira consulta UPDATE usa o método modify() da coluna XML para especificar um XML DML de inserção
instrução que insere um novo nó <Employee> no final da marcação existente. Observe esta sintaxe
cuidadosamente. Toda a instrução insert é colocada entre aspas e atua como um parâmetro para o método
modify(). A primeira expressão na instrução insert é a nova marcação XML a ser inserida seguida pela cláusula after.
Em nosso exemplo, queremos inserir o novo <Employee> após o segundo nó <Employee> e, portanto, especificamos
(/Employee)[2]. Você também pode usar as cláusulas before, as first e as last.
A segunda consulta UPDATE usa o método modify() junto com o valor de substituição de XML DML
declaração. O valor de substituição da instrução aceita duas expressões:

•O primeiro especifica a marcação a ser substituída. Em nosso exemplo, queremos substituir o


Atributo EmployeeID do primeiro nó <Employee>.

•A segunda expressão é o novo valor a ser substituído. Em nosso exemplo, queremos


atribua um valor de 10 ao atributo EmployeeID.

Por fim, a terceira instrução UPDATE usa o método modify() junto com a instrução delete XML DML. A
instrução delete usa a expressão com base na qual a marcação deve ser excluída. Em nosso exemplo, queremos
excluir um funcionário com EmployeeID igual a 3.

332
Machine Translated by Google

Capítulo 11 ÿ XML no SQL Server

Suporte XQuery no tipo de dados XML


Ao trabalhar com o tipo de dados XML, você viu que ele usa fortemente expressões XQuery. As expressões XQuery,
por sua vez, são baseadas na sintaxe XPath. No Capítulo 4, você foi apresentado às funções XPath. As especificações
XQuery suportam quase todas as funções que você aprendeu anteriormente.

Resumo
O SQL Server oferece forte integração com XML. Este capítulo apresentou muitos dos recursos XML do SQL Server, que
permitem exibir dados relacionais como XML. Isso é feito com a ajuda da cláusula FOR XML da instrução SELECT. Você
também pode examinar os dados XML de maneira relacional usando a função OPENXML.

Você também viu como o SQL Server facilita a recuperação de dados em um aplicativo cliente com a ajuda das
classes gerenciadas SQLXML. Essas classes permitem que você selecione dados como um fluxo, um XmlReader ou um DataSet.
Eles também permitem que você atualize UpdateGrams e DiffGrams no banco de dados. O tipo de dados XML do SQL Server
pode ser usado para armazenar documentos XML inteiros ou fragmentos e permite manipulá-los via XML DML.

333
Machine Translated by Google

CAPÍTULO 12

XML em .NET Framework

Até agora, você aprendeu a trabalhar com seus próprios dados XML. Isso inclui leitura, gravação, validação, serialização e consulta
de dados XML. A Microsoft tem usado XML extensivamente no .NET Framework.
Esse uso de XML vem em diferentes tipos, como marcação XAML de nota, marcação de controle de servidor de ASP.NET e sistema de
configuração do .NET Framework. Entender o uso de XML no .NET Framework é, portanto, essencial para qualquer desenvolvedor .NET.
Este capítulo apresenta muitos desses recursos.
Especificamente, você aprende sobre os seguintes tópicos:

•Usando XAML na definição das interfaces de usuário do WPF

•Usando controles de servidor ASP.NET para definir a interface do usuário da web

• Compreender os controles do servidor ASP.NET, como XML, fonte de dados XML, TreeView, Menu e
SiteMap

•Usando o sistema de configuração do .NET Framework

•Entendendo a documentação XML

Observe que, embora este capítulo abranja uma ampla gama de tópicos, de forma alguma ele fornece uma descrição exaustiva
tratamento a esses temas. O foco aqui é apontar brevemente o uso de XML em várias áreas do .NET Framework.

Usando XAML para definir a interface de usuário do WPF


Você desenvolveu aplicativos Windows Forms ao longo deste livro. Como você sabe, um Windows Forms são classes que contêm todo o
código necessário para exibir o formulário e os manipuladores de eventos. Assim, duas preocupações — interface do usuário e
funcionamento (manipuladores de eventos) — são misturadas. Uma abordagem de programação melhor seria separar essas duas
preocupações em suas próprias unidades.
Os aplicativos Windows Presentation Foundation (WPF) fazem exatamente isso. WPF usa um vocabulário especial de XML
chamado XAML (Extensible Application Markup Language) para definir uma interface de usuário. A marcação XAML normalmente reside
em arquivos com uma extensão .xaml. O código responsável pelo funcionamento do aplicativo (lógica de tempo de execução, como
manipuladores de eventos e métodos) está alojado em uma classe C#. Quando você projeta uma interface de usuário de um aplicativo
Windows Forms, a interface do usuário é convertida em código C#. Esse não é o caso dos aplicativos WPF. Quando você projeta uma
interface de usuário do WPF no Visual Studio, a interface do usuário é convertida em marcação XAML (e não em código C#).

A marcação XAML é uma marcação XML e, portanto, segue todas as regras da gramática XML. Você pode criar/
edite uma marcação XAML em qualquer editor XML/texto, mas o Visual Studio fornece um editor avançado para trabalhar com ela.
Apenas para entender a aparência da marcação XAML, você criará um aplicativo WPF conforme mostrado na Figura 12-1.

© Bipin Joshi 2017 335


B. Joshi, Beginning XML with C# 7, https://doi.org/10.1007/978-1-4842-3105-0_12
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Figura 12-1. Um aplicativo WPF para gerenciar Employees.xml

Este aplicativo deve parecer familiar para você porque você desenvolveu um aplicativo Windows Forms
semelhante no Capítulo 2. No que diz respeito à funcionalidade do aplicativo, é exatamente o mesmo — adicionar, atualizar
e excluir funcionários do arquivo Employees.xml. Não vamos discutir a funcionalidade novamente para economizar espaço.
Nosso principal interesse aqui é a marcação XAML. Você pode obter o código-fonte completo deste aplicativo no download do
código-fonte deste livro.
Para desenvolver esse aplicativo, crie um novo projeto de aplicativo WPF usando o Visual Studio (consulte a Figura 12-2).

Figura 12-2. Criando um novo projeto de aplicativo WPF

336
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Assim que o projeto for criado, você notará dois arquivos .xaml nele chamados App.xaml e MainWindow.xaml.
Nosso interesse é o arquivo MainWindow.xaml, pois ele define a interface do usuário de nosso aplicativo. Você pode adicionar janelas WPF
usando a caixa de diálogo Adicionar novo item.
Você pode abrir o arquivo MainWindow.xaml no Visual Studio Designer (veja a Figura 12-3) e editá-lo de duas maneiras:

•Arraste e solte os controles WPF da caixa de ferramentas na superfície de design e defina suas propriedades. Fazer
isso irá gerar certa marcação XAML para você.

• Edite manualmente o arquivo .xaml e adicione a marcação (consistindo principalmente em determinados


elementos e seus atributos) nele diretamente. A superfície de design também refletirá as alterações relevantes.

O arquivo MainWindow.xaml pronto para edição é mostrado na Figura 12-3. A Figura 12-4 mostra a caixa de ferramentas com
Controles do WPF.

Figura 12-3. MainWindow.xaml no Visual Studio Designer

337
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Figura 12-4. Caixa de ferramentas mostrando os controles do WPF

Você pode tentar brincar com o designer apenas para obter alguma familiaridade. E tente projetar uma interface de
usuário do aplicativo conforme mostrado anteriormente. Não entraremos em detalhes sobre o processo de design da IU
aqui. A marcação XAML responsável pela interface do usuário é mostrada na Listagem 12-1 (para economizar espaço,
parte da marcação foi omitida da listagem).

Listagem 12-1. Marcação XAML responsável pela interface do usuário

<Window x:Class="WpfAndXaml.MainWindow"
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:local="clr-namespace:WpfAndXaml" mc:Ignorable=" d"

Title="Window1" Height="400.2" Width="433.431" Loaded="Window_Loaded">


....
<Grid HorizontalAlignment="Center" Height="367" VerticalAlignment="Center"
Width="396" Margin="10,10,9,10"> <Label
Content="ID do funcionário:" HorizontalAlignment="Left" Margin="13,22,0,0"
VerticalAlignment="Top" RenderTransformOrigin="-0.575,0.017"/> <Label
Content="First Name:" HorizontalAlignment="Left" Margin="23,53,0,0"
VerticalAlignment="Top" RenderTransformOrigin="-0.575,0.017"/> <Label
Content="Sobrenome:" HorizontalAlignment="Left" Margin="24,84,0,0"
VerticalAlignment="Top" RenderTransformOrigin="-0.575,0.017"/> <Label
Content="Telefone Residencial:" HorizontalAlignment="Left" Margin="11,115,0,0"
VerticalAlignment="Top" RenderTransformOrigin="-0.575,0.017"/> <Label
Content="Notas:" HorizontalAlignment="Left" Margin="49,146,0,0"
VerticalAlignment="Top" RenderTransformOrigin="-0.575,0.017"/> <ComboBox
x:Name="comboBox1" HorizontalAlignment="Left" Margin="106,26,0,0"
VerticalAlignment="Top" Width="143"/> <TextBox
x:Name="textBox1" HorizontalAlignment="Left" Height="23"
Margin="106,57,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top"
Width="219" RenderTransformOrigin="0.439,-0.159"/>

338
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

<TextBox x:Name="textBox2" HorizontalAlignment="Left" Height="23"


TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="219"
Margem="106,88,0,0"/>
<TextBox x:Name="textBox3" HorizontalAlignment="Left" Height="23"
TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="143"
Margem="106,119,0,0"/>
<TextBox x:Name="textBox4" HorizontalAlignment="Left" Height="88"
TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="219"
Margem="106,150,0,0"/>
<Button x:Name="button1" Content="Add" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="75" Margin="49,262,0,0" Click="button1_Click"
Altura="24"/>
<Button x:Name="button2" Content="Update" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="75" Margin="163,262,0,0" Click="button2_Click"
Altura="24"/>
<Button x:Name="button3" Content="Delete" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="75" Margin="279,262,0,0" Click="button3_Click"
Altura="24"/>
.... ....
</Grid>
</Grid>
</Janela>

Embora não vamos entender a marcação do ponto de vista do WPF, vamos inspecioná-la rapidamente do ponto
de vista do XML.
A marcação tem o elemento raiz Window indicando que é uma janela. Existem certos namespaces usados pelos
aplicativos WPF. Vale a pena conhecer dois deles porque você os encontrará em todos os documentos WPF XAML:

• http://schemas.microsoft.com/winfx/2006/xaml/presentation é o principal namespace do WPF. Ele


contém todas as classes WPF, incluindo os controles usados nas interfaces do usuário. Como
você pode ver, é o namespace padrão para todo o documento XAML.

• http://schemas.microsoft.com/winfx/2006/xaml é o namespace XAML. Ele tem o prefixo de


namespace de x. Ele inclui vários recursos XAML necessários para todos os aplicativos.

O atributo x:Class especifica que uma classe C# chamada MainWindow conterá o código, como manipuladores
de eventos. A classe MainWindow herda da classe Window (indicada pelo elemento <Window>). Essa classe geralmente
é chamada de classe code-behind.
A seção <Grid> atua como um esquema de layout para o contêiner dos controles WPF alojados na janela.
Dentro, existem elementos de marcação como <ComboBox>, <Label>, <TextBox> e <Button> que representam os
respectivos elementos da interface do usuário. Cada um dos elementos tem seu próprio conjunto de atributos (como
Content, Text, HorizontalAlignment, VerticalAlignment e Margin).
Na verdade, esses elementos são mapeados para determinadas classes definidas pela estrutura do WPF e
geralmente são chamados de tipos de apoio. Por exemplo, o elemento <Button> é mapeado para a classe
System.Windows.Controls.Button. Os atributos Margin e FontSize do elemento <Button> correspondem às propriedades
Margin e FontSize da classe Button, respectivamente.
Até agora tudo bem. Agora, vamos ver como adicionar manipuladores de eventos aos botões. Clique duas vezes em
qualquer um dos controles Button para abrir a classe MainWindow (o arquivo MainWindow.xaml.cs) e o manipulador de eventos
Click desse botão. O manipulador de eventos Click do botão1 (Adicionar) é mostrado na Listagem 12-2.

339
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Listagem 12-2. Clique no manipulador de eventos do controle de botão

classe parcial pública MainWindow : Janela {

public MainWindow() {

InitializeComponent();
}

private void button1_Click(object sender, RoutedEventArgs e) { //seu código aqui }

O método button1_Click() representa o manipulador de eventos Click do botão. Depois de gerar esse manipulador de eventos
vazio, examine a marcação XAML novamente.

<Botão x:Nome="botão1"
Conteúdo="Adicionar"

HorizontalAlignment="Esquerda"
VerticalAlignment="Topo"
Largura="75"
Margem="49,262,0,0"
Click="button1_Click"
Altura="24"/>

Como você pode ver, o atributo Click foi adicionado ao elemento <Button> que aponta para o Click
manipulador de eventos.

Como mencionado, não discutiremos o código responsável pelo funcionamento deste aplicativo, pois ele é bastante semelhante
ao exemplo que você desenvolveu no Capítulo 2. Você pode obter o código-fonte completo no download do código-fonte do livro.
Você pode até copiar e colar o código do manipulador de eventos em seu projeto para fazer o aplicativo funcionar conforme o esperado.

Quando você compila o aplicativo, a marcação XAML é convertida em um tipo CLR e mesclada com a classe code-behind
(observe que a classe code-behind é uma classe parcial) para formar uma única unidade de código. Agora você pode executar o
aplicativo e ver se funciona conforme o esperado.

Exibindo dados XML em um aplicativo WPF


Na seção anterior, você aprendeu que o WPF usa XAML para definir seus elementos de interface do usuário. No entanto, seria mais
interessante ver como os dados XML podem ser exibidos em aplicativos WPF. Para ver como isso é feito, crie outro aplicativo WPF,
conforme mostrado na Figura 12-5.

340
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Figura 12-5. Dados XML do funcionário exibidos em uma grade

A interface do usuário do aplicativo consiste em um DataGrid que exibe dados XML do arquivo Employees.xml. O DataGrid
tem cinco colunas que exibem EmployeeID, FirstName, LastName, HomePhone e Notes.
Para desenvolver este aplicativo, crie um novo projeto WPF como antes e arraste e solte um controle Grid nele.
Em seguida, configure um recurso para a grade (não confundir com DataGrid), conforme mostrado na Listagem 12-3.

Listagem 12-3. Configurando XmlDataProvider

<Grid.Resources>
<XmlDataProvider x:Key="EmployeeData"
Origem="Empregados.xml"
XPath="/employees/employee"/> </
Grid.Resources>

Um recurso é um objeto que você deseja usar frequentemente em um aplicativo. Aqui você configura
XmlDataProvider como um recurso. Um objeto XmlDataProvider pode apontar para um arquivo XML ou conter dados XML
embutidos. Em nosso exemplo, os dados do funcionário são armazenados no arquivo Employees.xml e, portanto, a propriedade
Source do XmlDataProvider é definida como Employees.xml. A chave especifica um nome pelo qual um recurso é identificado,
EmployeeData neste caso. A propriedade XPath especifica uma consulta XPath que nos fornece os dados desejados. Nesse
caso, queremos preencher as linhas da grade com dados dos nós <employee>. Portanto, a consulta XPath é definida como /
Employees/employee.
Arraste e solte um controle DataGrid da caixa de ferramentas e altere seu XAML (você pode fazer isso usando o painel
Editor XAML do designer) para se parecer com a Listagem 12-4.

Listagem 12-4. Colunas DataGrid são vinculadas a dados XML

<DataGrid x:Name="grid1" DataContext="{StaticResource EmployeeData}"


ItemsSource="{Binding XPath=/employees/employee}"
AutoGenerateColumns="False" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<DataGrid.Columns>
<DataGridTextColumn Header="EmployeeID"
Binding="{Binding XPath=@employeeid}" />
<DataGridTextColumn Header="FirstName"
Binding="{Binding XPath=firstname}" />
<DataGridTextColumn Header="LastName"
Binding="{Binding XPath=lastname}" />
<DataGridTextColumn Header="HomePhone"
Binding="{Binding XPath=homephone}" />

341
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

<DataGridTextColumn Header="Notas"
Binding="{Binding XPath=notas}" />
</DataGrid.Columns> </
DataGrid>

A propriedade DataContext do DataGrid é definida como XmlDataProvider (recurso estático significa simplesmente que
o recurso em consideração é resolvido no momento do carregamento e não no tempo de execução).
Observe também que o DataGrid tem cinco colunas conforme definido pelos elementos DataGridTextColumn. A coluna
EmployeeID é vinculada ao atributo employeeid dos elementos <employee> e, portanto, sua propriedade de vinculação é
configurada para usar a consulta XPath @employeeid. Da mesma forma, as outras quatro colunas são dados vinculados aos
elementos firstname, lastname, homephone e notes.

ÿ Observação A sintaxe de vinculação de dados usada pelo WPF pode parecer um pouco estranha se você nunca trabalhou com o WPF antes.

Embora não entremos nos detalhes dessa sintaxe neste livro, você pode ler a documentação do MSDN para saber mais.

Depois que a marcação XAML estiver pronta, execute o aplicativo e veja se o DataGrid está preenchido com os
dados do funcionário.

Usando XML em ASP.NET


ASP.NET é uma estrutura para criar aplicativos da Web dinâmicos e orientados a dados. No ASP.NET, as páginas da Web são
chamadas de Web Forms. Os formulários da Web usam a extensão .aspx e contêm marcação HTML, marcação de controle de
servidor e, opcionalmente, código. Cada Web Form é uma classe que herda direta ou indiretamente do System.Web.
Classe base UI.Page.

ÿ Observação Seus aplicativos ASP.NET podem usar Web Forms ou MVC. Você também pode usar a nova estrutura - ASP.

NET Core — para criar aplicativos da Web. A discussão e os exemplos discutidos nas seções a seguir usam ASP.NET Web Forms. Meu

objetivo é apenas mostrar como o ASP.NET usa XML e a sintaxe baseada em XML em vários lugares. Então, quando digo ASP.NET, quero

dizer ASP.NET Web Forms.

Os controles de servidor são wrappers orientados a objetos sobre os elementos HTML tradicionais. Eles são processados
pelo ASP.NET no lado do servidor, por isso são chamados de controles de servidor. Eles fornecem muitas vantagens sobre os
controles HTML tradicionais, incluindo funcionalidade avançada, vinculação de dados, recursos orientados a objetos e muitos
outros. Um site ASP.NET é uma coleção de formulários da Web e recursos relacionados, como imagens, arquivos JavaScript e
componentes compilados.

342
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

XML e ASP.NET
ASP.NET usa XML em vários lugares. Algumas das principais áreas onde o XML é usado extensivamente são as seguintes:

•Marcação de controle do servidor

• Controle de fonte de dados XML

• Controles de navegação, como TreeView, Menu e SiteMap

•Controle de XML

•Configuração do site

Você aprenderá sobre todos esses recursos nas seções a seguir.

Marcação de controle do servidor

Conforme mencionado anteriormente, os formulários da Web do ASP.NET consistem principalmente em marcação de controle de
servidor e HTML. A marcação de controle do servidor é, na verdade, um vocabulário especial de XML. Cada controle de servidor tem
um nome de marca predefinido, prefixo de marca e atributos. Alguns controles de servidor são elementos vazios (eles não contêm
nenhum elemento filho), enquanto outros podem conter marcação ou texto.
Para entender melhor os controles do servidor, você desenvolverá um site ASP.NET com um formulário da Web. O formulário
da Web representa uma página típica de Fale Conosco. A página será usada pelos usuários finais para entrar em contato com você em
caso de dúvidas, feedback ou comentários sobre seu site.

Criando um site
Para criar um novo site usando o Visual Studio, você precisa escolher a opção de menu File ÿ New ÿ Web Site.
Esta opção abre a caixa de diálogo New Web Site mostrada na Figura 12-6.

Figura 12-6. Criando um novo site

343
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

O modelo ASP.NET Web Forms Site permite que você crie um site. A lista suspensa do local da Web indica o local de destino
onde o site da Web será criado. Possíveis locais são sistema de arquivos, servidor web (HTTP) e pasta FTP (FTP). Em nosso
exemplo, escolheremos o sistema de arquivos. O caminho especificado após o menu suspenso Localização é a pasta de destino onde o
site será criado. Certifique-se de selecionar Visual C# como o idioma. Clique no botão OK para criar o site. O Visual Studio criará o site
com uma determinada estrutura de pastas e também haverá um formulário da Web chamado Default.aspx.

ÿ Nota Na maioria dos nossos exemplos, não precisamos de vários itens criados pelo modelo de projeto padrão. Você pode
simplesmente removê-los do projeto para reduzir a desordem. Você também pode usar o modelo de projeto de site vazio
do ASP.NET para criar um projeto vazio e adicionar os itens conforme necessário.

Projetando o formulário da Web


A próxima tarefa é criar o Formulário da Web Fale Conosco. Para fazer isso, mude para a visualização Design, escolha Table ÿ Insert
Table no menu e insira uma tabela com 11 linhas e 2 colunas no formulário da Web padrão. Agora você precisa arrastar e soltar vários
controles e organizá-los nas células desta tabela, conforme detalhado nas etapas a seguir. A Figura 12-7 mostra Default.aspx após a
conclusão do projeto.

Figura 12-7. Projetando um formulário da Web

344
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

O Web Form consiste em muitos controles de servidor, como Label, TextBox, DropDownList, RadioButtonList,
Button, RequiredFieldValidator, RegularExpressionValidator e ValidationSummary.
Para começar a criar o formulário da Web, use as seguintes etapas:

1. Arraste e solte um controle Label na primeira linha da tabela. Defina sua propriedade Text como
Contate-nos. Defina sua propriedade Font para sua escolha. Além disso, arraste e solte um controle
HTML de regra horizontal abaixo do rótulo Fale conosco.

2. Arraste e solte um controle ValidationSummary na segunda linha. O


O controle ValidationSummary está disponível no nó Validação da caixa de ferramentas e é usado
para exibir uma lista consolidada de erros de validação do formulário da Web atual.

3. Arraste e solte sete controles Label nas linhas 3 a 9. Defina suas propriedades Text como Your
Name :, Your Email :, Contacting For :, Subject :, Message :, Web Site (Opcional) :, e You
represent :, respectivamente .

4. Arraste e solte um controle TextBox depois de Your Name, Your Email, Subject,
Rótulos de mensagem e site da Web (opcional), respectivamente. Defina a propriedade TextMode
da caixa de texto Message como MultiLine.

5. Arraste e solte um controle DropDownList após o rótulo Contacting For.

6. Arraste e solte um RadioButtonList após o rótulo You Represent.

7. Arraste e solte um controle Button na linha 10 e defina sua propriedade Text como Submit.

8. Arraste e solte um controle Label na linha 11 e defina sua propriedade Text como uma string em branco.
Este rótulo será usado para exibir uma mensagem de sucesso para o usuário.

9. Selecione o controle DropDownList. Abra sua janela de propriedades e localize a propriedade Items.
Adicione quatro itens na lista suspensa: selecione, cotação de vendas, problema técnico e outros.

10. Selecione o controle RadioButtonList. Abra sua janela de propriedades e localize a propriedade
Items. Adicione dois itens no RadioButtonList: Individual e Company.

11. Arraste e solte um controle RequiredFieldValidator na frente dos controles


que aceitam nome, e-mail, motivo do contato, assunto e mensagem. O controle
RequiredFieldValidator é usado para validar se o controle ao qual foi anexado contém algum valor.
Defina a propriedade ControlToValidate de todos os controles RequiredFieldValidator para a
propriedade ID das respectivas caixas de texto ou DropDownList.

12. Defina a propriedade InitialValue do RequiredFieldValidator anexado ao menu suspenso


Contacting For para Please Select. Dessa forma, o usuário precisará escolher uma opção
diferente de Please Select.

345
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

13. Arraste e solte um controle RegularExpressionValidator na frente dos controles que


aceitam um e-mail e um site. O controle RegularExpressionValidator valida o valor
inserido para um padrão específico. O padrão é definido usando a sintaxe de
expressão regular. Defina a propriedade ControlToValidate de ambos os controles
RegularExpressionValidator para a propriedade ID das respectivas caixas de
texto. Defina a propriedade ValidationExpression do primeiro controle
RegularExpressionValidator como Internet Email Address. Da mesma forma, defina a
propriedade ValidationExpression do segundo controle RegularExpressionValidator
como URL da Internet.

14. Defina a propriedade Text de todos os controles de validação (RequiredFieldValidator e


Regular-ExpressionValidator) como *. Este texto será exibido no lugar do controle de
validação sempre que houver um erro de validação. Além disso, defina a propriedade
ErrorMessage de todos os controles de validação para alguma mensagem de erro
significativa. A ErrorMessage será exibida no controle ValidationSummary caso ocorra
algum erro de validação.

Nosso design de formulário da Web acabou. Na parte inferior do Web Form Designer, você encontrará uma guia
chamada Source. Clique nele para ver a marcação gerada para o nosso Web Form. A marcação relevante de Default.aspx
é mostrada na Listagem 12-5.

Listagem 12-5. Marcação de controle de servidor de Default.aspx

<%@ Idioma da página="C#" AutoEventWireup="true"


CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html>

...
<form id="form1" runat="servidor">
...
<asp:Label ID="Label1" runat="servidor" Font-Names="Arial"
Font-Size="X-Large" Text="Fale Conosco">
</asp:Label>
...
<asp:ValidationSummary ID="ValidationSummary1" runat="servidor" />
...
<asp:Label ID="Label2" runat="server" Text="Seu nome:"></asp:Label>
...
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator
ID="RequiredFieldValidator1"
runat="servidor" ControlToValidate="TextBox1"
Display="Dinâmico"
ErrorMessage="Por favor, digite seu nome">*
</asp:RequiredFieldValidator>
...
<asp:Label ID="Label3" runat="server" Text="Seu e-mail :"></asp:Label>
...
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator
ID="RequiredFieldValidator2" runat="servidor"
ControlToValidate="TextBox2"

346
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Display="Dinâmico"
ErrorMessage="Por favor, digite seu e-mail">*
</asp:RequiredFieldValidator>
<asp:RegularExpressionValidator
ID="RegularExpressionValidator1"
runat="server"
ControlToValidate="TextBox2"
Display="Dinâmico"
ErrorMessage="Por favor, digite um endereço de e-mail válido"
ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*">*
</asp:RegularExpressionValidator></td>
...
<asp:Label ID="Label4" runat="server" Text="Contactando para:"></asp:Label>
...
<asp:DropDownList ID="DropDownList1" runat="server">
<asp:ListItem Value=" Selecione ">Selecione</asp:ListItem> <asp:ListItem
Value=" Cotação de vendas ">Cotação de vendas</asp :ListItem> <asp:ListItem
Value=" Problema técnico ">Problema técnico</asp:ListItem> <asp:ListItem Value="
Other ">Outro</asp:ListItem> </asp:DropDownList> <asp:RequiredFieldValidator ID
="RequiredFieldValidator3" runat="servidor"

ControlToValidate="DropDownList1" Display="Dinâmico"
ErrorMessage="Por favor, selecione um motivo para entrar em contato conosco"
InitialValue="PS">* </
asp:RequiredFieldValidator>
...
<asp:Label ID="Label5" runat="server" Text="Subject :"></asp:Label>
...
<asp:TextBox ID="TextBox3" runat="server" Columns="37"></asp:TextBox>
<asp:RequiredFieldValidator
ID="RequiredFieldValidator4" runat="servidor" ControlToValidate="TextBox3"
Display="Dynamic" ErrorMessage="Por favor, insira o
assunto">* </asp:RequiredFieldValidator>
...
<asp:Label ID="Label6" runat="server" Text="Mensagem:"></asp:Label>
...
<asp:TextBox ID="TextBox4" runat="servidor" Columns="30" Linhas="3"
TextMode="MultiLine"></asp:TextBox>
<asp:RequiredFieldValidator
ID="RequiredFieldValidator5" runat="servidor" ControlToValidate="TextBox4"
Display="Dynamic" ErrorMessage="Por favor, digite a
mensagem">* </asp:RequiredFieldValidator>
...
<asp:Label ID="Label7" runat="server" Text="Web Site (Opcional) :"></asp:Label>
...
<asp:TextBox ID="TextBox5" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator ID="RegularExpressionValidator2"
runat="server" ControlToValidate="TextBox5"
Display="Dynamic" ErrorMessage="Insira um URL válido"
ValidationExpression="http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?"> *</
asp :RegularExpressionValidator>
...

347
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

<asp:Label ID="Label8" runat="server" Text="Você representa :"></asp:Label>


...
<asp:RadioButtonList ID="RadioButtonList1" runat="servidor"
RepeatDirection="Horizontal">
<asp:ListItem Selected="True" Value="Individual">Indivíduo</asp:ListItem> <asp:ListItem
Value="ListItem">Empresa</asp:ListItem> </asp:RadioButtonList >

...
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Enviar" />
...
<asp:Label ID="Label9" runat="server" Font-Bold="True" ForeColor="Red"></asp:Label>
...
</html>

Observe a marcação com atenção. No topo, você tem uma diretiva chamada @Page. Uma diretiva fornece
informações sobre alguma entidade para o mecanismo de processamento ASP.NET. A diretiva @Page fornece detalhes sobre o
formulário da Web atual, como linguagem de codificação, nome do arquivo de código e a classe do arquivo de código que representa
esse formulário da Web.
Em seguida, há uma declaração <!DOCTYPE> que indica que este documento é um documento HTML5.
Se você observar melhor, notará que cada controle de servidor é representado por uma marcação XML especial.
Por exemplo, um controle Label é representado por uma marca <asp:Label> e um controle TextBox é representado por uma
marca <asp:TextBox>. A parte antes dos dois pontos (:) — em outras palavras, asp — é chamada de prefixo de tag. A parte
após os dois pontos — Label ou TextBox — é chamada de nome de tag.
Cada controle de servidor tem seu atributo ID definido como um valor exclusivo. A ID de um controle é usada para
acessá-lo programaticamente. Da mesma forma, todo controle de servidor possui um atributo runat, que deve ter o valor server
e indica que a tag é uma marcação de controle de servidor. Você também observará que todas as propriedades definidas por
meio da janela Propriedades são representadas como atributos ou elementos filho na marcação de controle do servidor. Assim,
a marcação de controle do servidor ASP.NET é um vocabulário especial de XML.

Escrevendo Código

Nesta seção, você escreverá algum código para que, quando um usuário inserir dados válidos e clicar no botão Enviar,
os dados sejam enviados por e-mail para você. Para atender a esse requisito, você precisa manipular o evento Click do
botão Enviar. Clique duas vezes em Enviar e você será levado ao arquivo code-behind (Default.aspx.cs), onde poderá
escrever o código. A Listagem 12.6 mostra a classe Web Form e o esqueleto do manipulador de eventos Click. Vamos
preencher isso a seguir.

Listagem 12-6. Classe de formulário da Web e o manipulador de eventos Click

classe parcial pública _Default : System.Web.UI.Page {

Protected void Button1_Click(remetente do objeto, EventArgs e) {

}
}

O arquivo code-behind contém uma classe parcial chamada _Default que herda de System.Web.
Classe base UI.Page, que fornece funcionalidade básica para seu Web Form. Lembre-se que a diretiva @Page possui
um atributo chamado Inherits que se refere a esta classe. A classe _Default é marcada como parcial porque somente
quando o arquivo de marcação (.aspx) e o arquivo code-behind (.cs) são combinados, a classe completa é gerada. Esse

348
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

a mesclagem ocorre em tempo de execução e é tarefa do ASP.NET Framework. Além disso, observe vários namespaces que
são importados na parte superior do arquivo code-behind. Todos os namespaces que começam com System.Web estão
localizados fisicamente em um assembly, System.Web.dll. Este é o assembly principal para ASP.NET Web Forms.

ÿ Observação Uma discussão completa dos controles de servidor ASP.NET e conceitos relacionados está além do
escopo deste livro. Se você estiver interessado no desenvolvimento de aplicativos da Web ASP.NET usando Web Forms,

considere visitar https://www.asp.net/web-forms para saber mais.

Para codificar a funcionalidade, você precisa importar dois namespaces: System.Net e System.Net.Mail.
O último namespace fornece classes para enviar e-mails. A Listagem 12-7 mostra o manipulador de eventos Click depois de
adicionar o código necessário.

Listagem 12-7. Enviando e-mail do arquivo code-behind

protected void Button1_Click(object sender, EventArgs e) { SmtpClient client


= new SmtpClient("localhost"); client.Credentials =
CredentialCache.DefaultNetworkCredentials; MailMessage msg = new
MailMessage(); msg.From = new MailAddress(TextBox2.Text);
msg.To.Add("user@localhost"); msg.Subject = TextBox3.Text; msg.Body = "[" +
DropDownList1.SelectedItem.Text + "]" + TextBox4.Text + "\r\n" +

TextBox1.Text + "\r\n" + TextBox5.Text;


cliente.Enviar(msg); Label9.Text = "Sua mensagem foi
enviada. Obrigado!"; }

O código declara um objeto da classe SmtpClient. A classe SmtpClient permite enviar e-mails
baseado em protocolo de transferência de correio simples (SMTP). O construtor da classe SmtpClient aceita o endereço
IP ou o nome da máquina usada para operações SMTP. Em nosso exemplo, supõe-se que você esteja usando uma
instalação local do IIS para enviar e-mails e, portanto, localhost é passado como parâmetro. A propriedade Credentials da
classe SmtpClient indica as credenciais de rede de um usuário para autenticar o remetente. A propriedade
DefaultNetworkCredentials da classe CredentialCache indica as credenciais de autenticação do usuário atual do Windows.

Depois que as credenciais forem definidas, uma nova MailMessage será criada. A classe MailMessage representa
uma mensagem de email. As propriedades From e To desta classe representam o remetente e o destinatário, respectivamente,
e são do tipo MailAddress (certifique-se de alterar o endereço de e-mail para se adequar à sua configuração). As propriedades
Assunto e Corpo indicam o assunto e o corpo do e-mail, respectivamente. Todas essas propriedades são atribuídas usando
os valores inseridos em vários controles de formulário da Web. Finalmente, o método Send() da classe SmtpClient envia a
MailMessage fornecida para um ou mais destinatários. Uma mensagem de sucesso é exibida em um rótulo informando ao
usuário que a mensagem foi recebida.

349
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Executando o aplicativo da Web Para executar o aplicativo

da Web que você acabou de concluir, você precisa escolher a opção de menu Debug ÿ Start Debugging (ou apenas pressionar F5). O Visual
Studio iniciará o servidor Web de desenvolvimento em segundo plano e hospedará seu site nele. Ele também abrirá um navegador e carregará o
Default.aspx. A Figura 12-8 mostra o padrão. aspx Web Form no navegador.

Figura 12-8. Default.aspx em um navegador

Se você tentar clicar no botão Enviar sem inserir nenhum valor, deverá ver um erro de validação
mensagens, conforme mostrado na Figura 12-9.

Figura 12-9. Formulário da Web mostrando erros de validação

350
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Observe como o ValidationSummary exibe uma lista coletiva de mensagens de erro, enquanto um asterisco é exibido
no lugar de controles de validação individuais. Se você inserir valores válidos nos controles e clicar em Enviar, o código
enviará um e-mail para o seu endereço de e-mail e exibirá uma mensagem de sucesso, conforme mostrado na Figura 12-10.

Figura 12-10. Execução bem-sucedida do formulário da Web

ÿ Observação Como esse foi o primeiro exemplo de ASP.NET, descrevi todas as etapas necessárias para desenvolver e executar o

aplicativo. Agora que você está familiarizado com os projetos de sites da Web, os exemplos futuros se concentram no tópico principal

que está sendo discutido, ignorando o detalhamento dessas etapas.

O controle de fonte de dados XML


Um dos pontos fortes dos controles de servidor é sua capacidade de realizar vinculação de dados com dados relacionais ou
hierárquicos. Os controles de fonte de dados ASP.NET são um conjunto de controles de servidor da Web que automatizam tarefas
comuns de acesso a dados, como buscar registros e exibi-los em outros controles vinculados a dados. Tudo isso é alcançado sem
que nenhum código seja escrito pelo desenvolvedor. Como estamos examinando o XML neste livro, é no controle de fonte de
dados XML que estamos interessados. Esse controle é muito útil quando você está usando controles de servidor, como TreeView,
que exibem essencialmente dados hierárquicos. Vamos ver como o controle de fonte de dados XML pode ser usado junto com um
controle TreeView.
Comece criando um novo site via Visual Studio. Adicione um novo arquivo XML ao seu site usando a caixa de diálogo
Adicionar novo item e nomeie-o como Navigation.xml. O arquivo Navigation.xml contém marcação XML que representa a estrutura
de navegação do site. A marcação XML de Navigation.xml é mostrada na Listagem 12-8.

Listagem 12-8. Marcação XML do arquivo Navigation.xml

<?xml version="1.0" encoding="utf-8" ?> <node


text="Home" url="default.aspx">
<node text="Produtos" url="produtos.aspx">
<node text="Product 1" url="product1.aspx"></node> <node
text="Product 2" url="product2.aspx"></node> <node text="Product 3"
url= "product3.aspx"></node> </node> <node text="Serviços"
url="services.aspx">

<node text="Serviço 1" url="service1.aspx"></node> <node text="Serviço


2" url="service2.aspx"></node> <node text="Serviço 3" url=
"service3.aspx"></node> </node> <node text="Sobre nós"
url="about.aspx"></node> <node text="Fale conosco" url="contact.aspx">
</node> </node>

351
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

A tag raiz do documento é <node>, que contém ainda várias tags <node>. Cada <nó>
O elemento representa um nó do controle TreeView e possui dois atributos, text e url:

•O atributo text especifica o texto a ser exibido no nó TreeView.

•O atributo url aponta para uma URL onde o usuário deve ser navegado.

O aninhamento dos elementos <node> decide o aninhamento do TreeView renderizado. Assim, o nó raiz do
TreeView será Home. O nó inicial terá quatro filhos imediatos: Produtos, Serviços, Sobre nós e Fale conosco. Da mesma
forma, os nós Produtos e Serviços terão três filhos cada.
Agora arraste e solte um controle XmlDataSource da caixa de ferramentas para o Web Form Designer. Defina
sua propriedade DataFile como Navigation.xml. A propriedade DataFile aponta para um arquivo XML que fornecerá
dados para o controle de fonte de dados XML. Em seguida, arraste e solte um controle TreeView no formulário da Web
e defina sua propriedade DataSourceID como a ID do controle XmlDataSource que você acabou de configurar. Agora
localize a propriedade DataBindings do TreeView e abra o TreeView DataBindings Editor (consulte a Figura 12-11).

Figura 12-11. Editor de DataBindings TreeView

Na área Ligações de Dados Disponíveis, você verá todos os nós em cada nível do arquivo XML. Selecione os
nó em cada nível e clique no botão Adicionar. Agora você terá três entradas na área Selected Data Bindings. Selecione
a primeira vinculação de dados e defina suas propriedades TextField e NavigateUrlField como text e url, respectivamente.
Como você deve ter adivinhado, text e url são os atributos do elemento <node>. A propriedade DataMember indica o
nome do elemento do documento XML que está fornecendo os dados e é automaticamente definido como nó. Repita o
mesmo processo para as duas vinculações de dados restantes. A Listagem 12-9 mostra a marcação completa de
Default.aspx.

352
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Listagem 12-9. Marcação contendo o XmlDataSource e o TreeView

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs"


Herdar="_Padrão" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml" > <head


runat="server"> <title>Página sem título</title> </head>
<body> <form id="form1 " runat="servidor">
<asp:XmlDataSource ID="XmlDataSource1"
runat="servidor"

DataFile="~/Navigation.xml"> </
asp:XmlDataSource> <asp:TreeView ID="TreeView1"
runat="server" AutoGenerateDataBindings="False"
DataSourceID="XmlDataSource1" Font-Bold="True" Font-Size="Large"
ShowLines="True">
<DataBindings>
<asp:TreeNodeBinding DataMember="node" NavigateUrlField="url"
TextField="text" />
<asp:TreeNodeBinding DataMember="node" NavigateUrlField="url"
TargetField="text" />
<asp:TreeNodeBinding DataMember="node" NavigateUrlField="url"
TargetField="texto" />
</DataBindings>
</asp:TreeView> </
form> </body> </html>

Como você pode ver, o controle de fonte de dados XML e o controle TreeView são representados pelas
marcas de marcação <asp:XmlDataSource> e <asp:TreeView>, respectivamente. Cada ligação de nó TreeView é
representada por uma tag <asp:TreeNodeBinding>. Os atributos DataMember, NavigateUrlField e TextField do
elemento TreeNodeBinding representam as propriedades que você atribuiu anteriormente.
Agora execute o site da Web como antes e veja como o controle TreeView renderiza os vários nós com base em
a estrutura especificada no arquivo Navigation.xml. A Figura 12-12 mostra um exemplo de execução do Web Form.

353
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Figura 12-12. Um TreeView preenchido usando um controle de fonte de dados XML

Aplicando Transformações
No exemplo anterior, você forneceu o arquivo XML como está para o controle de fonte de dados XML. E se você quiser
aplicar uma transformação XSLT ao arquivo XML e, em seguida, vinculá-lo ao TreeView? Felizmente, o controle da fonte de
dados XML tem um recurso integrado para fazer exatamente isso: a propriedade TransformFile, que aponta para um arquivo
XSLT. Antes de fornecer os dados XML para outros controles, como um TreeView, a folha de estilo XSLT é aplicada aos dados
XML e, em seguida, os dados transformados são transmitidos. Suponha que você vinculou um controle TreeView a um
documento XML, conforme mostrado na Listagem 12-10.

Listagem 12-10. Documento XML para ser vinculado a um TreeView

<?xml version="1.0" encoding="utf-8"?> <MenuItem


Title="Home" URL="default.aspx">
<MenuItem Title="Produtos" URL="produtos.aspx">
<MenuItem Title="Produto 1" URL="produto1.aspx" /> <MenuItem
Title="Produto 2" URL="produto2.aspx" /> <MenuItem Title="Produto
3" URL="produto3.aspx" / > </MenuItem>

<MenuItem Title="Serviços" URL="services.aspx">


<MenuItem Title="Serviço 1" URL="service1.aspx" /> <MenuItem
Title="Serviço 2" URL="service2.aspx" /> <MenuItem Title="Serviço
3" URL="service3.aspx" / > </MenuItem>

<MenuItem Title="Sobre nós" URL="about.aspx" /> <MenuItem


Title="Fale conosco" URL="contact.aspx" /> </MenuItem>

354
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Como você pode ver, este documento XML usa o elemento <MenuItem> para representar um nó TreeView. Os
atributos Title e URL representam as propriedades Text e NavigateUrlField de nós TreeView individuais.
Agora suponha que, por algum motivo, você queira vincular o mesmo TreeView a outro documento XML, conforme
mostrado na Listagem 12-11.

Listagem 12-11. Marcação XML do novo arquivo XML

<?xml version="1.0" encoding="utf-8" ?> <node


text="Home" url="default.aspx">
<node text="Produtos" url="produtos.aspx">
<node text="Product 1" url="product1.aspx"></node> <node
text="Product 2" url="product2.aspx"></node> <node text="Product
3" url= "product3.aspx"></node> </node>

<node text="Serviços" url="services.aspx">


<node text="Serviço 1" url="service1.aspx"></node> <node
text="Serviço 2" url="service2.aspx"></node> <node text="Serviço
3" url= "service3.aspx"></node> </node>

<node text="Sobre nós" url="about.aspx"></node> <node


text="Fale conosco" url="contact.aspx"></node> </node>

Você deve ter notado que este é o mesmo arquivo XML que usamos no exemplo anterior. Para atender à mudança,
você precisa reconfigurar as ligações de dados do nó TreeView para se adequar ao novo documento XML. Se a ligação de
dados subjacente tiver um aninhamento complexo, isso pode não ser uma tarefa fácil. Essa situação pode ser evitada se
você aplicar uma folha de estilo XSLT ao XML da Listagem 12-11 e transformá-la para corresponder ao XML da Listagem
12-10. A folha de estilo XSLT que pode fazer essa transformação é mostrada na Listagem 12-12.

Listagem 12-12. Uma folha de estilo XSLT para transformar a nova marcação XML

<?xml version="1.0" encoding="UTF-8" ?>


<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl :template match="/">

<xsl:for-each select="."> <xsl:apply-


templates/> </xsl:for-each>

</xsl:template>
<xsl:template match="nó">
<xsl:element name="MenuItem">
<xsl:nome do atributo="Título">
<xsl:value-of select="@text"/>
</xsl:attribute>
<xsl:attribute name="URL">
<xsl:value-of select="@url"/> </
xsl:attribute> <xsl:apply-templates /> </
xsl:element>

</xsl:template> </
xsl:stylesheet>

355
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

A folha de estilo transforma um elemento <node> em um elemento <MenuItem>. Além disso, ele transforma os
atributos text e url nos atributos Title e URL, respectivamente.
Para testar como a propriedade TransformFile funciona, você precisa modificar o exemplo anterior seguindo estas etapas:

1. Adicione um novo arquivo XSLT chamado Navigation.xslt ao site.

2. Digite a marcação mostrada na Listagem 12-12.

3. Defina a propriedade TransformFile do controle de fonte de dados XML como Navigation.


xslt.

4. Abra o TreeView DataBindings Editor do TreeView e modifique os dados


ligações para usar o nó MenuItem como DataMember, Title como TextField e URL como
NavigateUrlField.

Desta vez, o TreeView DataBindings Editor se parece com a Figura 12-13.

Figura 12-13. Editor TreeView DataBindings mostrando nós transformados

Você observará que, embora a propriedade DataFile ainda seja Navigation.xml, as vinculações de dados mostradas
são de acordo com a transformação especificada na folha de estilo XSLT. Se você executar o Web Form novamente, a
saída deve ser a mesma da Figura 12-12 anterior.

Filtrando dados usando expressões XPath


Às vezes, você pode querer filtrar os dados do arquivo XML de origem e vincular os dados filtrados a outros controles.
É aqui que entra a propriedade XPath do controle de fonte de dados XML. A propriedade XPath pega uma expressão XPath
válida e a aplica aos dados XML de origem.
Suponha, por exemplo, que você queira exibir apenas os nós relacionados ao produto no TreeView. Você pode
conseguir isso definindo a propriedade XPath da fonte de dados XML como /node/node[@text="Products"]. Dessa forma,
apenas os nós relacionados ao produto (Produtos, Produto 1, Produto 2 e Produto 3) serão filtrados. O TreeView terá
aninhamento de até dois níveis apenas (Produtos como o nó superior e os outros três nós como nós filhos). A Figura 12-14
mostra um exemplo de execução do mesmo formulário da Web após definir a propriedade XPath.

356
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Figura 12-14. TreeView depois de aplicar o filtro XPath

Vinculando uma fonte de dados XML a um controle de menu


Nos exemplos anteriores, você usou um TreeView para exibir dados fornecidos por um controle de fonte de dados XML. ASP.
NET também fornece um controle Menu que pode ser usado para exibir dados hierárquicos. O controle Menu corresponde de
perto ao controle TreeView em relação à vinculação de dados XML. No entanto, difere em sua aparência.
O controle Menu renderiza menus suspensos dinâmicos em seus formulários da Web semelhantes aos aplicativos tradicionais
do Windows. Vamos ver um exemplo de como pode ser usado.
Crie um novo site no Visual Studio. Adicione o arquivo Navigation.xml que você criou anteriormente ao site usando a
caixa de diálogo Adicionar item existente. Arraste e solte um controle XmlDataSource no formulário da Web padrão e defina sua
propriedade DataFile como Navigation.xml. Agora arraste e solte um controle Menu no formulário e defina sua propriedade
DataSourceID para a ID do controle XmlDataSource que você acabou de configurar.
Agora, localize a propriedade DataBindings do controle Menu. Semelhante ao TreeView, o controle Menu
abre o Menu DataBindings Editor, onde você pode configurar as ligações de dados.
O processo de configuração de vinculações de dados é exatamente o mesmo de antes. Você precisa selecionar o necessário
vinculações de dados e defina suas propriedades DataMember, TextField e NavigateUrl como node, text e url, respectivamente.
A Figura 12-15 mostra o Menu DataBindings Editor com as ligações de dados necessárias adicionadas.

Figura 12-15. Menu Editor de DataBindings

357
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

A marcação completa do Web Form é mostrada na Listagem 12-13.

Listagem 12-13. Marcação do Controle de Menu

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs"


Herdar="_Padrão" %>

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" > <head
runat="server"> <title>Página sem título</title> </head>
<body> < form id="form1" runat="servidor">
<asp:XmlDataSource ID="XmlDataSource1" runat="servidor"

DataFile="~/Navigation.xml"></asp:XmlDataSource> <asp:Menu
ID="Menu1" runat="server" BackColor="#E3EAEB"
DataSourceID="XmlDataSource1"
<DataBindings>
<asp:MenuItemBinding DataMember="node" NavigateUrlField="url"
TextField="text" />
<asp:MenuItemBinding DataMember="node" NavigateUrlField="url"
TextField="text" />
<asp:MenuItemBinding DataMember="node" NavigateUrlField="url"
TextField="texto" />
</DataBindings> </
asp:Menu> </form> </
body> </html>

Como você pode ver, o controle Menu é representado pela marca de marcação <asp:Menu>. O <DataBindings>
A seção define uma ou mais vinculações de dados, em que cada vinculação de dados é representada
por um elemento <asp:MenuItemBinding>. A execução do Web Form deve renderizar o menu conforme mostrado na Figura 12-16.

Figura 12-16. O controle Menu em ação

358
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Trabalhando com mapas do site


Um mapa do site é um arquivo XML que detalha o layout geral de navegação do seu site. Você pode consumir esse arquivo de mapa do
site conforme necessário. O arquivo do mapa do site tem uma extensão .sitemap. Vamos examinar os arquivos de mapa do site por meio
de um exemplo. Dê uma olhada na Figura 12-17.

Figura 12-17. Estrutura de um site

A Figura 12-17 mostra a estrutura de diretórios de um exemplo de site da Web. A página inicial (Default.aspx) e a página
Fale Conosco (contact.aspx) residem na pasta raiz do site. Existem duas subpastas chamadas Produtos e Serviços. Cada um
deles contém dois formulários da Web—Product1.aspx e Product2.aspx, e Service1.aspx e Service2.aspx—respectivamente. Agora
vamos representar a estrutura do site usando um mapa do site.
Crie um novo site usando o Visual Studio. Adicione um novo arquivo de mapa do site usando a caixa de diálogo Adicionar novo
item (consulte a Figura 12-18). Nomeie o arquivo de mapa do site Web.sitemap.

359
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Figura 12-18. Adicionando um novo mapa do site

Agora digite a marcação XML mostrada na Listagem 12-14 no arquivo Web.sitemap.

Listagem 12-14. Conteúdo do arquivo Web.sitemap

<?xml version="1.0" encoding="utf-8" ?> <siteMap


xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" > <siteMapNode url="default.aspx"
title="Home" description="Meu Web Site"> <siteMapNode url="~/products/default.aspx"
title="Produtos">
<siteMapNode url="~/products/product1.aspx" title="Primeiro Produto" /> <siteMapNode
url="~/products/product2.aspx" title="Segundo Produto" /> </siteMapNode> <siteMapNode
url= "~/services/default.aspx" title="Serviços">

<siteMapNode url="~/services/service1.aspx" title="Primeiro Serviço" /> <siteMapNode


url="~/services/service2.aspx" title="Segundo Serviço" /> </siteMapNode> <siteMapNode
url= "contact.aspx" title="Contate-nos" /> </siteMapNode> </siteMap>

O arquivo de mapa do site contém um conjunto de tags e atributos predefinidos. O nó raiz de um arquivo de
mapa do site é <siteMap>. Além disso, contém várias tags <siteMapNodes> dependendo da estrutura do seu site. A
tag <siteMapNode> possui quatro atributos importantes. Eles estão listados na Tabela 12-1.

360
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Tabela 12-1. Atributos de um nó de mapa do site

Atributo Descrição
título Indica o título da página. Esse atributo geralmente é usado por controles de navegação para exibir
o título da URL.
url Indica a URL da página que este nó representa.

descrição Especifica a descrição da página de destino. Você pode usar esta descrição para mostrar dicas de
ferramentas.
funções Ao usar a filtragem de segurança, esse atributo especifica as funções que têm permissão para
acessar essa página.

ÿ Observação A filtragem de segurança é um recurso que implementa a segurança baseada em funções renderizando apenas

os nós permitidos para o usuário atual. Em outras palavras, um determinado <siteMapNode> estará acessível a um usuário
apenas se a função do usuário for especificada no atributo de funções do elemento <siteMapNode> .

Os arquivos de mapa do site são frequentemente usados para renderizar algum tipo de estrutura de navegação. Há duas maneiras
comuns de consumir o arquivo de mapa do site que você acabou de criar:

• Em um controle SiteMapPath
• Em um controle SiteMapDataSource

Nas seções a seguir, você verá os dois.

Usando um controle SiteMapPath O controle

SiteMapPath permite renderizar o que geralmente é chamado de breadcrumbs. A Figura 12-19 mostra o que são breadcrumbs.

Figura 12-19. Migalhas de pão

O controle SiteMapPath exibe vários níveis de navegação. Você pode clicar nos níveis pai ou raiz
para navegar de volta ou para o nível superior. Antes de nos aprofundarmos nos detalhes, vamos primeiro criar a estrutura de
diretórios e os formulários da Web necessários. Comece adicionando duas pastas ao site chamado Produtos e Serviços. Adicione
os Web Forms conforme mostrado na Tabela 12-2.

361
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Tabela 12-2. Pastas e formulários da Web

Nome do formulário da Web Pasta

Default.aspx Pasta raiz

Contact.aspx Pasta raiz

Produto1.aspx Produtos

Produto2.aspx Produtos

Service1.aspx Serviços

Service2.aspx Serviços

Agora arraste e solte um controle SiteMapPath em cada Web Form. Execute Service2.aspx no navegador,
e você deve ver algo semelhante à Figura 12-19. O controle SiteMapPath renderiza automaticamente trilhas de
navegação para o formulário da Web atual com base em sua localização no arquivo de mapa do site.

Usando um controle SiteMapDataSource O uso de mapas de sites

não se limita apenas ao controle SiteMapPath. Você também pode anexar o mapa do site a controles de navegação,
como um TreeView. Neste exemplo, usaremos o mesmo arquivo de mapa do site para vincular a um TreeView.

Crie um novo site usando o Visual Studio. Adicione o mesmo arquivo Web.sitemap a ele. Desta vez, arraste e solte
um controle SiteMapDataSource no Web Form. O controle SiteMapDataSource seleciona automaticamente o arquivo
Web.sitemap e o fornece a outros controles. Além disso, arraste e solte um controle TreeView e defina sua propriedade
DataSourceID para o atributo ID do SiteMapDataSource. É isso. Execute o formulário da Web e ele deve se parecer com a
Figura 12-20.

Figura 12-20. TreeView vinculado ao controle SiteMapDataSource

362
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Usando o Controle XML


No Capítulo 6, você aprendeu a aplicar folhas de estilo XSLT a dados XML e transformá-los de um vocabulário para
outro. Você conseguiu isso criando uma instância da classe XslCompiledTransform. Em seguida, você carregou
uma folha de estilo XSLT nela usando seu método Load(). Finalmente, você fez a transformação usando seu método
Transform(). O ASP.NET fornece uma alternativa fácil para essa codificação manual: o controle XML. O controle
XML aceita um documento XML e uma folha de estilo XSLT. Em seguida, ele aplica a folha de estilo aos dados XML
e renderiza o conteúdo na página da web. O uso mais comum do controle XML é transformar dados XML em HTML,
embora, é claro, não precise ser HTML.
Para ilustrar o uso do controle XML, crie um novo site. Adicione um novo arquivo XML chamado Employees.xml
e digite a marcação mostrada na Listagem 12-15.

Listagem 12-15. Arquivo XML que fornece dados para o controle XML

<?xml version="1.0" encoding="utf-8" ?> <?xml-


stylesheet type="text/xsl" href="Employees.xslt"?> <!-- Esta é a lista de
funcionários --> <funcionários> <funcionário id="1">

<firstname>Nancy</firstname>
<lastname>Davolio</lastname>
<homephone>(206) 555-9857</homephone>
<notes> <![CDATA[A educação inclui
bacharelado em psicologia pela Colorado State University em 1970.
Ela também completou "The Art of the Cold Call". Nancy é membro do Toastmasters International.]]> </
notes> </employee> <employee employeeid="2">

<firstname>Andrew</firstname>
<lastname>Fuller</lastname>
<homephone>(206) 555-9482</homephone>
<notes> <![CDATA[Andrew recebeu seu
comercial do BTS em 1974 e um Ph.D. em marketing internacional pela Universidade
de Dallas em 1981. Ele é fluente em francês e italiano e lê alemão. Ele ingressou na empresa como
representante de vendas, foi promovido a gerente de vendas em janeiro de 1992 e a vice-presidente
de vendas em março de 1993. Andrew é membro da Sales Management Roundtable, da Seattle
Chamber of Commerce e da Pacific Rim Importers Association. ]]> </notes> </employee> <employee
employeeid="3">

<firstname>Janet</firstname>
<lastname>Leverling</lastname>
<homephone>(206) 555-3412</homephone>
<notes> <![CDATA[Janet é bacharel em química
pelo Boston College (1984) .

363
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Ela também concluiu um programa de certificação em gerenciamento de varejo de alimentos.


Janet foi contratada como associada de vendas em 1991 e promovida a representante
de vendas em fevereiro de 1992.]]> </notes> </employee> </employees>

Da mesma forma, adicione uma nova folha de estilo XSLT denominada Employees.xsl e digite a marcação mostrada
na Listagem 12-16.

Listagem 12-16. Folha de estilo XSLT a ser aplicada

<?xml version="1.0" encoding="UTF-8" ?>


<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl :template
match="/"> <html>

<body>
<h1>Lista de Funcionários</h1>
<table border="1">
<tr>
<th>ID do funcionário</
th> <th>Nome</th>
<th>Sobrenome</th>
<th>Telefone Residencial</th>
<th>Notas</th>
</tr>
<xsl:for-each select="funcionários/funcionário">
<tr>
<td>
<xsl:value-of select="@employeeid"/> </td>

<td>
<xsl:value-of select="firstname"/>
</td>
<td>
<xsl:value-of select="lastname"/>
</td>
<td>
<xsl:value-of select="homephone"/> </td>

<td>
<xsl:value-of select="notas"/>
</td>
</tr>
</xsl:para-cada>
</table>
</body>
</html>
</xsl:template> </
xsl:stylesheet>

364
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

ÿ Observação Esses são os mesmos arquivos usados no Capítulo 6. Se desejar, você pode adicioná-los ao seu site
da Web em vez de recriá-los.

Agora arraste e solte um controle XML no formulário da Web padrão. Defina sua propriedade DocumentSource como
Employees.xml e sua propriedade TransformSource como Employees.xsl. A primeira propriedade aponta para o arquivo XML que
deve ser transformado, enquanto a última propriedade aponta para a folha de estilo XSLT que deve ser aplicada ao DocumentSource.
Agora execute o Web Form e você deverá ver algo semelhante à Figura 12-21.

Figura 12-21. Dados XML transformados em HTML usando o controle XML

Usando o Sistema de Configuração do .NET Framework


O sistema de configuração do .NET Framework depende fortemente de arquivos XML. Existem dois arquivos XML distintos que
armazenam a configuração do .NET Framework:

•Arquivos de configuração da máquina

• Arquivos de configuração do aplicativo

Quando você instala o .NET Framework em uma máquina, um arquivo XML chamado Machine.config é criado
na pasta <installation_path>/Config. O arquivo Machine.config é o arquivo de configuração mestre e contém definições de
configuração que são aplicadas a todos os aplicativos .NET em execução nessa máquina.
Embora este arquivo esteja no formato XML e possa ser editado diretamente, você deve fazer isso com cautela. Isso é
porque qualquer alteração feita neste arquivo afetará todos os aplicativos em execução nessa máquina.
A Figura 12-22 mostra a localização de Machine.config em uma instalação de amostra.

365
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Figura 12-22. Localização de Machine.config

Para substituir as configurações especificadas no arquivo Machine.config, você precisa criar arquivos de configuração
do aplicativo. Os arquivos de configuração do aplicativo também são arquivos XML contendo marcação XML especial. Para
aplicativos baseados no Windows, o arquivo de configuração do aplicativo tem o formato <exe_name>.exe.config, em que
exe_name é o nome do executável do aplicativo.

ÿ Observação Ao desenvolver aplicativos do Windows no Visual Studio, você descobrirá que há um arquivo App.config
na pasta raiz do projeto. Este arquivo contém a configuração do aplicativo de tempo de desenvolvimento. O arquivo
<exe_name>.exe. config pode ser encontrado nas pastas Bin\Debug ou Bin\Release . Você deve implantar
<exe_name>.exe.config junto com seu aplicativo e não o arquivo App.config .

Para aplicativos da web, o nome do arquivo de configuração do aplicativo deve ser web.config. Nas seções a
seguir, você aprenderá mais sobre o arquivo web.config e o vocabulário XML usado nele.

ÿ Observação Há também arquivos de configuração de segurança que armazenam a configuração relacionada à segurança de acesso ao código.

Esses arquivos devem ser alterados usando a ferramenta Code Access Security Policy (Caspol.exe). É
recomendável que você altere esses arquivos apenas se estiver ciente do impacto que eles terão na política de
segurança. Não entraremos em detalhes sobre arquivos de configuração de segurança neste livro.

366
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Estrutura do arquivo web.config


A Listagem 12-17 mostra o esboço estrutural geral de um arquivo web.config.

Listagem 12-17. Contorno Estrutural de web.config

<configuration>
<appSettings />
<connectionStrings />
<system.web /> </
configuration>

Como você pode ver, o nó raiz do arquivo web.config é <configuration>, e há três principais
subseções:

•A seção <appSettings> é usada para especificar as definições de configuração do aplicativo.

•A seção <connectionStrings> é usada para armazenar uma ou mais conexões de banco de dados
cordas.

•A seção <system.web> contém todas as configurações aplicáveis aos aplicativos da web.

Todas essas seções são opcionais. No entanto, na maioria dos casos do mundo real, você terá pelo menos a
seção <system.web>.

ÿ Observação O arquivo web.config contém muitas seções de configuração. Não é possível cobrir todas as
seções aqui. Vou discutir apenas algumas seções comumente usadas.

Herança do Web.config
O arquivo web.config exibe o que geralmente é chamado de comportamento de herança. Em um único aplicativo da web,
pode haver um ou mais arquivos web.config em pastas diferentes. As configurações de um arquivo web.config são aplicadas à
pasta na qual ele reside e a todas as subpastas. No entanto, se as subpastas contiverem um web.config próprio, as configurações
especificadas nesse web.config terão precedência.

Usando Web.config para tarefas comuns de configuração


Agora que temos uma compreensão básica de como o web.config funciona, vamos ver como realizar algumas tarefas comuns
de configuração. Vamos cobrir brevemente as seguintes tarefas em particular:

•Como armazenar e recuperar as configurações do aplicativo

•Como armazenar e recuperar suas strings de conexão de banco de dados

•Como trabalhar com autenticação de formulários

•Como lidar com o estado da sessão

•Como fornecer páginas de erro personalizadas em seu site

367
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Armazenando e recuperando definições de configuração do aplicativo


Evitar valores de codificação permanente é um requisito obrigatório em muitos aplicativos do mundo real. Anteriormente neste
capítulo, desenvolvemos um formulário da Web Fale Conosco que enviava mensagens de usuários para um endereço de e-mail especificado.
Nesse exemplo, codificamos um endereço de e-mail no arquivo code-behind. E se o endereço de e-mail mudar após a
implantação? Obviamente, você precisa alterar o código-fonte para corresponder ao novo endereço de e-mail e reimplantar o
aplicativo. Esta não é uma prática recomendada para aplicativos do mundo real. Não seria bom se pudéssemos isolar o endereço de
e-mail do aplicativo, armazená-lo em um local externo e recuperá-lo dentro do seu código? Antes do .NET Framework, os
desenvolvedores faziam isso usando arquivos .INI ou o registro. No .NET, você tem uma boa alternativa: a seção de configuração do
aplicativo dos arquivos de configuração.
A seção <appSettings> do web.config permite que você armazene essas configurações específicas do aplicativo. Você
pode ler essas configurações em seu código-fonte. Se as configurações mudarem após a implantação do aplicativo, você precisará
alterar apenas o arquivo web.config e não o código-fonte. Vamos modificar nosso formulário da Web Entre em contato conosco
para usar as definições de configuração do aplicativo.

ÿ Observação Embora o exemplo discutido aqui use a seção <appSettings> em um aplicativo da web,
<appSettings> é igualmente aplicável a aplicativos do Windows.

Abra o mesmo site escolhendo File ÿ Open ÿ Web Site no menu. Abra o arquivo web.config
e modifique a seção <appSettings>, conforme mostrado na Listagem 12-18.

Listagem 12-18. Armazenando valores na seção <appSettings>

<appSettings>
<add key="host" value="localhost"/> <add
key="email" value="user@localhost"/> </appSettings>

A seção <appSettings> pode conter um ou mais elementos <add>. O elemento <add> tem dois atributos:

•O atributo chave define uma chave com a qual o valor será acessado no código.

•O atributo value especifica o valor real da chave.

Em nosso exemplo, definimos duas chaves: host e email. A primeira chave armazena o valor do host SMTP e a última armazena
seu endereço de e-mail. Agora abra o code-behind do Web Form e modifique o manipulador de eventos Click do botão Submit,
conforme mostrado na Listagem 12-19.

Listagem 12-19. Recuperando valores da seção <appSettings>

protegido void Button1_Click(remetente do objeto, EventArgs e) { string host =


ConfigurationManager.AppSettings["host"]; string email =
ConfigurationManager.AppSettings["email"]; Cliente SmtpClient = new
SmtpClient(host); client.Credentials = CredentialCache.DefaultNetworkCredentials;
MailMessage msg = new MailMessage(); msg.From = new MailAddress(TextBox2.Text);
msg.To.Add(email); msg.Subject = TextBox3.Text;

368
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

msg.Body =
"[" + DropDownList1.SelectedItem.Text + "]" + TextBox4.Text + "\r\n" +
TextBox1.Text + "\r\n" + TextBox5.Text; cliente.Enviar(msg);
Label9.Text = "Sua mensagem foi enviada. Obrigado!"; }

Observe o código marcado em negrito. O código usa uma classe chamada ConfigurationManager, que reside no namespace
System.Configuration. Por padrão, System.Configuration é importado no code-behind.
A propriedade AppSettings da classe ConfigurationManager expõe toda a seção <appSettings> como uma NameValueCollection.
Você pode acessar valores individuais usando um índice ou um nome de chave, embora seja mais comum acessá-los usando nomes
de chave.
O código recupera os valores de duas chaves — host e email — e os armazena em uma variável de string. O
O construtor da classe SmtpClient agora aceita o valor armazenado na variável de string do host em vez de um valor embutido
em código. Da mesma forma, o método Add() aceita o valor armazenado na variável de string de e-mail e não um valor embutido em
código. Se você executar o aplicativo, deverá obter os resultados como antes, mas agora poderá alterar o nome do host e o endereço
de e-mail sem tocar no código-fonte.

Armazenando e recuperando strings de conexão com o banco de dados Armazenar strings de

conexão com o banco de dados fora do código-fonte é provavelmente a tarefa de configuração mais comum. O arquivo de
configuração possui uma seção especial para armazenar strings de conexão com o banco de dados chamada <connectionStrings>.
A seção <connectionStrings> permite armazenar uma ou mais strings de conexão de banco de dados que podem ser recuperadas
posteriormente em seu código. Para recuperar as strings de conexão armazenadas na seção <connectionStrings>, você precisa
novamente usar a classe ConfigurationManager.
Para ilustrar o uso da seção <connectionStrings>, você desenvolverá um formulário da Web simples para listar funcionários.
O Web Form exibirá uma lista de funcionários em um controle GridView. Para começar, crie um novo site usando o Visual Studio
como antes.

ÿ Observação Embora o exemplo discutido aqui use a seção <connectionStrings> em um aplicativo da web,
<connectionStrings> é igualmente aplicável a aplicativos do Windows.

Abra o arquivo web.config no IDE e modifique a seção <connectionStrings>, conforme mostrado na Listagem 12-20.

Listagem 12-20. Adicionando uma string de conexão à seção <connectionStrings>

<connectionStrings>
<adicionar nome="vento norte"
connectionString="fonte de dados=.;catálogo inicial=Northwind; segurança
integrada=true" providerName="System.Data.SqlClient"/>

</connectionStrings>

369
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

A seção <connectionStrings> pode conter um ou mais elementos <add>, cada um definindo uma string de conexão com o banco
de dados:

•O atributo name do elemento <add> define um nome para essa string de conexão.
Esse nome é usado posteriormente para acessar a string de conexão.

•O atributo connectionString especifica a string de conexão do banco de dados real.

• Finalmente, o atributo providerName indica o provedor de dados .NET que pode ser usado para se
comunicar com o banco de dados.

Agora abra o formulário da Web padrão e arraste e solte um controle GridView nele. Em seguida, digite o código
mostrado na Listagem 12-21 no evento Page_Load do Web Form.

Listagem 12-21. Recuperando a string de conexão

protected void Page_Load(remetente do objeto, EventArgs e) {

string strConn =
ConfigurationManager.ConnectionStrings["northwind"].ConnectionString; SqlDataAdapter da =

new SqlDataAdapter("SELECT EmployeeID,FirstName, LastName FROM Employees", strConn); DataSet ds


= new DataSet(); da.Fill(ds, "funcionários"); GridView1.DataSource = ds;
GridView1.DataBind(); }

O código usa a classe ConfigurationManager para recuperar o valor da string de conexão. A coleção
ConnectionStrings pode ser acessada usando um índice ou um nome de string de conexão. Em nosso exemplo, nós o
acessamos com um nome.
Cada string de conexão armazenada na seção <connectionStrings> é representada por uma classe
ConnectionStringSettings e a propriedade ConnectionString dessa classe retorna a string de conexão real. A cadeia de conexão
é então usada como o segundo parâmetro do construtor SqlDataAdapter, sendo o primeiro parâmetro a consulta SELECT.

Um DataSet é então preenchido usando o método Fill() da classe SqlDataAdapter. O DataSet assim criado atua como um
DataSource para o controle GridView. O método DataBind() do controle GridView vincula os dados do DataSet ao GridView. Se você
executar o Web Form depois de escrever o código, deverá ver algo semelhante à Figura 12-23.

370
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Figura 12-23. Formulário da Web exibindo uma listagem de funcionários

Usando autenticação de formulários Ao desenvolver

sites da Web, você pode precisar garantir que as páginas sejam acessíveis apenas a usuários válidos do aplicativo. Além disso,
você pode querer que apenas os usuários pertencentes a determinada função (digamos, Administrador) tenham permissão para
acessar as páginas. O último é freqüentemente chamado de segurança baseada em função. Para implementar esses recursos de segurança,
você usa autenticação e autorização.
Autenticação é um processo pelo qual você decide se uma pessoa é um usuário válido do aplicativo.
Normalmente, essa validade é determinada por uma combinação de nome de usuário e senha. A autorização é um processo pelo qual você
decide se um usuário autenticado pode acessar uma determinada página ou recurso. Uma maneira comum de autorizar usuários é adicioná-
los a uma ou mais funções.
O arquivo web.config tem duas seções—<autenticação> e <autorização>—que permitem configurar os respectivos aspectos de
segurança para um site. Como exemplo, vamos construir um site da Web simples que autentique um usuário e exiba uma mensagem
de boas-vindas após uma tentativa de login bem-sucedida.

ÿ Observação Para manter as coisas simples, não implementaremos autenticação baseada em banco de dados e segurança
baseada em função neste exemplo. Em um caso realista, você poderia usar ASP.NET Membership and Roles ou, melhor
ainda, ASP.NET Identity para desenvolver um esquema completo de autenticação e autorização para seu site.

Crie um novo site usando o Visual Studio como antes. Em seguida, abra o arquivo web.config no IDE e adicione as seções
<authentication> e <authorization>, conforme mostrado na Listagem 12-22.

371
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Listagem 12-22. Configurando autenticação de formulários

<modo de autenticação="Formulários">
<forms loginUrl="~/login.aspx" defaultUrl="~/
default.aspx"> </forms> </authentication>
<authorization> <deny users="?"/>

</autorização>

A marcação mostrada na Listagem 12-22 define o atributo mode da seção <authentication> para Forms.
O atributo mode pode assumir um destes valores—None, Forms, Windows, Passport e Federated. A autenticação de
formulários é uma forma muito comum de autenticação em que uma página de login é apresentada ao usuário, o usuário
insere algum nome de usuário e senha e, em seguida, essas credenciais são validadas em algum armazenamento de dados
(como o SQL Server).
A seção <authentication> contém o elemento <forms> que configura a autenticação de formulários.
O atributo loginUrl da marca <forms> indica que Login.aspx é a página de login do aplicativo.
Caso algum usuário tente acessar outras páginas sem fazer login primeiro, ele será redirecionado para esta página. O
atributo defaultUrl da tag <forms> indica que, após uma tentativa de login bem-sucedida, o usuário pode ser redirecionado
para a página padrão.
A seção <autorização> contém o elemento <deny>. O atributo users da tag <deny> é definido como ?, indicando que
usuários anônimos não devem ter permissão para acessar o site.
Agora adicione dois Web Forms ao projeto — Default.aspx e Login.aspx. Em seguida, abra Login.aspx e arraste e solte
um controle Login nele da caixa de ferramentas. Como você deve ter adivinhado, o controle Login permite capturar facilmente
o nome de usuário e a senha de um usuário. Nesse estágio, Login.aspx deve se parecer com a Figura 12-24.

Figura 12-24. Login.aspx com controle de login

Agora clique duas vezes no controle Login para acessar o manipulador de eventos Authenticate. O evento Autenticar
handler é um local para escrever seu código que valida as credenciais do usuário. Em um caso do mundo real, você teria um
acesso ao banco de dados envolvido aqui. Para simplificar, nosso exemplo usa um nome de usuário e senha embutidos em
código. O manipulador de eventos Authenticate é mostrado na Listagem 12-23.

372
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Listagem 12-23. Autenticar manipulador de eventos

protected void Login1_Authenticate(remetente do objeto, AuthenticateEventArgs e) {

if(Login1.UserName=="admin" && Login1.Password=="senha") {

FormsAuthentication.SetAuthCookie(Login1.UserName, Login1.RememberMeSet); e.Authenticated =


true; Response.Redirect(FormsAuthentication.DefaultUrl);

}
outro {
e.Authenticated = false;
}
}

O manipulador de eventos Authenticate verifica as propriedades UserName e Password do controle Login.


Se forem administrador e senha, respectivamente, ele chama o método SetAuthCookie() da classe FormsAuthentication
(espaço de nomes System.Web.Security). A autenticação do Forms funciona com a ajuda de um cookie (por padrão). A
presença desse cookie junto com uma solicitação indica que um usuário é um usuário autenticado. A ausência deste
cookie indica que o usuário ainda não foi autenticado.
O método SetAuthCookie() emite esse cookie para o usuário. Os dois parâmetros do método SetAuthCookie() são nome de
usuário e se deve lembrar o status de login mesmo depois de fechar o navegador (cookie persistente).

Depois que o cookie de autenticação é emitido, o código redireciona o usuário para a página padrão. Observe como a
propriedade DefaultUrl da classe FormsAuthentication é usada para recuperar o defaultUrl definido anteriormente no arquivo de
configuração.
Se o nome de usuário e a senha não corresponderem aos valores especificados, a propriedade Authenticated da
classe AuthenticateEventArgs será definida como false. Dessa forma, o controle Login exibirá uma mensagem de erro ao
usuário.
Em seguida, abra o arquivo Default.aspx e arraste e solte dois controles Label e um controle Button sobre ele. Defina a
propriedade Text do Button como Logout. A página padrão exibirá uma mensagem de boas-vindas ao usuário. Para especificar
essa mensagem, escreva o código mostrado na Listagem 12-24 no manipulador de eventos Page_Load.

Listagem 12-24. Recuperando o nome de usuário

protected void Page_Load(remetente do objeto, EventArgs e) {

Label1.Text = $"Bem-vindo {Page.User.Identity.Name}!"; if


(Request.IsAuthenticated) {

Label2.Text = "Você é um usuário autenticado.";


}
}

O código recupera o nome do usuário conectado no momento usando Page.User.Identity.Name e forma uma mensagem
de boas-vindas com base nele. O código então verifica a propriedade IsAuthenticated do objeto Request.
Esta propriedade indica se uma solicitação é autenticada. Se a solicitação for autenticada, uma mensagem é exibida no
segundo Label.
Agora, clique duas vezes no controle Button para entrar em seu manipulador de eventos Click. Escreva o código mostrado
na Listagem 12-25 no manipulador de eventos Click.

373
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Listagem 12-25. Desconectando um usuário

Protected void Button1_Click(remetente do objeto, EventArgs e) {

FormsAuthentication.SignOut();
Response.Redirect(FormsAuthentication.LoginUrl);
}

O código chama o método SignOut() da classe FormsAuthentication. O método SignOut() remove o cookie de autenticação
emitido anteriormente. Depois que o cookie é removido, o usuário é levado de volta à página de login. Isso é feito redirecionando
o controle para a página, conforme indicado pela propriedade LoginUrl de FormsAuthentication.

Se você executar o aplicativo, será direcionado para a página de login mostrada anteriormente. Se você inserir válido
credenciais, a página padrão será exibida conforme mostrado na Figura 12-25.

Figura 12-25. Página padrão exibindo mensagem de boas-vindas

Como você pode ver, a mensagem de boas-vindas exibe o nome de usuário. Você pode clicar no botão Sair para
saia e retorne à página de login.

Configurando o estado da sessão


A sessão ASP.NET permite que você armazene informações arbitrárias. O estado da sessão é mantido por usuário. A sessão é
exposta como um objeto intrínseco — Session — que armazena dados como pares chave-valor. Para configurar o comportamento da
sessão, o web.config possui o elemento <sessionState>. Usando o elemento <sessionState>, você pode ajustar vários aspectos da
sessão, como seu modo de armazenamento, tempo limite e natureza sem cookie.
Para ver como <sessionState> pode ser usado, crie um novo site ASP.NET como antes. Abra a web.
config e adicione o elemento <sessionState>, conforme mostrado na Listagem 12-26.

Listagem 12-26. O elemento <sessionState>

<sessionState
mode="InProc"
timeout="2"
cookieless="true"> </
sessionState>

374
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

O elemento <sessionState> mostrado na Listagem 12-26 define o atributo mode como InProc. O atributo mode controla
onde os dados da sessão serão armazenados. Os valores possíveis são Off, InProc, StateServer, SQLServer e Custom. Se você
definir o modo como Desativado, a sessão será desativada e você não poderá armazenar nenhum dado na sessão. O valor de
InProc indica que os dados da sessão são armazenados na memória do servidor web que hospeda o aplicativo. Os modos
StateServer e SQLServer permitem que você armazene o estado da sessão fora do servidor web (não os discutiremos neste livro).

O atributo timeout define o tempo ocioso em minutos após o qual a sessão é esvaziada. O valor 2 significa que se você
armazenar alguns dados na sessão e depois não usar o aplicativo por 2 minutos, os dados serão removidos da sessão. O tempo
limite padrão da sessão é de 20 minutos.
Por padrão, as sessões requerem cookies ativados no navegador do cliente. Esse cookie normalmente armazena o
ID de uma sessão. Se você não quiser usar cookies por algum motivo, o atributo sem cookie pode ser útil.
Definir sem cookie como falso significa que a sessão não usará cookies; em vez disso, o ASP.NET adicionará algo à URL (você
verá isso em um minuto).
Agora, abra Default.aspx e arraste e solte um controle Label nele. Em seguida, adicione o código mostrado em
Listagem 12-27 para o manipulador de eventos Page_Load.

Listagem 12-27. O manipulador de eventos Page_Load

protected void Page_Load(remetente do objeto, EventArgs e) {

if(Sessão["timestamp"]==nulo) {

Session["timestamp"] = DateTime.Now.ToString();
}
Label1.Text = Session["timestamp"].ToString();
}

O manipulador de eventos Page_Load primeiro verifica se Session contém uma chave chamada timestamp. Se este
key não existe (o valor é nulo), os valores de data e hora atuais são armazenados na sessão.
O valor do carimbo de data/hora é então atribuído ao rótulo.
Execute o site; seu formulário da Web deve se parecer com a Figura 12-26.

Figura 12-26. Carimbo de data/ hora exibido da sessão

Se você atualizar o navegador, verá que o carimbo de data/hora não muda. Isso indica que o valor armazenado na
sessão durante a primeira execução da página está sendo usado conforme o esperado. Agora, mantenha a janela do navegador
ociosa por 3-4 minutos e, em seguida, atualize-a novamente. Você descobrirá que o carimbo de data/hora mudou. Isso ocorre
devido ao valor de tempo limite de 2 minutos. Após 2 minutos de tempo ocioso, a chave de registro de data e hora foi removida da
sessão e, portanto, um novo registro de data e hora foi armazenado.
Observe também a barra de endereço do navegador. Você verá algum segmento de string inserido na URL. Esse é o
resultado da configuração do atributo sem cookie como verdadeiro. Devido a essa configuração, o ASP.NET adiciona um ID de
sessão à URL em vez de armazená-lo em um cookie.

375
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Exibindo páginas de erro personalizadas


Mesmo depois de tomar muito cuidado durante a fase de codificação, podem surgir erros em tempo de execução em seu site. Os
usuários cometem erros de digitação nos URLs das páginas e tentam acessar áreas restritas, falhas de rede podem ocorrer e assim por diante.
Como uma prática de programação robusta, você deve fazer provisões para interceptar todos esses erros inesperados. Para isso, o
ASP.NET permite que você especifique o que é conhecido como páginas de erro personalizadas no arquivo web.config. Isso é feito
usando o elemento <customErrors> do web.config. Vamos ver como.
Crie um novo site no Visual Studio. Adicione uma nova pasta chamada Admin. Esta pasta deve
contém páginas administrativas e os usuários não estão autorizados a acessá-lo. Adicione os cinco Web Forms listados na Tabela
12-3.

Tabela 12-3. Disposição de formulários da Web

Formulário da Web Pasta Descrição

Default.aspx Raiz A página padrão do site

FileNotFound.aspx Raiz Página de erro personalizada que é exibida para o código de erro HTTP 404

UnAuthorized.aspx Raiz Página de erro personalizada que é exibida para o código de erro HTTP 403

GlobalErrorPage.aspx Raiz Página de erro personalizada que é exibida para qualquer outro erro não
tratado no código ou de outra forma

Default.aspx Administrador
Representa a página padrão da pasta Admin

Agora projete Default.aspx da pasta raiz para se parecer com a página mostrada na Figura 12-27.

Figura 12-27. Projeto de Default.aspx

Default.aspx contém dois controles de hiperlink intitulados Ir para Pasta Admin e Ir para Arquivo Inexistente.
Defina a propriedade NavigateUrl desses controles de hiperlink como ~/admin/default.aspx e ~/notexists.aspx, respectivamente. Observe
que estamos definindo deliberadamente o NavigateUrl do segundo hiperlink para um arquivo inexistente. Agora arraste e solte um controle
LinkButton e defina sua propriedade Text como Throw Exception. Adicione o código mostrado na Listagem 12-28 em seu manipulador de
eventos Click.

Listagem 12-28. Lançando uma exceção

protected void LinkButton1_Click(object sender, EventArgs e) { throw new Exception("Erro


inesperado"); }

376
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

O código simplesmente lança uma nova exceção. Como esta é uma exceção não tratada, teremos uma chance
para capturá-lo usando uma página de erro personalizada. Agora adicione um rótulo e um controle de hiperlink cada um em
FileNotFound. aspx, UnAuthorized.aspx e GlobalErrorPage.aspx. Defina a propriedade Text dos controles Label para uma
mensagem de erro amigável. Aponte o hiperlink para Default.aspx para que os usuários possam navegar facilmente de volta à
página inicial.
Adicione o código mostrado na listagem 12-29 no evento Page_Load do formulário da Web Admin/Default.aspx.

Listagem 12-29. Lançando um HttpException

protected void Page_Load(object sender, EventArgs e) { throw new


HttpException(403, "Unauthorized"); }

O código gera um HttpException com um código de status de 403 e uma mensagem de string. O HttpException
classe representa uma exceção específica de HTTP. Dessa forma, acionamos uma exceção com código de status 403
(acesso não autorizado).
Agora abra web.config no IDE e adicione a marcação mostrada na Listagem 12-30 na seção <system.web>.

Listagem 12-30. Especificando páginas de erro personalizadas

<customErrors mode="On" defaultRedirect="GlobalErrorPage.aspx"> <error statusCode="403"


redirect="~/UnAuthorized.aspx"/> <error statusCode="404" redirect="~/FileNotFound.aspx"/
> </customErrors>

A seção <customErrors> permite que você especifique páginas de erro personalizadas para seu site. O modo
atributo tem três valores possíveis:

• Se o modo estiver ativado, as páginas de erro personalizadas serão ativadas para todas as máquinas que
navegam no site.

• Se o modo estiver desativado, as páginas de erro personalizadas serão desativadas para todas as máquinas.

• Se o modo for RemoteOnly, os erros personalizados são ativados apenas para máquinas remotas que
navegam no site, mas são desativados para navegadores locais.

Durante o desenvolvimento, geralmente seu servidor da Web e o navegador serão executados no mesmo
máquina e, portanto, você deve definir o modo como On.
O atributo defaultRedirect aponta para uma página da web que deve ser exibida caso haja alguma
erro não tratado em todo o aplicativo.
A seção <customErrors> pode ter várias tags <error>. O atributo statusCode da tag <error> especifica o código de erro
HTTP no nível do servidor da web. O atributo de redirecionamento especifica a página da Web a ser exibida no caso desse erro. Em
nosso exemplo, configuramos duas páginas de erro personalizadas: uma para o código de status 403 (UnAuthorized.aspx) e a outra para
o código de status 404 (FileNotFound.aspx).
Agora execute Default.aspx e clique nos três links, um por um. Você notará que, em vez de exibir
a página de erro padrão, desta vez o ASP.NET exibe as páginas de erro personalizadas conforme especificado em web.config.
A Figura 12-28 mostra um exemplo de execução do site.

377
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Figura 12-28. Página de erro personalizada para o código de status 403

Documentação de código com comentários XML


Documentar seu código-fonte é um requisito comum em qualquer desenvolvimento profissional. Todo mundo sabe a
importância de um código bem documentado. No entanto, documentar seu código-fonte é apenas uma parte da história.
Freqüentemente, você também precisa gerar arquivos de ajuda profissionais que acompanham seu aplicativo e são usados pelos
usuários finais.
Existem várias maneiras de criar documentação e arquivos de ajuda. A maioria deles é manual, pois alguém (um
desenvolvedor ou redator técnico) precisa digitar o texto de ajuda em formato HTML ou PDF. Em seguida, uma ferramenta
(como Microsoft HTML Help Workshop) é usada para compilar os arquivos de origem em um arquivo .CHM. Isso significa
que há duplicação de trabalho. Primeiro, os desenvolvedores precisam escrever comentários no código-fonte. Em seguida, as
mesmas informações são repetidas nos arquivos de ajuda.
Felizmente, o .NET Framework e o Visual Studio suportam um recurso chamado comentários XML. Ao usar esse recurso,
você pode adicionar comentários ao seu código-fonte usando um vocabulário XML específico. Posteriormente, você pode
extrair esses comentários XML em um arquivo XML separado e transformá-lo em HTML ou algum outro formato. Assim, a
documentação do código é automatizada e evita a duplicação.
Em C#, os comentários XML são indicados por três barras (///). Existem várias tags XML que você pode usar em comentários
XML. Nas seções a seguir, você aprenderá muitos deles.

Criando uma biblioteca de classes Para

começar, você precisa criar uma biblioteca de classes chamada XmlComments.dll. Essa biblioteca de classes representa uma
calculadora matemática simples e consiste em uma única classe chamada Calculadora. A classe Calculadora permite somar,
subtrair, dividir e multiplicar números. Embora este exemplo possa parecer muito simples (e de fato é), seu objetivo aqui é
aprender a sintaxe de comentários XML.
Crie um novo projeto de biblioteca de classes no Visual Studio. Nomeie o projeto como XmlComments e a classe
Calculadora. Digite o código da Listagem 12-31 na classe Calculadora.

Listagem 12-31. A classe da calculadora

public class Calculator { public


int Add(int a, int b) { return (a +
b); }

378
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

public int Subtract(int a, int b) {

retornar (a - b); }
public int Divide(int a,
int b) {

retornar (a/b); }
public int Multiply(int
a, int b) {

retornar (a * b); }

A classe consiste em quatro métodos: Add(), Subtract(), Divide() e Multiply(). Cada método aceita dois
parâmetros e executa a ação correspondente neles. O resultado do cálculo é retornado ao chamador. Agora que
você tem a classe pronta, vamos adicionar comentários de documentação XML a ela.

Documentando o resumo e as observações


Para descrever suas classes e seus membros, você pode usar duas tags: <summary> e <remarks>. A tag
<summary> é usada para descrever um tipo ou seus membros. A tag <remarks> é usada para especificar
informações adicionais sobre o tipo ou membro diferente do especificado em <summary>. A Listagem 12-32
mostra a classe Calculator depois de adicionar as tags <summary> e <remarks>.

Listagem 12-32. Adicionando <resumo> e <observações>

/// <summary> ///


Esta é uma classe que representa /// uma
calculadora matemática simples. /// </resumo> ///
<observações>

/// Esta classe é desenvolvida em .NET Framework 4.7 /// </


remarks>
calculadora de classe pública
{
...

Adicionando parágrafos
O resumo ou comentários podem consistir em vários parágrafos de texto. Cada parágrafo é representado por uma tag
<para>. Observe que uma tag <para> é sempre um elemento filho de <summary> ou <remarks>. A Listagem 12-33 mostra
o uso de uma tag <para>.

Listagem 12-33. Usando uma tag <para>

/// <summary> ///


Esta é uma classe que representa /// uma
calculadora matemática simples. /// <para> ///
Você pode usá-lo para somar, subtrair,

379
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

/// divide e multiplica inteiros e /// números


fracionários. /// </para> /// </summary> ///
<remarks> /// Esta classe é desenvolvida
em .NET Framework 4.7 /// </remarks>

Documentando parâmetros de método e valores de retorno


Os métodos geralmente usam um ou mais parâmetros. Eles também podem retornar algum valor para o chamador. Os
parâmetros são representados por tags <param>, enquanto os valores de retorno são representados por tags <return>.
A tag <param> tem um atributo—name—que indica o nome do parâmetro. A Listagem 12-34 mostra o uso de ambas
as tags no método Add().

Listagem 12-34. Documentando parâmetros e valores de retorno

/// <summary> ///


Este método adiciona dois inteiros. /// </
summary> /// <param name="a">O
primeiro número</param> /// <param name="b">O
segundo número</param> /// <returns>Um inteiro
representando a adição de a e b</returns> public int Add(int a, int b) { return (a +
b); }

Especificando o escopo e as permissões


Sua classe pode conter membros privados, protegidos ou públicos. O escopo desses membros pode ser indicado
usando a tag <permission>. A tag <permission> possui um atributo, chamado cref, que especifica o nome do membro
no contexto fornecido. A Listagem 12-35 mostra um exemplo de uso da tag <permission>.

Listagem 12-35. Usando a tag <permission>

/// <summary> ///


Este método adiciona dois inteiros. /// </
summary> /// <param name="a">O primeiro
número</param> /// <param name="b">O segundo
número</param> /// <returns>Um inteiro representando a
adição de a e b</returns> /// <permission cref="Add">Método público</
permission> public int Add(int a, int b) {

retornar (a + b); }

380
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Especificando links para outros membros


Você pode precisar fazer referências cruzadas aos membros de sua classe. A própria biblioteca MSDN é um
bom exemplo disso. Em muitos lugares, a documentação de uma classe aponta para outra classe relacionada.
Além disso, na parte inferior da página de documentação, aparece uma seção chamada Consulte também. Você
pode conseguir o mesmo para sua documentação usando as tags <see> e <seealso>. A tag <see> deve ser usada
dentro das tags <summary> ou <remarks>. A tag <seealso> possui um atributo chamado cref que aponta para outro
membro e pode ser usado fora da tag <summary>. A Listagem 12-36 ilustra o uso da tag <seealso> como exemplo.

Listagem 12-36. Usando a tag <seealso>

/// <summary> ///


Este método adiciona dois inteiros. /// </
summary> /// <param name="a">O primeiro
número</param> /// <param name="b">O segundo
número</param> /// <returns>Um inteiro representando a
adição de a e b</returns> /// <permission cref="Add">Método público</permission> ///
<veja também cref="Subtract"/> public int Add(int a, int b) { retornar (a + b);

Adicionando listas
Sua documentação pode precisar de listas com marcadores ou numeradas. Isso pode ser feito usando três tags: <list>,
<item> e <listheader>. As tags <item> e <listheader> devem aparecer dentro da tag <list>. A tag <list> tem um atributo
chamado type que pode assumir um valor de marcador, número ou tabela. A tag <listheader> serve para fornecer um
cabeçalho para a lista. Finalmente, a tag <item> encapsula um único item da lista. A Listagem 12-37 mostra o uso
dessas tags.

Listagem 12-37. Usando as tags <list>, <item> e <listheader>

/// <summary> ///


Esta é uma classe que representa /// uma
calculadora matemática simples. /// <para> ///
Você pode usá-lo para adicionar, subtrair, ///
dividir e multiplicar inteiros e /// números
fracionários. /// </para> /// <list type="bullet"> ///
<listheader>Operações suportadas</
listheader> /// <item>Adição</item> ///
<item>Subtração</ item> /// <item>Divisão</
item> /// <item>Multiplicação</item> /// </list> /// </
summary> /// <observações>

381
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

/// Esta classe é desenvolvida no .NET Framework 4.7 ///


</remarks> public class Calculator {

...

Gerando documentação XML a partir de comentários


Para gerar a documentação XML a partir dos comentários do código-fonte, você precisa abrir a caixa de diálogo de propriedades
do projeto (consulte a Figura 12-29).

Figura 12-29. Gerando documentação XML

A seção Saída da guia Construir permite especificar se a documentação XML e o nome do arquivo devem ser
gerados. Especifique o nome do arquivo como XmlComments.xml e construa a biblioteca de classes XmlComments.
Como resultado da compilação, você obterá XmlComments.dll e XmlComments.xml na pasta de saída. A Figura 12-30
mostra XmlComments.xml aberto em um navegador.

382
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Figura 12-30. XmlComments.xml visualizado no navegador

Como você deve ter adivinhado, a documentação em XML é boa, mas precisa ser convertida para um formato mais
legível, como HTML ou arquivos de ajuda. Não há uma maneira interna de fazer isso no Visual Studio. Algumas das maneiras de
gerar arquivos de documentação amigáveis incluem:

•Escrever uma folha de estilo XSLT que transforma a documentação XML em HTML.

• Usando uma ferramenta de terceiros que lê os comentários XML ou o arquivo de documentação XML e
gera documentação legível para você.

Na próxima seção, você usará a última técnica para gerar documentação a partir do XML
comentários.

Usando o Sandcastle Help File Builder para gerar arquivos de ajuda O Sandcastle Help File Builder (SHFB) é

uma ferramenta para gerar documentação a partir dos comentários XML que você aprendeu na seção anterior. Esta ferramenta
está disponível no GitHub em https://github.com/EWSoftware/SHFB.
Você pode alimentar qualquer assembly .NET para a ferramenta e gerar documentação em qualquer um dos seguintes formatos:

•Ajuda HTML 1 (.chm)

•Visualizador da Ajuda do MS (.mshc)

• XML aberto (.docx)

• Remarcação (.md)

•HTML (.htm/.html)

383
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Para usar o SHFB, você deve instalá-lo e suas dependências. O assistente de instalação guia você por todas
as etapas.
Depois de instalar o SHFB, abra-o e crie um novo projeto SHFB selecionando File ÿ New Project. Salve o projeto
recém-criado em alguma pasta. Em seguida, clique com o botão direito do mouse em Documentation Sources no Project
Explorer e selecione o arquivo XmlComments.dll do exemplo anterior. Isso indica que você deseja gerar a documentação
deste assembly. Nesse estágio, sua janela SHFB terá a aparência mostrada na Figura 12-31.

Figura 12-31. Fontes de documentação adicionadas ao SHFB

Observe que o Documentation Sources também lista XmlComments.xml automaticamente. Na lista de formatos de
arquivo de ajuda, marque a opção Somente site. Selecionar esta opção gerará documentação em formato HTML e você
poderá visualizá-la facilmente em qualquer navegador.
Em seguida, selecione Documentation ÿ Build Project no menu e deixe a ferramenta gerar a
documentação para você. Depois que a documentação for gerada, você pode ir para a pasta Help na pasta do projeto SHFB
e iniciar Index.html em qualquer navegador. A Figura 12-32 mostra a documentação gerada para o assembly XmlComments.

384
Machine Translated by Google

Capítulo 12 ÿ XML no .NET Framework

Figura 12-32. Documentação para a classe Calculadora

Como você pode ver, o SHFB converteu seus comentários XML em páginas da Web bem formatadas e estilizadas. Tente
navegar para várias partes da documentação usando a árvore à esquerda.

Resumo
O .NET Framework não apenas permite que você trabalhe com uma variedade de tecnologias XML, mas também usa XML em
muitos lugares. Este capítulo forneceu uma introdução a essas áreas.
Você começou com aplicativos WPF, onde a interface do usuário é definida por XAML. Você também exibiu
dados de um documento XML em um DataGrid de um aplicativo WPF.
ASP.NET usa XML pesadamente para marcação e configuração de controle de servidor. Então você aprendeu sobre
os arquivos de configuração do aplicativo. Especificamente, você aprendeu a armazenar e recuperar definições de
configuração de aplicativos e strings de conexão de banco de dados. O arquivo web.config é um arquivo XML que
armazena informações de configuração do ASP.NET. Você aprendeu como configurar seu site para usar autenticação de
formulários e estado de sessão. Você também aprendeu a configurar páginas de erro personalizadas para lidar com erros
inesperados em seu site.
Por fim, você aprendeu sobre comentários XML — um recurso que pode ajudar na geração de documentação. Você
também usou o Sandcastle Help File Builder (SHFB) para converter os comentários XML em documentação HTML.

385
Machine Translated by Google

CAPÍTULO 13

Trabalhando com LINQ to XML

Tradicionalmente, os sistemas de software usavam diferentes métodos de acesso a dados de diferentes armazenamentos
de dados. Por exemplo, para acessar dados de bancos de dados relacionais, arquivos simples e arquivos XML, os
desenvolvedores precisavam aprender e dominar diferentes modelos de objetos. Esses modelos de objetos geralmente eram
totalmente diferentes uns dos outros em termos de sintaxe de uso e filosofia subjacente. Começando com o .NET Framework 3.5,
a Microsoft introduziu a tecnologia Language Integrated Query (LINQ) que simplifica o incômodo envolvido ao consumir diferentes
fontes de dados. LINQ vem em três tipos principais: LINQ to Objects, LINQ to ADO.NET e LINQ to XML.

Este capítulo aborda os recursos do LINQ to XML junto com uma breve cartilha sobre o LINQ em geral. Lembre-se, no
entanto, que qualquer cobertura detalhada do LINQ está além do escopo deste livro. Este capítulo se restringe aos seguintes
tópicos:
• Breve introdução às expressões LINQ

• Recursos de LINQ para XML

•Quando usar LINQ to XML

•Carregar e salvar documentos XML usando LINQ to XML

•Manipulação de documentos XML usando LINQ to XML

• Validação de documentos XML usando LINQ to XML

•Transformando documentos XML usando LINQ to XML

Visão geral do LINQ


Suponha que você esteja desenvolvendo um aplicativo complexo que lida com dados armazenados no SQL Server, arquivos
separados por vírgula, coleções na memória e arquivos XML. Você recebeu a tarefa de desenvolver um conjunto de classes que
levará os dados para dentro e para fora desses armazenamentos de dados. Como você deve ter adivinhado, provavelmente
acabará com várias estratégias de acesso a dados e modelos de objeto, cada um especializado para um determinado tipo de fonte de dados.
Não seria bom se você pudesse usar apenas uma forma unificada de acessar dados para todas as suas fontes de dados? É
disso que se trata o LINQ. O LINQ fornece uma maneira unificada de acessar dados que residem em coleções na memória,
bancos de dados e arquivos XML. A Figura 13-1 mostra a arquitetura geral do LINQ.

© Bipin Joshi 2017 387


B. Joshi, Beginning XML with C# 7, https://doi.org/10.1007/978-1-4842-3105-0_13
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Figura 13-1. Arquitetura do LINQ

A camada inferior da arquitetura LINQ consiste em fontes de dados habilitadas para LINQ. Uma fonte de dados habilitada para
LINQ normalmente é um objeto que implementa as interfaces genéricas IEnumerable<T> ou IQueryable<T>. Você pode se perguntar
como os bancos de dados relacionais e os arquivos XML implementarão essas interfaces; as classes LINQ to ADO.NET e LINQ to
XML fornecem toda a infraestrutura necessária para extrair os dados dessas fontes para uma coleção que implementa as interfaces
IEnumerable<T> ou IQueryable<T>.
O .NET Framework fornece o que é conhecido como operadores de consulta padrão que funcionam em qualquer fonte
de dados habilitada para LINQ e fornecem recursos como consulta, filtragem, classificação e agrupamento de dados.
Pode-se usar os operadores de consulta padrão diretamente nas fontes de dados LINQ. No entanto, para tornar a vida ainda
mais fácil, o C# fornece palavras-chave e construções de linguagem para executar tarefas LINQ nativamente. Isso significa que
consultar o armazenamento de dados subjacente agora é um recurso integrado à própria linguagem de programação C#. Usando
esses recursos, você pode escrever o que é conhecido como expressões de consulta LINQ ou consultas LINQ.

ÿ Observação Embora você possa usar operadores de consulta padrão para trabalhar com fontes de dados habilitadas para

LINQ, neste capítulo usarei palavras-chave da linguagem C# para escrever as consultas LINQ.

Trabalhando com consultas LINQ


Para ter uma ideia de como o LINQ funciona no nível do código, vamos desenvolver um exemplo simples. Por meio deste
exemplo, você aprenderá a agrupar, classificar, filtrar e selecionar dados da coleção genérica. O aplicativo de exemplo é mostrado
na Figura 13-2.

388
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Figura 13-2. Operações LINQ básicas

A aplicação preenche uma lista genérica de objetos Employee com dados da tabela Employees. A interface
do usuário do aplicativo consiste em uma série de caixas de combinação que listam as colunas da tabela
Employee (EmployeeID, FirstName, LastName, BirthDate e Country). Para agrupar, classificar, filtrar e selecionar
dados da lista, você pode escolher o campo desejado na caixa de combinação e clicar no botão Mostrar. Os resultados
da operação correspondente são mostrados em um controle TextBox somente leitura.
Antes de entrar nos detalhes das consultas LINQ, primeiro você precisa criar a classe Employee. Listagem 13-1
mostra o código completo da classe Employee.

Listagem 13-1. A Classe Empregado

public class Employee


{ public int EmployeeID
{ get; definir; } public string FirstName { get;
definir; } public string Sobrenome { get; definir; }
public DataHoraDataNascimento { get; definir; }
public string País { get; definir; } public static
List<Funcionário> GetEmployees() { SqlConnection
cnn = new SqlConnection(@"fonte de dados=.\sqlexpress;
catálogo inicial=northwind;segurança integrada=true");
cnn.Open(); SqlCommand cmd = new SqlCommand( "SELECIONE id do funcionário,
nome, sobrenome, data de nascimento, país DOS funcionários"); cmd.Connection
= cnn; Leitor SqlDataReader = cmd.ExecuteReader(); List<Funcionário> itens = new
List<Funcionário>();

389
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

while (leitor.Read()) {

Item Funcionário = new Funcionário();


item.EmployeeID = leitor.GetInt32(0); item.FirstName
= leitor.GetString(1); item.LastName =
leitor.GetString(2); item.BirthDate =
leitor.GetDateTime(3); item.País = leitor.GetString(4);
itens.Adicionar(item); } leitor.Close(); cnn.Close();
devolver itens;

}
}

A classe Employee consiste em cinco propriedades públicas, ou seja, EmployeeID, FirstName, LastName,
BirthDate e Country. O método estático GetEmployees() se conecta ao banco de dados Northwind e busca todos os
registros da tabela Employees. Os valores são então preenchidos em uma lista de objetos Employee.
Por fim, a lista é devolvida ao chamador.
Depois de criar a classe Employee, você pode chamar seu método GetEmployees() do evento Load do formulário
(consulte a Listagem 13-2).

Listagem 13-2. Armazenando Objetos Funcionários em uma Lista Genérica

List<Funcionário> funcionários = null;


private void Form1_Load(remetente do objeto, EventArgs e)
{ empregados = Employee.GetEmployees(); }

Observe que na Listagem 13-2 a lista genérica é declarada no nível de classe porque várias funções precisam acessar
os dados. O manipulador de eventos Load do formulário simplesmente chama o método GetEmployees() e armazena os
dados retornados na lista.

Agrupando dados usando LINQ


A Listagem 13-3 mostra o manipulador de eventos Click do botão Mostrar que agrupa os dados com base no campo
selecionado.

Listagem 13-3. Agrupando dados usando LINQ

private void button1_Click(remetente do objeto, EventArgs e) {

txtResults.Clear(); if
(comboBox1.SelectedItem.ToString() == "País") {

var resultado = do funcionário no grupo de funcionários


funcionário por funcionário.País;

390
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

foreach (var grupo no resultado) {

OutputResults("Grupo:" + grupo.Chave);
foreach (funcionário emp no grupo) {

OutputResults(emp); }

}
}
...
}

O código primeiro decide qual campo será usado para fins de agrupamento (vemos aqui o código que trata
do campo país). Em seguida, ele forma uma expressão de consulta LINQ que agrupa os itens da lista com base
nesse campo. Observe o uso de extensões de linguagem C# para LINQ na forma das palavras-chave var, from e
group. Quando você trabalha com LINQ, o tipo de retorno das consultas geralmente é desconhecido no momento
de escrever a consulta. Para indicar esse tipo anônimo, C# usa a palavra-chave var. A consulta LINQ a seguir
indica que você deseja buscar todos os itens da coleção de funcionários e agrupá-los por valores de país. Em
tempo de execução, essa consulta LINQ retorna um dicionário.
O loop foreach externo itera por todos os itens desse dicionário. A cada iteração, a chave do item do
dicionário é exibida na caixa de texto usando o método auxiliar OutputResults() (discutido a seguir).
O loop interno foreach percorre todos os funcionários de um grupo e emite seus detalhes na caixa de texto.
A Listagem 13-3 mostra o código apenas para o campo País, mas os demais campos seguirão a mesma estrutura.
O método auxiliar OutputResults() tem duas sobrecargas, conforme mostrado na Listagem 13-4.

Listagem 13-4. Saída de resultados para uma caixa de texto

private void OutputResults(Employee emp)


{ txtResults.Text += "[" + emp.EmployeeID + "] ";
txtResults.Text += emp.FirstName + + emp.LastName +
""

"(" + emp.BirthDate.ToShortDateString() + ")," + emp.Country +


"\r\n";

private void OutputResults(string msg) {

txtResults.Text += msg + "\r\n";


}

A primeira sobrecarga do método auxiliar OutputResults() aceita um objeto Employee e anexa seus valores de
propriedade ao conteúdo da caixa de texto. A segunda sobrecarga simplesmente aceita um valor de string e o anexa ao
conteúdo da caixa de texto.

391
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Classificando dados usando LINQ

Agora vamos implementar a funcionalidade de classificação. A operação de classificação é feita no manipulador de eventos
Click do segundo botão Mostrar. A parte do manipulador de eventos é mostrada na Listagem 13-5.

Listagem 13-5. Classificando dados usando LINQ

private void button2_Click(remetente do objeto, EventArgs e)


{ txtResults.Clear(); if (comboBox2.SelectedItem.ToString() ==
"País") {

var resultado = do funcionário em funcionários


pedido por funcionário.País
selecionar funcionário;
foreach (var empregado no resultado)
{ OutputResults(funcionário); }

}
...
}

A cláusula orderby de uma consulta LINQ classifica os resultados com base nos campos indicados. O código
na Listagem 13-5 classifica os dados com base nos valores do País. Observe o uso da palavra-chave select que
decide o que buscar. Revisitaremos a palavra-chave select em seções posteriores. O loop foreach itera os resultados
e os emite na caixa de texto.

Filtrando dados usando LINQ


A lista de funcionários contém detalhes sobre todos os funcionários do sistema. Freqüentemente, você precisa trabalhar com um
subconjunto dos dados totais com base em alguns critérios. A cláusula where das consultas LINQ permite filtrar os dados com
base em alguma condição. Em nosso exemplo, o manipulador de eventos Click do terceiro botão Mostrar filtra a lista de funcionários
com base nos critérios inseridos na caixa de texto correspondente. Uma parte do código relevante é mostrada na Listagem 13-6.

Listagem 13-6. Filtrando dados usando LINQ

private void button3_Click(remetente do objeto, EventArgs e)


{ txtResults.Clear(); if (comboBox3.SelectedItem.ToString() ==
"País") { var resultado = de funcionário em funcionários where
funcionário.País==textBox1.Text seleciona funcionário; foreach
(var empregado no resultado) { OutputResults(funcionário); } }

...
}

392
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

O código da Listagem 13-6 filtra a lista de funcionários para um país específico conforme fornecido na caixa de texto.
Nosso exemplo usa o operador == de C#. Você também pode usar o && (AND) e || (OR) para criar condições mais complexas.
Os resultados são então enviados para a caixa de texto como antes.

Moldando dados usando LINQ


Em todas as consultas LINQ até agora, buscamos toda a instância Employee. E se quisermos apenas uma parte da
instância Employee ou alguns dados computados? É aí que a cláusula select é útil. A cláusula select pode ser usada
para controlar a forma do resultado (também conhecida como projeção). Em nossos exemplos, a caixa de combinação
do campo de saída controla qual dos campos será retornado na saída. A forma real da saída é decidida por consultas
LINQ que vão dentro do evento Click do quarto botão Show (consulte a Listagem 13-7).

Listagem 13-7. Selecionando dados usando LINQ

private void button4_Click(remetente do objeto, EventArgs e)


{ txtResults.Clear(); if(comboBox4.SelectedIndex==0) { var result = de
funcionário em funcionários selecione novo

{funcionário.FuncionárioID, funcionário.Nome, funcionário.Sobrenome};


foreach (var emp no resultado)
{ OutputResults("[" + emp.EmployeeID
+ "] " + emp.LastName); emp.FirstName + } } +
""

...
}

O código primeiro decide qual dos campos do objeto Employee deve ser selecionado. Em seguida, ele constrói um
novo objeto anônimo que inclui as propriedades EmployeeID, FirstName e LastName. Os resultados são então exibidos na
caixa de texto.
Agora você pode executar o aplicativo e testar como essas consultas LINQ funcionam.

Tecnologias XML clássicas versus LINQ to XML


Agora que você tem alguma ideia sobre LINQ, vamos nos concentrar em LINQ to XML. Antes de se aprofundar na
codificação real, seria útil entender as principais diferenças entre as tecnologias XML clássicas (XML DOM, XSLT, XSD
etc.) e LINQ to XML. As seções a seguir resumem essas diferenças principais.

Trabalhando com Fragmentos XML


Nas tecnologias XML clássicas, como XML DOM, você trabalha essencialmente com um documento XML, portanto, acessa
os dados XML de cima para baixo. Esse mecanismo pode não ser ideal em todos os cenários, porque ao trabalhar com XML,
seu foco principal está nos elementos e atributos. No entanto, com uma abordagem DOM tradicional, você adiciona uma
camada adicional de complexidade. LINQ for XML permite que você trabalhe com elementos e atributos XML diretamente
sem se preocupar com o nó do documento. Esse acesso direto permite trabalhar com fragmentos XML compostos por
elementos e atributos com mais facilidade.

393
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Construção Visual de Árvores XML


Ao trabalhar com XML DOM, você cria várias partes de um documento XML (elementos, atributos, nós de texto e assim
por diante) e, em seguida, adiciona-os ao documento. Esse estilo de criação de documentos XML não fornece nenhuma
pista visual sobre o aninhamento de documentos. O LINQ to XML fornece o que é conhecido como construção funcional,
que permite criar árvores XML de forma a fornecer uma pista visual sobre o aninhamento da estrutura XML, como você verá
mais adiante neste capítulo.

Facilidade no Manuseio de Namespace


Lidar com namespaces XML e prefixos de namespace é tedioso em tecnologias XML clássicas. O LINQ to XML elimina a
complexidade de lidar com prefixos de namespace dos desenvolvedores, fazendo automaticamente a substituição apropriada
em tempo de execução.

Renomeando nós XML


No DOM, você não pode renomear um nó diretamente. Você precisa criar um novo nó e copiar os filhos do nó mais antigo
para o novo. O LINQ to XML simplifica essa tarefa permitindo que você renomeie os nós diretamente.

Métodos estáticos para carregar XML


Ao trabalhar com DOM, você deve instanciar a classe XmlDocument primeiro e depois carregar o documento XML.
LINQ to XML, no entanto, permite que você carregue dados XML por meio de métodos estáticos, simplificando seu código.

Tratamento de espaços em branco


Quando você lê um arquivo XML do disco, geralmente ele contém espaços em branco na forma de indentações.
Esse espaço em branco não é o seu foco no que diz respeito ao processamento XML. No entanto, ao salvar os dados de
volta, você pode querer preservar os recuos. LINQ to XML simplifica a manipulação de espaços em branco para você. Por
padrão, quando você salva um documento XML, apenas espaços em branco significativos são salvos. Você pode, é claro,
alterar esse comportamento padrão, se desejar.

Transformação XML
XSLT fornece uma abordagem declarativa baseada em regras para transformação de dados XML. No entanto, muitos
desenvolvedores acham difícil dominar o XSLT e gastam uma quantidade significativa de tempo desenvolvendo folhas de
estilo XSLT. LINQ to XML fornece um modelo de programação com o qual a maioria dos desenvolvedores está familiarizada.
Você pode transformar seu documento XML usando construção funcional, reunindo dados de várias fontes e montando-os em
um novo formulário. Isso pode reduzir os custos de desenvolvimento e manutenção. O XSLT ainda é melhor ao lidar com
cenários complexos centrados em documentos.

Quando usar LINQ to XML


Em geral, podemos dizer que as tecnologias XML clássicas são melhores se quisermos que nossos aplicativos sigam os
padrões W3C e modifiquemos os aplicativos existentes que já usam as abordagens clássicas. LINQ to XML pode ser uma
escolha para novos desenvolvimentos onde a conformidade com o W3C não é a preocupação e você deseja codificar com a
sintaxe familiar do C# sem gastar muito tempo dominando a família de tecnologias XML.

394
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Hierarquia de classes LINQ to XML


LINQ to XML fornece uma extensa coleção de classes para uma variedade de propósitos. Essas classes residem no namespace
System.Xml.Linq do assembly System.Xml.Linq.dll. A Figura 13-3 mostra algumas das classes mais comumente usadas dessa hierarquia.

Figura 13-3. Hierarquia de classe LINQ para XML

A classe abstrata XNode representa um nó de uma árvore XML. As classes XContainer, XText, XComment e
XProcessingInstruction herdam da classe XNode. A classe XContainer, por sua vez, atua como uma classe base para as classes
XDocument e XElement. Como você deve ter adivinhado, essas classes representam um nó de texto, comentário, instrução de
processamento, documento XML e elemento, respectivamente. As classes como XAttribute, XComment, XCData e XDeclaration são
independentes da hierarquia XNode e representam atributo, comentário, seção CDATA e declaração XML, respectivamente. A classe
XName representa o nome de um elemento (XElement) ou atributo (XAttribute). Da mesma forma, a classe XNamespace representa
um namespace de um elemento ou atributo.

Abrindo um documento XML existente para análise


Ao trabalhar com XML DOM clássico, você usou a classe XmlDocument para carregar um documento XML. LINQ to XML oferece
duas classes para carregar dados XML: XDocument e XElement. Na maioria das situações, XElement é tudo que você precisa para
carregar arquivos XML, fluxos e fragmentos. No entanto, em alguns casos raros, pode ser necessário usar o XDocument. Esses
casos especiais incluem o seguinte:

•Você deseja adicionar comentários no nível superior.

•Você deseja adicionar instruções de processamento no nível superior.

•Você deseja usar um DTD para seu documento XML.

Em qualquer caso, você pode carregar dados XML de um URI, um TextReader, um XmlReader ou até mesmo uma string XML.
Para demonstrar o uso de XElement no carregamento de dados XML, vamos construir um aplicativo como o mostrado na Figura 13-4.

395
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Figura 13-4. Aplicativo que carrega dados XML em XElement

O aplicativo consiste em quatro botões de opção para selecionar o local de onde os dados XML devem ser carregados.
A caixa de texto permite especificar um URL onde o arquivo XML está localizado ou a marcação XML bruta. Clicar no botão
Carregar carrega os dados em um XElement e exibe uma caixa de mensagem com uma mensagem de sucesso. O
manipulador de eventos Click do botão Load é mostrado na Listagem 13-8.

Listagem 13-8. Carregando dados XML usando XElement

private void button1_Click(remetente do objeto, EventArgs e) {

tentar

XElement raiz = null; if


(radioButton1.Checked) { root =
XElement.Load(textBox1.Text); }
if (radioButton2.Checked) { leitor StreamReader
= File.OpenText(textBox1.Text); raiz =
XElement.Load(leitor); } if (radioButton3.Checked)
{ XmlReader reader = XmlReader.Create(textBox1.Text);
raiz = XElement.Load(leitor); } if (radioButton4.Checked) { root =
XElement.Parse(textBox1.Text); }

MessageBox.Show("Dados XML carregados com sucesso!"); }

396
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

catch (Exceção ex) {

MessageBox.Show(ex.Message); }

O código consiste em uma série de blocos if para verificar o botão de opção selecionado. Os três primeiros blocos if
usam o método Load() da classe XElement. Observe que Load() é um método estático da classe XElement e aceita um
URI, um TextReader ou um XmlReader como fonte de dados XML. Se os dados XML estiverem na forma de uma string
bruta, o método Parse() será usado em vez de Load(). Os métodos Load() e Parse() retornam uma instância da classe
XElement que pode ser usada para processamento adicional.

ÿ Observação Para compilar o código da Listagem 13-8, você deve importar System.IO e System.Xml.
Espaços de nomes Linq . Isso se aplica à maioria dos exemplos ilustrados neste capítulo.

Para testar o aplicativo, você pode usar o mesmo arquivo Employees.xml que criamos no Capítulo 2 (consulte
Listagem 2-2 para a marcação XML completa).

Navegando por uma árvore XML Em LINQ to XML, todos

os nós filhos de um elemento podem ser acessados por meio do método Nodes(). O método Nodes() retorna
uma sequência de IEnumerable<XNode> que pode ser repetida para acessar os nós individuais. Se seu interesse for
puramente em elementos, você pode usar o método Elements() que retorna uma sequência de IEnumerable<XElement>.
As propriedades FirstNode e LastNode permitem acessar o primeiro e o último nós, respectivamente. Da mesma forma,
os descendentes podem ser acessados por meio do método Descendants(). Para ver muitos desses métodos em ação,
desenvolveremos um aplicativo que preenche um controle TreeView com dados do arquivo Employees.xml. A interface do
usuário do aplicativo é mostrada na Figura 13-5.

Figura 13-5. Navegando por uma árvore XML usando LINQ to XML

397
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

O aplicativo consiste em um controle TreeView para exibir vários elementos <employee>. O TreeNode de cada
funcionário exibe ainda mais informações sobre esse funcionário. O código real para preencher a visualização em árvore vai
para o manipulador de eventos Click do botão Load Tree (consulte a Listagem 13-9).

Listagem 13-9. Carregando a árvore com detalhes do funcionário

private void button1_Click(object sender, EventArgs e) { XElement root =


XElement.Load($"{Application.StartupPath}\\Employees.xml"); TreeNode
rootNode = new TreeNode(root.Name.LocalName); treeView1.Nodes.Add(rootNode); foreach(XElement
employee in root.Elements()) { TreeNode employeeNode = new TreeNode("ID do funcionário:" +
employee.Attribute("employeeid").Value); rootNode.Nodes.Add(employeeNode); if (employee.HasElements)
{ foreach(XElement employeechild in employee.Descendants()) { TreeNode childNode = new
TreeNode(employeechild.Value); EmployeesNode.Nodes.Add(childNode); } } } }

O código primeiro carrega o arquivo Employees.xml em uma instância da classe XElement. A propriedade Nome de
a classe XElement é do tipo XName e representa o nome do elemento. A propriedade LocalName da classe XName retorna o
nome do elemento subjacente sem um qualificador de namespace. Esse nome é usado para adicionar o nó raiz do TreeView.

Como estamos interessados apenas nos elementos, o código usa o método Elements() para recuperar todos os
elementos filhos do elemento <employees>. O atributo employeeid do elemento <employee> é acessado por meio do método
Attribute(), que aceita o nome do atributo cujo valor deve ser recuperado e retorna uma instância XAttribute representando esse
atributo. A propriedade Value da classe XAttribute nos dá o valor do atributo. Um TreeNode para esse funcionário é adicionado
ao TreeView.
A propriedade booleana HasElements da classe XElement nos informa se um elemento tem algum elemento
filho. Nesse caso, iteramos por todos os descendentes do elemento <employee> atual usando seu método Descendants().
Em nosso exemplo, o método Descendants() retornará quatro XElements para os elementos <firstname>, <lastname>,
<homephone> e <notes>, respectivamente. A propriedade Value da classe XElement retorna o conteúdo interno do
elemento, que é exibido em um TreeNode.

ÿ Observação No exemplo anterior, a estrutura do documento XML era conhecida por nós. Nos casos em que o
aninhamento de tags XML não é conhecido em tempo de design, você pode usar um código recursivo que atravesse toda
a hierarquia aninhada.

398
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Procurando por elementos e atributos específicos


Pesquisar dados XML é um requisito comum em muitos cenários. LINQ to XML fornece maneiras poderosas de pesquisar seus dados.
Você pode usar todos os recursos do LINQ para criar suas consultas e pesquisar os dados XML. Mais comumente, precisamos pesquisar:

•Elementos que correspondem a um nome de tag específico

•Elementos contendo um valor específico

• Atributos que correspondem a um valor específico

Nas seções a seguir, desenvolvemos dois aplicativos: um permite pesquisar elementos com base
de seus nomes de marca e o outro permite que você pegue um funcionário específico com um ID de funcionário específico.

Recuperando elementos específicos usando o método Descendants() Quando você deseja recuperar um conjunto de

elementos com um determinado nome de tag, o método Descendants() pode ser útil. Para ilustrar o uso do método Descendants(),
desenvolvemos um aplicativo como o mostrado na Figura 13-6.

Figura 13-6. Aplicativo para pesquisar elementos por nome de tag

O aplicativo consiste em uma caixa de texto para especificar o nome da tag a ser pesquisado. Ao clicar no botão Pesquisar, os
resultados serão exibidos em uma caixa de listagem. Selecionar um nome de tag específico na caixa de listagem mostrará seu valor em
outra caixa de texto somente leitura. A caixa de seleção intitulada “Find Only the First Occurrence” determina se deve encontrar todas as
instâncias do nome da tag ou encerrar a pesquisa depois que a primeira correspondência for encontrada.
O código que faz o aplicativo funcionar é mostrado na Listagem 13-10.

Listagem 13-10. Usando o método Descendants() para pesquisar elementos específicos

XElement raiz = null;


fonte de dados XElement[] = null;

private void Form1_Load(remetente do objeto, EventArgs e) { root =


XElement.Load($"{Application.StartupPath}\\employees.xml"); }

399
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

private void button1_Click(remetente do objeto, EventArgs e) {

textBox2.Text = ""; var


subconjunto = do item em root.Descendants(textBox1.Text) selecione o item;

if (!checkBox1.Checked) {

fonte de dados = subconjunto.ToArray(); }


outro {

if (subset.Count() > 0) {

fonte de dados = novo XElement[1]; fonte


de dados[0] = subconjunto.Primeiro(); }

} listBox1.DataSource = fonte de dados;


listBox1.DisplayMember = "Nome"; } private
void listBox1_SelectedIndexChanged(remetente
do objeto, EventArgs e) {

textBox2.Text= fonte de dados[listBox1.SelectedIndex].Value;


}

O código declara duas variáveis de nível de classe: root e datasource. A variável raiz é do tipo XElement e contém
um ponteiro para o nó raiz de Employees.xml. A variável datasource é uma matriz do tipo XElement e é usada para vinculação
de dados. O arquivo Employees.xml é carregado na variável raiz no evento Load do formulário.

A tarefa principal de pesquisar um nome de tag ocorre no manipulador de eventos Click do botão Pesquisar. Observe a
consulta LINQ mostrada em negrito. Ele usa o método Descendants() da classe XElement. O método Descendants() tem duas
sobrecargas: uma que não usa nenhum parâmetro e outra que usa um nome de elemento como parâmetro. A primeira sobrecarga
retorna todos os elementos descendentes do nó atual, enquanto a última sobrecarga retorna todos os elementos descendentes
que correspondem ao nome da marca especificada. Se a caixa de seleção estiver desmarcada (ou seja, queremos exibir todas as
ocorrências da tag especificada), convertemos o resultado em uma matriz de objetos XElement usando o método ToArray(). Caso
contrário, criamos um array de XElements manualmente e armazenamos nele a primeira ocorrência do elemento. Observe o uso do
método First() para recuperar apenas a primeira instância do elemento. A variável da fonte de dados é então vinculada à caixa de
listagem.
Para exibir o valor real do elemento, o código manipula o evento SelectedIndexChanged
da caixa de listagem. Dentro dele, ele simplesmente recupera o XElement selecionado do array de fonte de dados e exibe sua
propriedade Value na caixa de texto.
Para testar o aplicativo, execute-o no Visual Studio. Digite o nome na caixa de texto de pesquisa e clique no botão
Pesquisar. O aplicativo deve listar três ocorrências da tag <firstname> na caixa de listagem. Clique em qualquer uma das instâncias
e você verá seu valor exibido na outra caixa de texto.

Pesquisa com base em valores de atributo Os dados XML geralmente precisam

ser pesquisados com base em valores de atributo e não apenas em nomes de tags. Os métodos Attributes() e
Attribute() ajudam você a fazer exatamente isso. Para demonstrar o uso desses métodos, desenvolveremos um aplicativo como o
mostrado na Figura 13-7.

400
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Figura 13-7. Aplicativo para pesquisar valores de atributos

O aplicativo consiste em uma caixa de combinação que exibe uma lista de IDs de funcionários. Lembre-se de
que o arquivo Employees.xml armazena IDs de funcionários no atributo employeeid do elemento <employee>. Ao
selecionar um ID de funcionário específico e clicar no botão Mostrar, os detalhes desse funcionário (nome, sobrenome, telefone
residencial e notas) são exibidos. O código que faz a busca é mostrado na Listagem 13-11.

Listagem 13-11. Pesquisando Valores de Atributos

private void Form1_Load(remetente do objeto, EventArgs e) { root =


XElement.Load($"{Application.StartupPath}\\employees.xml"); var result
= from item in root.Elements("employee")

where item.Attributes("employeeid").Count() > 0 selecione


item.Attribute("employeeid").Value;
foreach (var obj no resultado)
{ comboBox1.Items.Add(obj); } }

private void button1_Click(object sender, EventArgs e) { var result = from


item in root.Elements("employee")

onde item.Attribute("id do funcionário").


Valor == comboBox1.SelectedItem.ToString() selecionar
item; foreach (var obj no resultado) { label6.Text =
obj.Element("firstname").Value; label7.Text = obj.Element("sobrenome").Value; label8.Text
= obj.Element("telefone residencial").Value; label9.Text = obj.Element("notas").Value; } }

401
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

O código carrega o arquivo Employees.xml em uma instância de XElement no evento Load do formulário. Em seguida,
ele chama o método Elements() no elemento raiz, fornecendo o nome da tag como funcionário. Dessa forma, são retornados
apenas os elementos com o nome da tag do funcionário. A cláusula where verifica então se esses elementos contêm o atributo
employeeid e, em caso afirmativo, retorna o valor do atributo employeeid.
O método Attributes() tem duas sobrecargas: uma não aceita parâmetros e a outra aceita o nome do atributo como
parâmetro. A primeira sobrecarga retorna todos os atributos (na forma de uma coleção XAttribute) de um elemento, enquanto a
última retorna apenas os atributos com o nome especificado. O método Attribute() aceita o nome de um atributo a ser recuperado e
retorna uma instância XAttribute associada a ele. Observe que a consulta seleciona apenas o valor do atributo. O loop foreach, em
seguida, itera pelos resultados e preenche os IDs dos funcionários na caixa de combinação.

No manipulador de eventos Click do botão Mostrar, o código busca um elemento cujo atributo employeeid
corresponde ao valor selecionado na caixa de combinação. A propriedade Value da classe XAttribute representa o valor de um
atributo. Os resultados são então exibidos nos controles Label.

Modificando Dados XML


LINQ to XML fornece maneiras fáceis de adicionar, remover e alterar o conteúdo de uma árvore XML. Essas maneiras podem
ser melhor vistas com um exemplo. Vamos criar um aplicativo como o mostrado na Figura 13-8 , que representa uma tela de
entrada de dados para o arquivo Employees.xml.

Figura 13-8. Tela de entrada de dados para o arquivo Employees.xml

O aplicativo exibe os elementos <employee> do arquivo Employees.xml. Você pode navegar entre vários funcionários
usando os controles do VCR fornecidos na parte inferior. Você também pode pular para um funcionário com um ID de funcionário
específico usando a caixa de combinação. Os botões Adicionar, Atualizar e Excluir adicionam, alteram ou removem um elemento
<employee> da árvore XML subjacente, respectivamente. O botão Salvar salva a árvore XML modificada de volta no arquivo
Employees.xml.

402
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Carregando o documento XML


Quando o aplicativo é iniciado, ele carrega o arquivo Employees.xml em uma instância da classe XElement. Isso é feito no
evento Load do formulário (consulte a Listagem 13-12).

Listagem 13-12. Carregando um documento XML

Documento XElement = null;

private void Form1_Load(remetente do objeto, EventArgs e) {

doc=XElement.Load($"{Application.StartupPath}\\Employees.xml"); var result=from


item in doc.Descendants("employee") select item.Attribute("employeeid").Value;

foreach (var obj no resultado) {

comboBox1.Items.Add(obj); }

FillControls(); }

O código declara uma variável do tipo XElement no nível do formulário. No evento Load, ele carrega Employees.xml nele.
Os elementos descendentes do nó raiz (ou seja, todos os elementos <employee>) são recuperados usando o método
Descendants() e seus atributos são adicionados à caixa de combinação. O método FillControls() (discutido posteriormente)
simplesmente busca o elemento <employee> correspondente ao ID do funcionário atualmente selecionado e exibe seus detalhes
(nome, sobrenome, telefone residencial e anotações) nas caixas de texto apropriadas.

Navegando entre vários nós A disposição do videocassete na

parte inferior da tela permite que você navegue entre vários elementos <employee>. Os manipuladores de evento Click
dos botões próximo, anterior, primeiro e último (consulte a Listagem 13-13) ajustam essencialmente o índice selecionado da caixa
de combinação e chamam o método auxiliar FillControls() para mostrar os detalhes do funcionário selecionado.

Listagem 13-13. Navegando entre os elementos do funcionário

private void button4_Click(remetente do objeto, EventArgs e) {

comboBox1.SelectedIndex = 0;
FillControls(); }

private void button5_Click(object sender, EventArgs e) { if


(comboBox1.SelectedIndex > 0) {

comboBox1.SelectedIndex = comboBox1.SelectedIndex - 1; }

FillControls(); }

403
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

botão void privado6_Click(remetente do objeto, EventArgs e) {

if (comboBox1.SelectedIndex < comboBox1.Items.Count - 1) {

comboBox1.SelectedIndex = comboBox1.SelectedIndex + 1;
}
FillControls(); }

private void button7_Click(remetente do objeto, EventArgs e) {

comboBox1.SelectedIndex = comboBox1.Items.Count - 1;
FillControls(); }

Adicionando Novo Conteúdo Para

adicionar um novo funcionário, precisamos especificar o ID do funcionário, nome, sobrenome e notas nos
respectivos controles e clicar no botão Adicionar. Dentro do evento Click do botão Adicionar, precisamos criar
novas instâncias de XElement que representam os elementos <employee>, <firstname>, <lastname>, <homephone>
e <notes>. A Listagem 13-14 mostra como isso é feito.

Listagem 13-14. Adicionando um novo elemento de funcionário

private void button1_Click(remetente do objeto, EventArgs e) {

XElement empregado = new XElement("funcionário",


new XElement("firstname", textBox1.Text), new
XElement("lastname", textBox2.Text), new
XElement("homephone", textBox3.Text), new
XElement("notas", new XCData(textBox4.Text) ));

empregado.SetAttributeValue("codigodofuncionário", comboBox1.Text); doc.Add(funcionário);


comboBox1.Items.Add(comboBox1.Text); FillControls(); }

Observe cuidadosamente a marcação na Listagem 13-14 . O código cria uma nova instância da classe
XElement usando construção funcional. O código escrito usando construção funcional fornece uma indicação visual
sobre o aninhamento da árvore XML. Depois de examinar nosso código, você pode identificar facilmente que
<firstname>, <lastname>, <homephone> e <notes> são os elementos filhos do elemento <employee>. O construtor da
classe XElement aceita uma matriz de parâmetros de elementos de conteúdo. O código fornece quatro novas instâncias
da classe XElement como o segundo parâmetro da instância principal de XElement. Como o elemento que representa
notas pode conter CDATA, seu valor é passado como uma instância da classe XCData.
O atributo employeeid do elemento <employee> recém-criado é atribuído por meio do método
SetAttributeValue(). O método SetAttributeValue() aceita um nome de atributo e seu valor como parâmetro. Se esse
atributo já existir, o método altera o valor do atributo; caso contrário, adiciona o atributo.

O elemento <employee> é adicionado ao elemento raiz usando o método Add() do XElement


aula. O método Add() adiciona o conteúdo fornecido como o elemento filho do elemento atual.

404
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Modificando o Conteúdo Existente


Quaisquer alterações nos valores dos elementos <firstname>, <lastname>, <homephone> e <notes> são salvas na árvore XML
no evento Click do botão Update (consulte a Listagem 13-15).

Listagem 13-15. Modificando um elemento existente

private void button2_Click(remetente do objeto, EventArgs e) {

string id do funcionário = comboBox1.SelectedItem.ToString(); var


empregados = do item em doc.Descendants("funcionário") where
item.Attribute("id do funcionário").Value == id do funcionário selecionar item;

foreach (var empregado em empregados) {

empregado.SetElementValue("primeiro nome", textBox1.Text);


empregado.SetElementValue("sobrenome", textBox2.Text);
empregado.SetElementValue("telefone residencial", textBox3.Text);
empregado.SetElementValue("notas", textBox4.Text); }

O código primeiro seleciona apenas o elemento <employee> cujo atributo employeeid corresponde ao selecionado
na caixa de combinação. Em seguida, ele chama o método SetElementValue() da classe XElement no funcionário
selecionado. O método SetElementValue() recebe dois parâmetros: o nome do elemento cujo valor deve ser definido e o novo
valor do elemento. Se o elemento não existir, ele será adicionado como um elemento filho do elemento atual.

Excluindo o conteúdo existente Para excluir

um funcionário, precisamos localizá-lo nos elementos <employee> disponíveis e removê-lo da árvore XML. O manipulador
de eventos Click do botão Delete faz essa tarefa (consulte a Listagem 13-16).

Listagem 13-16. Excluindo um elemento existente

private void button3_Click(remetente do objeto, EventArgs e) { string


employeeid = comboBox1.SelectedItem.ToString(); var empregados = do
item em doc.Descendants("funcionário") where item.Attribute("id do
funcionário").Value == id do funcionário selecionar item; foreach (var
empregado em empregados) { empregado.Remover(); quebrar; }
comboBox1.Items.Remove(employeeid); FillControls(); }

O código primeiro encontra o funcionário que deve ser excluído. Em seguida, ele chama o método Remove() no
funcionário XElement. O método Remove() remove o elemento atual da árvore.

405
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Salvando a árvore XML modificada em um arquivo A


árvore XML modificada pode ser salva de volta no disco usando o método Save() da classe XElement.
A Listagem 13-17 mostra o código relevante.

Listagem 13-17. Salvando uma árvore XML em um arquivo

private void button8_Click(remetente do objeto, EventArgs e) {

doc.Save($"{Application.StartupPath}\\Employees.xml"); }

O método Save() simplesmente aceita o caminho do arquivo onde a árvore XML será salva. Além de salvar uma
árvore XML em um arquivo, você também pode usar o método Save() para serializar uma árvore XML em objetos XmlWriter
ou TextWriter.

Exibindo os detalhes do funcionário No


código anterior, usamos o método auxiliar FillControls() para exibir os detalhes do funcionário atual.
Esse método é mostrado na Listagem 13-18.

Listagem 13-18. Exibição dos detalhes do funcionário atual

private void FillControls() { if


(comboBox1.SelectedIndex == -1) {

comboBox1.SelectedIndex = 0; } string
id do funcionário =
comboBox1.SelectedItem.ToString(); var empregados = do item em
doc.Elements() where item.Attribute("employeeid").Value == employeeid
selecionar item; foreach (var empregado em empregados) { textBox1.Text
= empregado.Element("nome").Value; textBox2.Text =
empregado.Element("sobrenome").Value; textBox3.Text = empregado.Element("telefone
residencial").Value; textBox4.Text = empregado.Element("notas").Value; } label6.Text =
$"Empregado {(comboBox1.SelectedIndex + 1)} de {comboBox1.Items.Count}"; }

O método auxiliar FillControls() simplesmente recupera o elemento <employee> cujo id do funcionário


atributo é selecionado na caixa de combinação. Em seguida, ele preenche as caixas de texto com os valores
dos elementos <firstname>, <lastname>, <homephone> e <notes>. O método Element() aceita o nome de um elemento
XML e retorna a primeira ocorrência desse elemento como uma ocorrência de XElement.
Por fim, o rótulo de status do visor do videocassete é atualizado para refletir a posição atual do funcionário.

406
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

ÿ Observação Além dos métodos SetElementValue() e SetAttributeValue() , você também pode usar a
propriedade Value das classes XElement e XAttribute para definir o valor de um elemento ou atributo
existente, respectivamente. Da mesma forma, além do método Add() , você pode usar os métodos
AddBeforeSelf() e AddAfterSelf() para adicionar conteúdo antes e depois do elemento atual, respectivamente.

Eventos da Classe XElement


Sempre que qualquer um dos elementos é adicionado, removido ou renomeado, a classe XElement gera os eventos
Changing e Changed. O evento Changing é gerado logo antes da alteração real ocorrer, enquanto o evento Changed é
gerado depois que a alteração é feita. Esses eventos oferecem a oportunidade de realizar pré-operações e pós-operações
relacionadas à mudança.
Para ilustrar o uso desses eventos, vamos modificar o aplicativo anterior para adicionar suporte aos eventos Alterando e
Alterado.
Modifique o evento Load do formulário para refletir as mudanças mostradas na Listagem 13-19.

Listagem 13-19. Fiação Alterando e Manipuladores de Eventos Alterados

private void Form1_Load(remetente do objeto, EventArgs e)


{ doc=XElement.Load($"{Application.StartupPath}\\employees.xml");

doc.Changing += new EventHandler<XObjectChangeEventArgs>(doc_Changing); doc.Changed


+= new EventHandler<XObjectChangeEventArgs>(doc_Changed);

var result=from item in doc.Descendants("employee") select


item.Attribute("employeeid").Value; foreach (var obj no
resultado) { comboBox1.Items.Add(obj); }

FillControls(); }

Observe o código marcado em negrito. O código liga os manipuladores de eventos para os eventos Alterando e Alterado.
Ambos os eventos recebem um parâmetro de argumento de evento do tipo XObjectChangeEventArgs. O manipulador de
eventos para o manipulador de eventos doc_Changed é mostrado na Listagem 13-20.

Listagem 13-20. Manipulando um evento alterado de XElement

void doc_Changed(remetente do objeto, XObjectChangeEventArgs e) {

string msg = ""; switch


(e.ObjectChange) {

case XObjectChange.Add: msg


= "Um novo elemento foi adicionado"; quebrar;

407
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

case XObjectChange.Remove: msg


= "Um elemento foi removido"; quebrar; case
XObjectChange.Name: msg = "Um elemento foi
renomeado"; quebrar; case XObjectChange.Value:
msg = "O valor foi alterado"; quebrar;

}
MessageBox.Show(msg); }

A classe XObjectChangeEventArgs simplesmente fornece uma dica sobre a causa do evento. O possível
as causas são adicionar um elemento (Adicionar), remover um elemento (Remover), renomear um elemento (Nome) e
alterar o valor de um elemento (Valor). Dependendo da causa, o código pode simplesmente exibir uma mensagem para o
usuário.
Para testar esses eventos, execute o aplicativo, altere os detalhes do funcionário e clique no botão Atualizar.
Você deverá ver uma série de caixas de mensagens informando sobre as alterações. A Figura 13-9 mostra um exemplo de
execução do aplicativo.

Figura 13-9. Evento alterado gerado após modificar as informações de um funcionário

Lidando com espaços em branco


O comportamento do espaço em branco pode ser controlado quando você chama os métodos Load(), Parse() e Save() em
XElement ou XDocument. Os métodos Load() e Parse() fornecem sobrecargas que aceitam um parâmetro do tipo LoadOptions.
A enumeração LoadOptions permite que você especifique se deve ou não preservar o espaço em branco ao carregar a árvore
XML. Da mesma forma, o método Save() fornece uma sobrecarga que aceita um parâmetro do tipo SaveOptions. A enumeração
SaveOptions permite especificar se os espaços devem ser preservados durante a serialização da árvore XML.

408
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Para ilustrar o uso das enumerações LoadOptions e SaveOptions, vamos desenvolver um aplicativo como o mostrado na
Figura 13-10.

Figura 13-10. Aplicativo para demonstrar o tratamento de espaços em branco

O aplicativo carrega o arquivo Employees.xml quando você clica no botão Carregar. A caixa de seleção Preservar
espaços em branco indica se queremos preservar os espaços em branco durante o carregamento e salvamento do documento.
O manipulador de eventos Click do botão Load é mostrado na Listagem 13-21.

Listagem 13-21. Usando enumerações LoadOptions e SaveOptions

private void button1_Click(object sender, EventArgs e) { XElement root = null;


string path= $"{Application.StartupPath}\\employees.xml"; if (!
checkBox1.Checked) { root=XElement.Load(path, LoadOptions.None);
MessageBox.Show(root.ToString()); root.Save(caminho, SaveOptions.None); }
else { root=XElement.Load(path, LoadOptions.PreserveWhitespace);
MessageBox.Show(root.ToString()); root.Save(caminho,
SaveOptions.DisableFormatting); } }

O código carrega o arquivo Employees.xml em uma instância da classe XElement. Dependendo do estado da caixa de
seleção, ele decide o valor LoadOptions apropriado. O valor de None indica que não pretendemos preservar espaços em branco
insignificantes, onde o valor de PreserveWhitespace indica que desejamos preservar espaços em branco. Da mesma forma, ao
salvar o documento de volta no disco, o valor SaveOptions controla o comportamento do espaço em branco. O valor de None
indica que o documento será recuado e os espaços em branco insignificantes não serão preservados, enquanto o valor de
DisableFormatting indica que não queremos recuar o documento e os espaços em branco serão preservados como estão. O
conteúdo XML carregado no XElement é exibido em uma caixa de mensagem usando o método ToString() da classe XElement.

Tente executar o aplicativo com a caixa de seleção desmarcada (consulte a Figura 13-11) e marcada (consulte
Figura 13-12). Observe que a caixa de mensagem na Figura 13-12 preservou o espaço em branco.

409
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Figura 13-11. Saída com LoadOptions.None

Figura 13-12. Saída com LoadOptions.PreserveWhitespace


410
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

ÿ Nota Lembre-se de que, assim como o XML DOM, o processo de análise será afetado quando você preservar
espaços em branco (consulte a discussão do Capítulo 2 na seção “Lidando com espaços em branco”). No entanto, como
o LINQ to XML permite consultar a árvore XML de maneiras flexíveis, o espaço em branco não criará nenhum problema,
desde que você não dependa da contagem de elementos em sua lógica.

Lidando com Namespaces


Às vezes, os dados XML que você está processando podem estar usando namespaces XML. Nesses casos, você pode
querer descobrir informações sobre o namespace que os dados XML estão usando. A propriedade Name da classe
XElement fornece um local conveniente para obter todas as informações sobre namespaces.
Para ilustrar o uso da propriedade Name, criaremos um aplicativo conforme mostrado na Figura 13-13.

Figura 13-13. Obtendo informações de namespace

O aplicativo consiste em um botão Carregar que carrega o arquivo Employees.xml. Os detalhes do namespace são
exibidos nos controles Label. Neste exemplo, usamos o arquivo Employees.xml da Listagem 2-21 no Capítulo 2. Uma parte do
arquivo completo é fornecida na Listagem 13-22.

Listagem 13-22. Employees.xml com um Namespace

<emp:employees xmlns:emp=" http://localhost/linqxml">


<emp:employee employeeid="1">
<emp:firstname>Nancy</emp:firstname>
<emp:lastname>Davolio</emp:lastname>
<emp:homephone>(206) 555-9857</emp:homephone >
...

Várias tags de marcação desta listagem são qualificadas por namespace. O manipulador de eventos Click do botão Load
é mostrado na Listagem 13-23.

Listagem 13-23. Usando a propriedade Name para obter informações de namespace

private void button1_Click(object sender, EventArgs e) { XElement root =


XElement.Load($"{Application.StartupPath}\\employees.xml"); label4.Text
= root.Name.NamespaceName; label5.Text = root.Name.LocalName; label6.Text = root.Name.ToString(); }

411
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

O código carrega o arquivo Employees.xml em uma instância da classe XElement. A propriedade Name de
XElement é do tipo XName. A propriedade Namespace da classe XName fornece o nome completo do namespace. O
LocalName fornece o nome dos elementos sem o qualificador de namespace, por exemplo, funcionário, enquanto chamar o
método ToString() retorna o nome do elemento junto com o qualificador de namespace. Observe como o nome do elemento
qualificado pelo namespace é mostrado na Figura 13-13. O namespace é colocado entre chaves ( { e } ) seguido do nome local
do elemento.

Especificando namespaces durante a construção de elementos Ao criar uma árvore XML

programaticamente, você precisa especificar as informações de namespace associadas a ela usando a classe XNamespace.
Para ilustrar como isso é feito, desenvolveremos um aplicativo como o mostrado na Figura 13-14.

Figura 13-14. Aplicativo que cria uma árvore XML com um namespace

O aplicativo consiste em quatro caixas de texto para inserir um nome de namespace, um prefixo de namespace (se
houver), um nome e um sobrenome. Clicar no botão Create XML Tree cria instâncias XElement e XAttribute conforme
necessário e exibe a árvore XML resultante em uma caixa de mensagem. O manipulador de eventos Click do botão é mostrado
na Listagem 13-24.

Listagem 13-24. Usando XNamespace para especificar informações de namespace

private void button1_Click(remetente do objeto, EventArgs e) {

XNamespace ns = textBox1.Text;
XElement root = new XElement(ns + "funcionário",
new XElement(ns + "firstname",textBox3.Text), new
XElement(ns + "lastname",textBox4.Text) );

if (textBox2.Text == "")
{ root.SetAttributeValue("xmlns",
ns);

} else

{ root.SetAttributeValue(XNamespace.Xmlns + textBox2.Text, ns); }

MessageBox.Show(root.ToString()); }

412
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

O código cria uma instância da classe XNamespace e define o nome do namespace conforme indicado na caixa de
texto. Observe que o operador = está sobrecarregado e, portanto, você pode atribuir um valor de string diretamente à sua
instância. O elemento <employee> é então criado. Ao especificar um nome de elemento, o código anexa o nome do elemento ao
namespace. Se um prefixo de namespace não for especificado, um atributo com o nome xmlns será adicionado ao elemento raiz.
Dessa forma, o código define um namespace padrão para a árvore XML. Se um prefixo de namespace for especificado, esse prefixo
será anexado ao URI xmlns (consulte http://www.w3.org/2000/xmlns/ para obter mais informações sobre xmlns). Observe o uso da
propriedade XNamespace.Xmlns, que retorna um objeto XNamespace correspondente ao URI xmlns. Finalmente, a árvore XML
resultante é mostrada usando uma caixa de mensagem.
A Figura 13-15 mostra um exemplo de geração de uma árvore XML.

Figura 13-15. Árvore XML com informações de namespace

Validando Documentos XML


Os documentos XML geralmente são validados em esquemas XML para garantir a exatidão dos dados. No Capítulo 5, você
aprendeu técnicas de validação usando XmlDocument, XmlReader e XPathNavigator. Agora é hora de ver como LINQ to XML
pode ser usado para validar documentos XML em esquemas XML. As classes XDocument e XElement fornecem um método de
extensão chamado Validate() que faz o trabalho de validar os dados XML em relação ao esquema XML. Lembre-se que, para
acessar o método de extensão Validate(), você deve importar o namespace System.Xml.Schema em seu código.

ÿ Os métodos de extensão de observação permitem adicionar métodos a uma classe existente sem herdar ou modificar
a classe original. Eles são um tipo especial de métodos estáticos que são chamados como se fossem métodos de instância
na classe estendida.

Para ilustrar o uso do método Validate(), vamos criar uma aplicação como a mostrada na
Figura 13-16.

413
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Figura 13-16. Aplicativo que valida XDocument em relação a um esquema XML

O aplicativo consiste em duas caixas de texto e um botão. As caixas de texto aceitam um caminho para o
documento XML a ser validado e um caminho para o arquivo XML Schema. Clicar no botão Validar carrega o
documento XML em uma instância XDocument e valida o documento em relação ao esquema fornecido. Erros durante
a validação (se houver) são relatados ao usuário. Neste exemplo, usaremos o arquivo de esquema Employees.xsd
que desenvolvemos no Capítulo 5 para validar o arquivo Employees.xml. Apenas para dar uma rápida recapitulação
da estrutura do arquivo XSD Schema, a Listagem 13-25 mostra uma parte dele.

Listagem 13-25. Marcação parcial de Employees.xsd

<?xml version="1.0" encoding="utf-8"?>


<xs:schema attributeFormDefault="unqualified"
elementFormDefault="qualified" xmlns:xs="http://
www.w3.org/2001/XMLSchema" >
<xs:element name="employees">
<xs:complexType> <xs:sequence>
<xs:element name="employee"
type="EmployeeType" minOccurs="0" maxOccurs="unbounded" /> </xs:
sequence> </xs:complexType> </xs:element> <xs:complexType
name="EmployeeType"> <xs:all> <xs:element name="firstname"
type="NameSimpleType" /> <xs:element name ="notes" type="NotesSimpleType" />
<xs:element name="lastname" type="NameSimpleType" /> <xs:element
name="homephone" type="PhoneSimpleType" /> </xs:all>

...

O manipulador de eventos Click do botão Validar é mostrado na Listagem 13-26.

Listagem 13-26. Validando contra o esquema XSD

private void button1_Click(object sender, EventArgs e) { XDocument


doc = XDocument.Load(textBox1.Text); esquema XmlSchemaSet=novo
XmlSchemaSet();

414
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

schema.Add(null,textBox2.Text);
manipulador ValidationEventHandler = new ValidationEventHandler(MyHandler);
doc.Validate(esquema, manipulador); }

public void MyHandler(remetente do objeto, ValidationEventArgs e) {

MessageBox.Show(e.Message); }

O código carrega o documento XML de origem em uma instância da classe XDocument. Em seguida, ele cria
um XmlSchemaSet e adiciona o esquema XML especificado a ele usando o método Add(). O método de extensão Validate()
da classe XDocument aceita dois parâmetros: XmlSchemaSet e um manipulador de eventos que é chamado se a validação
falhar. O manipulador de eventos é do tipo ValidationEventHandler. O método MyHandler() atua como o manipulador de
eventos em nosso exemplo. A validação é acionada chamando o método Validate(). Se houver algum erro de validação, o
manipulador de eventos MyHandler() será invocado. O manipulador de eventos MyHandler() recebe um parâmetro do tipo
ValidationEventArgs. A propriedade Message da classe ValidationEventArgs fornece informações sobre os erros de validação.

Para testar o código, remova o atributo employeeid do primeiro elemento <employee> do arquivo Employees.xml
e execute o aplicativo. A Figura 13-17 mostra uma mensagem de erro de validação após uma execução de amostra.

Figura 13-17. Um erro de validação

Transformando Árvores XML


No Capítulo 6, discutimos como transformar dados XML usando folhas de estilo XSLT. Embora essa abordagem ainda
seja válida, LINQ to XML fornece uma nova maneira prática de transformar dados XML. Usando essa nova abordagem,
você pode transformar o XML de uma forma em outra sem saber nada sobre XSLT.

ÿ Observação A forma de um documento XML refere-se aos nomes de seus elementos, nomes de atributos e aninhamento de

sua hierarquia.

Há dois cenários comuns na transformação de XML:

•Você pode querer transformar uma árvore XML em uma árvore HTML. Da mesma forma, você pode
querer transformar uma árvore XML em outra árvore XML com uma forma totalmente diferente.

•Você pode querer projetar o código XML de origem em um tipo totalmente diferente.

Em qualquer caso, LINQ to XML, juntamente com seu recurso de construção funcional, fornece uma maneira
fácil de realizar sua tarefa.

415
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Alterar a forma de uma árvore XML


Sua transformação pode exigir uma mudança na forma da árvore XML de origem. Este é um cenário comum
quando você deseja transformar XML em HTML para exibição no navegador. Isso também pode ser necessário
durante a transferência de dados XML de um sistema de software para outro quando o outro sistema espera uma
forma XML diferente. Para demonstrar como a construção funcional em LINQ to XML facilita a realização dessas
alterações na forma, criaremos um aplicativo como o mostrado na Figura 13-18.

Figura 13-18. Aplicativo que transforma XML em HTML

O aplicativo consiste em um botão intitulado Transform XML to HTML e uma caixa de seleção. Ao clicar no
botão, os dados XML de Employees.xml são transformados em uma tabela HTML e salvos como Employees. htm.
Se a caixa de seleção estiver marcada, o arquivo Employees.htm será exibido no navegador.
O código que faz o trabalho de transformar os dados é mostrado na Listagem 13-27.

Listagem 13-27. Transformando uma árvore XML usando construção funcional

private void button1_Click(object sender, EventArgs e) { XElement


root = XElement.Load($"{Application.StartupPath}\\employees.xml");

XElement html =
novo XElement("html",
new XElement("body",
new XElement("table",
new XAttribute("border",1), new
XElement("th", "ID do funcionário"), new
XElement("th", "First Name"), new
XElement("th", "LastName"), new
XElement("th", "Home Phone"), new
XElement("th", "Notas"),
do item na raiz.Descendants("employee") selecione
new XElement("tr", new XElement("td",

item.Attribute("employeeid").Value), new XElement("td",


item.Element(" nome").Value), new XElement("td",
item.Element("sobrenome").Value), new
XElement("td", item.Element("telefone residencial").Value),
new XElement("td ",
item.Element("notas").Value)))));
html.Save($"{Application.StartupPath}\
\employees.htm");

416
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

if (checkBox1.Checked) {

Process.Start($"{Application.StartupPath}\\employees.htm"); } outro {

MessageBox.Show($"Saída salva como {Application.StartupPath}\\employees.htm"); }

O código carrega o arquivo Employees.xml em uma instância da classe XElement. Em seguida, ele cria outro
XElement chamado html usando construção funcional. Observe como os dados XML são selecionados dos elementos
descendentes <employee> para os novos objetos XElement. Efetivamente, em apenas uma instrução, somos capazes de
transformar a árvore XML de origem em HTML.
O novo XElement formado após a transformação é salvo no disco como o arquivo Employees.htm. Se a caixa de
seleção estiver marcada, o código abre o arquivo Employees.htm no navegador; caso contrário, uma caixa de mensagem
de sucesso é exibida. A Figura 13-19 mostra o arquivo Employees.htm gerado como resultado de um exemplo de execução
do aplicativo.

Figura 13-19. Marcação HTML resultante

ÿ Nota Além de usar a construção funcional, você também pode criar XElements e XAttributes individuais e,
em seguida, criar o aninhamento necessário. No entanto, a construção funcional torna seu código organizado e fácil de
ler. Se seus dados XML forem gerados como resultado de algum processamento ou lógica complexa, talvez seja
necessário criar vários elementos e atributos como entidades independentes e, em seguida, associá-los uns aos outros.

Esse processo é muito semelhante à abordagem adotada pelo XML DOM.

417
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

Projetando um novo tipo


A projeção é o processo pelo qual os dados de origem são filtrados, têm sua forma alterada ou até mesmo seu
tipo é alterado. Por exemplo, suponha que você queira carregar dados de Employees.xml em uma nova coleção de
objetos Employee de forma que cada objeto Employee tenha os valores preenchidos dos elementos <firstname>,
<lastname>, <homephone> e <notes> de o XML de origem.
Para ilustrar como funciona a projeção, criaremos um aplicativo como o mostrado na
Figura 13-20.

Figura 13-20. Dados projetados como uma coleção e exibidos em uma grade

O aplicativo consiste em um botão intitulado “Project XML as a Collection” e um controle DataGridView.


Ao clicar no botão, os dados de Employees.xml são projetados como uma coleção de um tipo anônimo. A coleção é então
exibida no DataGridView.

ÿ Observação Um tipo anônimo é um tipo que não é explicitamente definido em seu código. O nome do tipo, bem como
suas propriedades, são inferidos e gerados pelo compilador.

O manipulador de eventos Click do botão Project XML as Collection executa o trabalho real de projetar o XML e é
mostrado na Listagem 13-28.

Listagem 13-28. Projetando XML em uma coleção

private void button1_Click(object sender, EventArgs e) { XElement root


= XElement.Load($"{Application.StartupPath}\\employees.xml"); var
empregados = do item na raiz.Descendentes("employee") selecione novo { EmployeeID =
item.Attribute("employeeid").Value, FirstName = item.Element("firstname").Value, LastName =
item.Element(" sobrenome").Value, HomePhone =
item.Element("homephone").Value, Notes =
item.Element("notes").Value };

dataGridView1.DataSource = empregados.ToArray(); }

418
Machine Translated by Google

Capítulo 13 ÿ Trabalhando com LINQ to XML

O código carrega o arquivo Employee.xml em uma instância da classe XElement. Em seguida, seleciona o necessário
dados como um tipo anônimo com cinco propriedades, ou seja, EmployeeID, FirstName, LastName, HomePhone e
Notes. Por fim, a coleção do tipo anônimo é convertida em uma matriz para vinculação de dados com o controle
DataGridView.

Resumo
LINQ to XML fornece uma maneira totalmente nova de processar dados XML. Este capítulo examinou muitos recursos do
LINQ to XML em detalhes. Você trabalhou com classes como XElement e XDocument que carregam árvores XML para
processamento e aprendeu a lidar com espaços em branco e namespaces em LINQ to XML. Operações comuns como
análise, navegação e modificação de dados XML também foram abordadas. Você também aprendeu a validar documentos
XML em esquemas XML. Transformar dados XML usando LINQ to XML tem suas próprias vantagens e você usou
construção funcional para transformar a forma do XML de origem. Finalmente, você projetou uma árvore XML em uma
coleção.

419
Machine Translated by Google

APÊNDICE A

Criando um XmlReader e um
XmlWriter personalizados

No Capítulo 3, você aprendeu sobre as classes XmlReader e XmlWriter. As classes abstratas XmlReader e XmlWriter podem ser
usadas de três maneiras:

•Para chamar o método Create() das respectivas classes que retornam uma instância das classes genéricas
XmlReader ou XmlWriter

•Usar as classes concretas XmlTextReader e XmlTextWriter fornecidas pelo .NET


Estrutura

• Para criar classes personalizadas que herdam das classes XmlReader e XmlWriter

Você já está familiarizado com as duas primeiras abordagens. Nas seções a seguir, você aprenderá
como criar leitores e gravadores personalizados a partir das classes base abstratas XmlReader e XmlWriter.

Criando um XmlReader personalizado


Nesta seção, você criará uma implementação personalizada da classe XmlReader. A classe SqlCommand fornece o método
ExecuteXmlReader() que retorna uma instância de XmlReader para o chamador. Isso funciona bem se o seu banco de dados
for SQL Server, mas e se o seu banco de dados for o Microsoft Office Access ou qualquer outro banco de dados compatível com
OLEDB? Além disso, as extensões XML, como a cláusula FOR XML, podem não estar disponíveis para todos os bancos de
dados. Isso significa que você não pode recuperar os dados e lê-los usando um XmlReader? Claro que não.
Não há solução pronta para uso para esse problema, mas você pode criar seu próprio mecanismo para superar essa
limitação, criando uma classe personalizada herdada da classe abstrata XmlReader. Você pode substituir as propriedades e
métodos necessários conforme sua necessidade. Os requisitos para a classe XmlReader personalizada são resumidos aqui:

• Ele deve aceitar a string de conexão do banco de dados e o nome da tabela para leitura.

•Os valores da coluna devem ser tratados como valores de atributo.

• Deve permitir a iteração na tabela para ler cada linha.

•Os valores da coluna devem ser acessíveis especificando um índice ou nome de coluna.

© Bipin Joshi 2017 421


B. Joshi, Beginning XML with C# 7, https://doi.org/10.1007/978-1-4842-3105-0
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Herdando de XmlReader
A classe XmlReader é uma classe abstrata e fornece várias propriedades e métodos que você precisa
substituir ao herdar dela. A Listagem A-1 mostra assinaturas dessas propriedades e métodos.

Listagem A-1. Propriedades e métodos da classe XmlReader

resumo público int AttributeCount; string


abstrata pública BaseURI {

pegar; }
public abstract void Close(); public
abstract int Profundidade {

pegar; }
public abstract bool EOF {

pegar; }
string abstrata pública GetAttribute(int i); string
abstrata pública GetAttribute(nome da string, namespaceURI da string); string
abstrata pública GetAttribute(nome da string); public abstract bool TemValor {

pegar; }
public abstract bool IsEmptyElement {

pegar; }
public abstract string LocalName {

pegar; }
string abstrata pública LookupNamespace(prefixo da string);
public abstract bool MoveToAttribute(string name, string ns); public
abstract bool MoveToAttribute(string name); public abstract bool
MoveToElement(); public abstract bool MoveToFirstAttribute(); public
abstract bool MoveToNextAttribute(); public abstract XmlNameTable
NameTable {

pegar; }
string abstrata pública NamespaceURI {

pegar; }

422
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

public abstract XmlNodeType NodeType {

pegar; }
string abstrata pública Prefixo {

pegar; }
public abstract bool Read(); public
abstract bool ReadAttributeValue(); resumo público
ReadState ReadState {

pegar; }
public abstract void ResolveEntity(); string
abstrata pública Valor {

pegar; }

Você pode substituir essas propriedades e métodos e escrever sua própria lógica de manipulação de dados. Se
você não deseja substituir uma propriedade ou método específico, ainda precisa ter sua implementação vazia.
Uma maneira melhor é lançar uma exceção em tais propriedades e métodos para que o chamador saiba que essas
propriedades e métodos não foram implementados por você. Não discutirei todas as propriedades aqui porque você já
está familiarizado com muitas delas (consulte o Capítulo 3 para obter mais informações).

Criando a classe TableReader Agora que você está

familiarizado com a classe abstrata XmlReader, vamos criar nossa própria implementação. Para fazer isso, crie um
novo projeto de biblioteca de classe de tipo usando o Visual Studio. Adicione uma classe chamada TableReader.
Certifique-se de que as referências aos assemblies System.Xml e System.Data sejam adicionadas ao projeto. Importe
os namespaces conforme mostrado na Listagem A-2 na parte superior da classe TableReader e certifique-se de que a
classe TableReader seja herdada da classe XmlReader.

Listagem A-2. Importando Namespaces e Configurando a Herança

usando System.Xml;
usando System.Data;
usando System.Data.OleDb;

classe TableReader:XmlReader
{
...

Você precisa adicionar uma implementação de cada propriedade e método mencionado. O Visual Studio fornece um
atalho para adicionar implementações vazias desses membros. Vá para a linha de declaração de classe e procure o ícone
Ações rápidas na margem esquerda. Abra a lista Quick Actions e escolha a opção Implement Abstract Class (consulte a
Figura A-1).

423
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Figura A-1. Adicionando implementações vazias de propriedades e métodos

Isso adicionará assinaturas fictícias de todas as propriedades e métodos que precisam ser substituídos.
Observe como a implementação fictícia lança uma exceção usando a palavra-chave throw. Dessa forma, se alguém
tentar usar membros não implementados, uma exceção será lançada indicando que “o método ou operação não está
implementado”. Codifique a classe TableReader conforme mostrado na Listagem A-3.

Listagem A-3. A classe TableReader

public class TableReader:XmlReader {

cnn privada OleDbConnection;


privado OleDbCommand cmd; leitor
OleDbDataReader privado; private int
intColumnIndex = -1; string privada
strValue;

public TableReader(string connectionString,string tableName) {

cnn = new OleDbConnection(connectionString); cmd =


new OleDbCommand(); cmd.Connection = cnn;
cmd.CommandText = tableName; cmd.CommandType
= CommandType.TableDirect; cnn.Open(); leitor =
cmd.ExecuteReader();

substituição pública int AttributeCount {

pegar

return leitor.FieldCount;
}
}

substituição pública void Close() {

leitor.Close();
cnn.Close();
}

424
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

public override int Depth {

pegar

retornar leitor.Profundidade;
}
}

string de substituição pública GetAttribute(int i) {

return leitor.GetValue(i).ToString();
}

string de substituição pública GetAttribute(nome da string) {

return leitor.GetValue(leitor.GetOrdinal(nome)).ToString();
}

substituição pública bool MoveToAttribute(nome da string) {

intColumnIndex = leitor.GetOrdinal(nome); retornar


verdadeiro;
}

substituição pública bool MoveToElement() {

intColumnIndex = -1;
retornar verdadeiro;
}

substituição pública bool MoveToFirstAttribute() {

intColumnIndex = 0;
retornar verdadeiro;
}

substituição pública bool MoveToNextAttribute() {

intColumnIndex++; if
(intColumnIndex > reader.FieldCount - 1) {

retorna falso;

}
outro {
retornar verdadeiro;
}
}

425
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

substituição pública bool Read() {

intColumnIndex = -1;
StrValue = ""; return
leitor.Read();
}

public override bool HasValue {

pegar

return leitor.IsDBNull(intColumnIndex);
}
}

substituição pública bool ReadAttributeValue() {

if (intColumnIndex < leitor.FieldCount) {

strValue = leitor.GetValue(intColumnIndex).ToString(); retornar


verdadeiro;

}
outro {
retorna falso;
}
}

string pública Nome {

pegar

if (intColumnIndex == -1) {

return cmd.CommandText;

}
outro {
return leitor.GetName(intColumnIndex);
}
}
}

string de substituição pública Valor


{
pegar

return strValor;
}
}
...
}

No texto a seguir, dissecamos o código passo a passo.


426
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Declarando variáveis de nível de classe

cnn privada OleDbConnection; privado


OleDbCommand cmd; leitor
OleDbDataReader privado; private int
intColumnIndex = -1; string privada strValue;

A classe TableReader declara variáveis privadas do tipo OleDbConnection, OleDbCommand e


OleDbDataReader no nível da classe:

•A classe OleDbConnection é usada para estabelecer uma conexão com qualquer banco de dados
compatível com OLEDB.

•A classe OleDbCommand é usada para executar qualquer consulta, consulta SQL ou procedimentos
armazenados em um banco de dados.

• A classe OleDbDataReader permite iterar por meio de um conjunto de resultados em um cursor


maneira orientada.

A variável inteira intColumnIndex controla o índice da coluna atual cujo valor deve ser lido.
Da mesma forma, a variável string strValue armazena o valor da coluna indicada por intColumnIndex.

Inicializando as Variáveis

public TableReader(string connectionString,string tableName) {

cnn = new OleDbConnection(connectionString); cmd = new


OleDbCommand(); cmd.Connection = cnn; cmd.CommandText
= tableName; cmd.CommandType = CommandType.TableDirect;
cnn.Open(); leitor = cmd.ExecuteReader();

O construtor da classe TableReader aceita dois parâmetros: a string de conexão com o banco de dados e o nome
da tabela cujos dados serão lidos. Usando a string de conexão, o OleDbConnection é instanciado. A propriedade Connection
da classe OleDbCommand é definida como a classe OleDbConnection que acabamos de instanciar. A propriedade CommandText
da classe OleDbCommand é definida como o nome da tabela cujos dados devem ser lidos.

Dê uma olhada na propriedade CommandType. É definido como TableDirect, que retorna todas as linhas da tabela
indicadas pela propriedade CommandText. Na verdade, funciona como se tivéssemos especificado SELECT * FROM
<tableName> como a consulta. A conexão com o banco de dados é então aberta. O método ExecuteReader() de
OleDbCommand é chamado e um OleDbDataReader é recuperado.

427
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Recuperando o Número Total de Atributos


substituição pública int AttributeCount {

get

{ return reader.FieldCount; }

A classe TableReader retornará valores de coluna como atributos nos dados XML resultantes. Portanto,
a propriedade somente leitura AttributeCount retorna o número total de colunas na tabela subjacente. O número
total de colunas na tabela é obtido usando a propriedade FieldCount da classe OleDbDataReader.

Fechando o Leitor
substituição pública void Close() {

leitor.Close();
cnn.Close();

} public override int Depth {

pegar

retornar leitor.Profundidade;
}
}

O método Close() fecha o OleDbDataReader, bem como o OleDbConnection. A propriedade Depth


retorna a profundidade do OleDbDataReader.

Atributos de Leitura
string de substituição pública GetAttribute(int i) {

return leitor.GetValue(i).ToString();
}

string de substituição pública GetAttribute(nome da string)


{
return leitor.GetValue(leitor.GetOrdinal(nome)).ToString();
}

Os valores da coluna podem ser recuperados usando duas sobrecargas do método GetAttribute(). A
primeira sobrecarga aceita o índice de atributo. No nosso caso, o índice do atributo é o mesmo que o índice
da coluna. O método GetValue() da classe OleDbDataReader aceita o índice da coluna e retorna o valor da
coluna como um objeto. O método ToString() retorna uma representação de string do objeto para o chamador.
A segunda sobrecarga aceita um nome de atributo. O método GetOrdinal() de OleDbDataReader aceita o
nome da coluna e retorna seu índice. O índice retornado é passado para o método GetValue() como antes.
428
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Navegando entre os atributos public


override bool MoveToAttribute(string name)
{
intColumnIndex = leitor.GetOrdinal(nome); retornar
verdadeiro;
}

substituição pública bool MoveToElement() {

intColumnIndex = -1;
retornar verdadeiro;
}

substituição pública bool MoveToFirstAttribute() {

intColumnIndex = 0;
retornar verdadeiro;
}

substituição pública bool MoveToNextAttribute() {

intColumnIndex++; if
(intColumnIndex > reader.FieldCount - 1) {

retorna falso;

}
outro {
retornar verdadeiro;
}
}

Os métodos MoveToAttribute(), MoveToFirstAttribute(), MoveToNextAtribute() e MoveToElement() permitem que


você navegue dentro dos atributos disponíveis:

•O método MoveToAttribute() aceita o nome da coluna (que é o


igual ao nome do atributo) e define a variável de índice da coluna como o índice dessa coluna.

•O método MoveToFirstAttribute() define o índice da coluna atual como 0, enquanto


MoveToNextAttribute() o incrementa para que o próximo valor da coluna possa ser lido.

•O método MoveToElement() simplesmente define o índice da coluna atual como -1, indicando que
nenhum valor de coluna pode ser lido. O método MoveToElement() destina-se a mover o leitor
para o nó do elemento a partir de qualquer um de seus atributos. Ao definir o índice da coluna
como -1, redefinimos o contador do índice da coluna e imitamos esse comportamento.

429
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Avançando o Leitor

substituição pública bool Read()


{
intColumnIndex = -1;
StrValue = ""; return
leitor.Read();
}

O método Read() permite iterar na tabela. Ele chama o método Read() da classe
OleDbDataReader e retorna um valor booleano indicando se a operação de leitura foi bem-sucedida.
À medida que o ponteiro do registro se move para um novo registro, o índice e o valor da coluna atual são redefinidos.

Verificando se o valor está vazio

public override bool HasValue {

pegar

return leitor.IsDBNull(intColumnIndex);
}
}

A propriedade HasValue indica se o TableReader contém algum valor. Se a coluna contiver um valor
NULL, HasValue deve retornar false. O método IsDbNull() da classe OleDbDataReader aceita um índice de
coluna e retorna true se a coluna contiver um valor NULL.

Valores de leitura

substituição pública bool ReadAttributeValue() {

if (intColumnIndex < leitor.FieldCount) {

strValue = leitor.GetValue(intColumnIndex).ToString(); retornar


verdadeiro;

}
outro {
retorna falso;
}
}

O método ReadAttributeValue() retorna o valor da coluna atual. Ele faz isso usando o método
GetValue() da classe OleDbDataReader como antes.

430
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Retornando o nome da tabela ou coluna

string pública Nome {

pegar

if (intColumnIndex == -1) {

return cmd.CommandText;

}
outro {
return leitor.GetName(intColumnIndex);
}
}
}

A propriedade Name retorna o nome da tabela subjacente ou o nome da coluna. Isso é útil para ver qual coluna
está sendo lida. O nome da tabela é obtido da propriedade CommandText da classe OleDbCommand, enquanto o nome
da coluna é obtido do método GetName() da classe OleDbDataReader.

Valores de retorno

string de substituição pública Valor {

pegar

return strValor;
}
}

Finalmente, a propriedade Value simplesmente retorna o valor armazenado na variável strValue. Note que strValue
é atribuído no método ReadAttributeValue().
As propriedades e métodos restantes não são implementados pela classe TableReader. Compile a
biblioteca de classes e você deve obter um assembly, TableReader.dll. Esse assembly pode ser usado em
aplicativos cliente para trabalhar com bancos de dados OLEDB e XML.

Usando a classe TableReader Para


consumir a classe TableReader, você precisa criar um aplicativo Windows Forms como o mostrado
na Figura A-2.

431
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Figura A-2. Aplicação que consome a classe TableReader

O aplicativo consiste em caixas de texto para inserir a string de conexão do banco de dados e o nome da
tabela, respectivamente. Depois de clicar no botão Ler, a classe TableReader é instanciada. Ele lê os dados da tabela
e os grava em um arquivo XML. O arquivo XML assim criado é exibido em um controle de navegador da Web. O
manipulador de eventos Click do botão Read contém o código mostrado na Listagem A-4.

Listagem A-4. Usando a classe TableReader

private void button1_Click(remetente do objeto, EventArgs e) {

TableReader tr = new TableReader(textBox1.Text, textBox2.Text); Escritor


XmlTextWriter =
new XmlTextWriter($"{Application.StartupPath}\\temp.xml", null);
escritor.WriteStartDocument();
escritor.WriteStartElement("raiz"); contagem
int = tr.AttributeCount; while (tr.Read()) {

escritor.WriteStartElement(tr.Name); for (int i


= 0; i < contagem; i++) {

tr.MoveToAttribute(i);
tr.ReadAttributeValue();
Writer.WriteAttributeString(tr.Name, tr.Value); }
escritor.WriteEndElement(); }

432
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

escritor.WriteEndElement();
tr.Close(); escritor.Close();

webBrowser1.Navigate($"{Application.StartupPath}\\temp.xml"); }

Antes de escrever o código anterior, adicione uma referência a TableReader.dll no aplicativo do Windows e
importe o namespace na parte superior. O código cria uma instância da classe TableReader passando a string de
conexão do banco de dados e o nome da tabela para seu construtor. Em seguida, é criado um XmlTextWriter que
grava dados em um arquivo XML temporário chamado temp.xml. A classe TableReader retornará apenas os dados
XML fragmentados; portanto, o elemento raiz é adicionado usando o método WriteStartElement() da classe
XmlTextWriter. O número total de colunas na tabela fornecida é recuperado usando a propriedade AttributeCount e
armazenado em uma variável para uso posterior.
Um loop while chama o método Read() da classe TableReader. A cada iteração, um elemento é
adicionado ao arquivo com o mesmo nome da tabela. Lembre-se de que a propriedade Name da classe TableReader
retorna o nome da tabela ou o nome da coluna, dependendo do índice da coluna atual. Como acabamos de chamar
o método Read(), o índice da coluna será -1 e, portanto, o nome da tabela será retornado.

Em seguida, um loop for itera todos os atributos - isto é, colunas. Com cada iteração do loop for, o valor do
atributo é lido usando o método ReadAttributeValue(). Um atributo é então gravado no arquivo junto com seu valor
usando o método WriteAttributeString() da classe XmlTextWriter. O método WriteEndElement() da classe
XmlTextWriter grava tags finais para o elemento aberto mais próximo. O TableReader e o XmlTextReader são
fechados usando seus respectivos métodos Close(). Por fim, o método Navigate() do controle do navegador da Web
mostra ao usuário o arquivo XML.

Criando um XmlWriter personalizado


Agora que você criou uma implementação personalizada de XmlReader, vamos avançar e ver como criar um
XmlWriter personalizado. Como exemplo, criaremos um gravador RSS que emite feeds RSS.
Really Simple Syndication (RSS) é uma maneira padrão de compartilhar o conteúdo do seu site com outras pessoas.
Nada mais é do que uma marcação XML padronizada que descreve o conteúdo que você deseja compartilhar. Como o RSS é um
formato amplamente aceito, seu conteúdo fica imediatamente pronto para ser consumido por outras pessoas. A Listagem A-5 ilustra
um documento RSS.

Listagem A-5. Exemplo de Marcação RSS

<rss versão="2.0">
<canal>
<title>Meu Web Site</title>
<link>http://localhost/mywebsite</link>
<description>Últimos Artigos de Meu Web Site</description>
<copyright>Copyright (C) Meu Web Site. Todos os direitos reservados.</copyright>
<generator>Meu gerador de RSS</generator> <item>

<title>Usando WebRequest e WebResponse</title>


<link>http://localhost/mywebsite/displayarticle.aspx?id=239</link> <description>A
descrição vai aqui</description> <pubDate>Domingo, 25 de janeiro 2004 00:00:00
GMT</pubDate> </item>

</canal>
</rss>

433
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Vejamos cada tag de marcação de perto:

•<rss> forma a marca raiz e possui um atributo de versão. O atributo version especifica a versão
da especificação RSS usada pela marcação, 2.0 neste caso.

•<canal> contém tags como nós <título>, <link> e <item>. Um canal representa informações
de metadados de uma fonte específica. Ele atua essencialmente como um contêiner para
o restante das tags. Um documento RSS pode conter um ou mais canais.

•<title> representa o título deste feed RSS.

•<link> representa o URL do site que fornece o feed RSS.

•<descrição> detalha mais informações sobre este feed.

•<copyright> especifica informações de direitos autorais.

•<generator> especifica o aplicativo que gerou este feed.

Além das tags anteriores, pode haver uma ou mais tags <item>, cada uma representando um item real que você
deseja compartilhar (por exemplo, um artigo ou uma entrada de blog). Cada tag <item> contém ainda os seguintes
subnós:

•<title> representa o título deste item (por exemplo, o título do artigo).

•<link> representa a URL deste item (por exemplo, a URL do artigo).

•<descrição> contém a descrição do item (por exemplo, um resumo do


artigo).

•<pubDate> contém a data de publicação do item. Um formato de data típico é Sun 28


Dez 2003 12:00:00 GMT.

ÿ Nota A marcação RSS mostrada aqui é a marcação básica. Pode ser necessário adicionar tags adicionais para
incorporar informações adicionais. Você pode obter mais informações sobre RSS em https://en.wikipedia.org/wiki/RSS.

O .NET Framework fornece o namespace System.ServiceModel.Syndication, que contém classes para ler e
gravar um feed RSS. No entanto, como exemplo, criaremos nossa própria classe personalizada chamada RssWriter.
A classe RssWriter herdará de XmlWriter e permitirá que você emita feeds RSS facilmente.
Para criar o RssWriter, você precisa criar um projeto de biblioteca de classes. Como antes, certifique-se de adicionar uma
referência ao assembly System.Xml.

Herdando de XmlWriter Para criar uma

implementação personalizada de XmlWriter, você precisa herdar dele e substituir as propriedades e métodos
mostrados na Listagem A-6.

Listagem A-6. Propriedades e métodos da classe XmlWriter

public abstract void Close(); public


abstract void Flush(); string abstrata
pública LookupPrefix(string ns); public abstract void
WriteBase64(byte[] buffer, int index, int count);

434
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

public abstract void WriteCData(string text); public


abstract void WriteCharEntity(char ch); public abstract
void WriteChars(char[] buffer, int index, int count); public abstract void
WriteComment(string texto); público abstrato vazio

WriteDocType(string name, string pubid, string sysid, string subset); public abstract
void WriteEndAttribute(); public abstract void WriteEndDocument(); public abstract void
WriteEndElement(); public abstract void WriteEntityRef(string name); public abstract void
WriteFullEndElement(); public abstract void WriteProcessingInstruction(nome da string,
texto da string); public abstract void WriteRaw(string data); public abstract void
WriteRaw(char[] buffer, int index, int count); público abstrato vazio

WriteStartAttribute(string prefix, string localName, string ns);


public abstract void WriteStartDocument(bool standalone); public
abstract void WriteStartDocument(); public abstract void
WriteStartElement(string prefix, string localName, string ns); public abstract WriteState WriteState {

pegar; }
public abstract void WriteString(string texto); public
abstract void WriteSurrogateCharEntity(char lowChar, char highChar); public abstract void
WriteWhitespace(string ws);

Muitas dessas propriedades e métodos devem ser familiares para você porque os discutimos no Capítulo 3.

Criando a classe RssWriter Para começar,


precisamos especificar que a classe RssWriter herda da classe base XmlWriter. Conforme mostrado na Figura
A-1, adicione definições fictícias das propriedades e métodos que implementam a classe base abstrata
XmlWriter. Em seguida, adicione algumas variáveis e um construtor à classe RssWriter, conforme mostrado na Listagem A-7.

Listagem A-7. O Construtor do RssWriter

public class RssWriter:XmlWriter {

gravador de XmlWriter privado;


fluxo privado objStream; public
RssWriter(Stream stream) {

objStream = fluxo;
escritor = XmlWriter.Create(objStream); }

O código declara variáveis de nível de classe dos tipos XmlWriter e Stream, respectivamente. O construtor
recebe um parâmetro do tipo Stream. Este fluxo atua como um fluxo de saída para emitir os feeds RSS.
Uma instância do XmlWriter é construída usando o método Create() da classe XmlWriter. O fluxo passado
para o construtor é fornecido ao método Create() para que a instância recém-criada de XmlWriter grave nesse
fluxo.

435
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Operações relacionadas ao fluxo de codificação

O fluxo precisa ser fechado e liberado para garantir que os dados emitidos sejam salvos corretamente. Os dois
métodos substituídos — Close() e Flush() — fazem exatamente isso. A Listagem A-8 mostra esses métodos.

Listagem A-8. Os métodos Close() e Flush()

substituição pública void Close()


{ objStream.Close(); escritor.Close();

} public override void Flush()


{ writer.Flush();

O método Close() chama o método Close() do fluxo subjacente, bem como o do XmlWriter. Da mesma
forma, o método Flush() chama o método Flush() do XmlWriter para que os dados sejam liberados para o fluxo.

Definindo enumerações para tags específicas de RSS

Seria bom fornecer prontamente nomes de tags e atributos RSS para que você não precise se lembrar deles. Isso é obtido
criando duas enumerações: RssElements e RssAttributes. As enumerações são mostradas na Listagem A-9.

Listagem A-9. Enumerações para representar tags e atributos RSS

public enum RssElements {

Rss,Canal,Título,Descrição,Link,Copyright,Gerador,Item,PubData }

public enum RssAttributes {

Versão
}

A enumeração RssElements contém valores para representar elementos RSS. Os Atributos RSS
enumeration contém apenas um valor — Version — que representa o atributo version do elemento <rss>.

Escrevendo elementos
Para emitir o feed RSS, você precisa escrever elementos como <rss> e <item> no fluxo de saída. Para
isso, criaremos três métodos: WriteElement(), WriteElementString() e WriteEndElement().
O código completo desses métodos é mostrado na Listagem A-10.

436
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Listagem A-10. Elementos de escrita

public void WriteStartElement(RssElements element) { string


elementName = ""; alternar (elemento) {

caso RssElements.Channel:
elementName = "canal"; quebrar;
case RssElements.Copyright:
elementName = "copyright"; quebrar;
case RssElements.Description:
elementName = "descrição";
quebrar; caso RssElements.Generator:

elementName = "gerador"; quebrar;


caso RssElements.Item:

nomeDoElemento = "item";
quebrar; caso
RssElements.Link:
elementName = "link";
quebrar; caso
RssElements.PubDate:
elementName = "pubDate";
quebrar; caso RssElementos.Rss:

nomeDoElemento = "rss";
quebrar; caso
RssElements.Title:
elementName = "título";
quebrar; }

escritor.WriteStartElement(elementName); }

public void WriteElementString(RssElements element, string value) { string elementName


= ""; alternar (elemento) {

caso RssElements.Channel:
elementName = "canal"; quebrar;
case RssElements.Copyright:
elementName = "copyright"; quebrar;
case RssElements.Description:
elementName = "descrição";
quebrar;

437
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

caso RssElements.Generator:
elementName = "gerador"; quebrar;
caso RssElements.Item:

nomeDoElemento = "item";
quebrar; caso
RssElements.Link:
elementName = "link";
quebrar; caso
RssElements.PubDate:
elementName = "pubDate";
quebrar; caso RssElementos.Rss:

nomeDoElemento = "rss";
quebrar; caso
RssElements.Title:
elementName = "título";
quebrar;

} escritor.WriteElementString(elementName, valor); }

substituição pública void WriteEndElement() {

escritor.WriteEndElement();
}

O método WriteStartElement() aceita um parâmetro do tipo RssElements que indica o elemento


nome a ser escrito. Ele contém uma instrução switch que verifica o nome do elemento fornecido em relação a vários valores
da enumeração RssElements. O nome do elemento é armazenado em uma variável de string. Por fim, o método
WriteStartElement() de XmlWriter é chamado fornecendo o nome do elemento armazenado na variável.

O método WriteElementString() aceita dois parâmetros: RssElements e o valor do elemento. Ele contém uma
instrução switch semelhante à do método anterior e armazena o nome do elemento em uma variável. O método
WriteElementString() da classe XmlWriter é chamado passando o nome do elemento e seu valor. Observe que
WriteStartElement() e WriteElementString() são métodos novos, ou seja, não são definidos pela classe base XmlWriter.

O método WriteEndElement() simplesmente chama o método WriteEndElement() do XmlWriter


instância para que a tag final do elemento mais próximo seja emitida.

Atributos de escrita
Assim como adicionamos métodos para escrever elementos, também precisamos adicionar métodos para emitir atributos.
Três métodos—WriteStartAttribute(), WriteAttributeString() e WriteEndAttribute()—farão esse trabalho.
A Listagem A-11 mostra esses métodos.

438
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Listagem A-11. Atributos de escrita

public void WriteStartAttribute(RssAttributes attb) { if (attb ==


RssAttributes.Version) { writer.WriteStartAttribute("version"); } }

public void WriteAttributeString(RssAttributes attb, string value) { if (attb ==


RssAttributes.Version) { writer.WriteAttributeString("versão",valor); } }

substituição pública void WriteEndAttribute()


{ writer.WriteEndAttribute(); }

O método WriteStartAttribute() aceita um parâmetro do tipo RssAttributes. Dentro dele verifica se o atributo
a ser emitido é Version, e se for, chama o método WriteStartAttribute() da instância XmlWriter para escrever o
atributo.
O método WriteAttributeString() aceita dois parâmetros: RssAttributes e o valor do
atributo. Em seguida, ele chama o método WriteAttributeString() da instância XmlWriter passando o valor e a
versão fornecidos como o nome do atributo.
O método WriteEndAttribute() simplesmente chama o método WriteEndAttribute() da instância XmlWriter.

Dados de gravação

Embora os métodos que criamos para escrever elementos cuidem da maior parte da geração do feed RSS, você pode precisar de
métodos adicionais para emitir comentários, dados de caracteres, espaços em branco e assim por diante. Para realizar essa tarefa,
escreveremos um conjunto de métodos, conforme mostrado na Listagem A-12.

Listagem A-12. Métodos para gravar dados

substituição pública void WriteCData(string text)


{ writer.WriteCData(text); }

substituição pública void WriteChars(char[] buffer, int index, int count)


{ writer.WriteChars(buffer, index, count); }

439
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

substituição pública void WriteComment(string texto) {

escritor.WriteComentário(texto); }

substituição pública void WriteWhitespace(string ws) {

escritor.WriteWhitespace(ws); }

substituição pública void WriteString(string texto) {

escritor.WriteString(texto); }

Esses métodos não contêm muito código. Eles simplesmente chamam o método correspondente na instância
XmlWriter. Por exemplo, o método WriteCData() aceita uma string e chama o método WriteCData() do XmlWriter passando
a string. Os métodos WriteChars(), WriteComment(), WriteWhitespace() e WriteString() também chamam os respectivos
métodos da ocorrência de XmlWriter.

Escrevendo uma declaração XML


Um feed RSS é um documento XML e, desse ponto de vista, deve conter uma declaração XML. Os métodos
WriteStartDocument() e WriteEndDocument() emitem uma declaração XML com versão 1.0.
Esses métodos são mostrados na Listagem A-13.

Listagem A-13. Escrevendo uma declaração XML

substituição pública void WriteStartDocument()


{ escritor.WriteStartDocument(); } public override void
WriteStartDocument(bool standalone)
{ writer.WriteStartDocument(standalone); } public override
void WriteEndDocument() { writer.WriteEndDocument(); }

O método WriteStartDocument() tem duas sobrecargas. Aquele com um parâmetro booleano emite um atributo
autônomo. Ambos os métodos chamam as respectivas sobrecargas do método WriteStartDocument() na instância de XmlWriter.
O método WriteEndDocument() simplesmente chama o método WriteEndDocument() da instância XmlWriter.

É isso: a classe RssWriter está pronta. Compile a biblioteca de classes para obter seu assembly de saída.

440
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Usando a classe RssWriter


Para consumir a classe RssWriter que acabamos de criar, você precisará criar um novo site no Visual Studio. Adicione uma
referência ao assembly no qual RssWriter reside. Abra o formulário da Web padrão no IDE e escreva o código mostrado na
Listagem A-14 em seu manipulador de eventos Page_Load.

Listagem A-14. Usando a classe RssWriter

protected void Page_Load(remetente do objeto, EventArgs e) {

Response.ContentEncoding = System.Text.Encoding.UTF8;
Response.ContentType = "texto/xml"; Gravador RssWriter = new
RssWriter(Response.OutputStream); escritor.WriteStartElement(RssElements.Rss);
Writer.WriteAttributeString(RssAttributes.Version, "2.0");
escritor.WriteStartElement(RssElements.Channel);
Writer.WriteElementString(RssElements.Title, "Meu Web Site");
Writer.WriteElementString(RssElements.Link, "http://localhost/mywebsite");
Writer.WriteElementString(RssElements.Description, "Últimos artigos do meu site");
Writer.WriteElementString(RssElements.Copyright, "Copyright (C) Meu Web Site. Todos os direitos reservados.");
Writer.WriteElementString(RssElements.Generator, "Meu XML RSS Generator");
escritor.WriteStartElement(RssElements.Item); escritor.WriteElementString(RssElements.Title, "

Crie e consuma RSS Feeds");


writer.WriteElementString(RssElements.Link, "http://localhost/mywebsite/Articles/ displayarticle.aspx?id=242");
writer.WriteElementString(RssElements.Description, "Este artigo explica como para criar e consumir feeds
RSS."); escritor.WriteElementString(RssElements.PubDate, "Seg, 04 de setembro de 2017 12:00:00 AM GMT");
escritor.WriteEndElement(); escritor.WriteEndElement(); escritor.WriteEndElement( ); Writer.Close(); Response.End();

O código define a propriedade ContentEncoding do objeto Response como UTF-8 (ou seja, ASCII). Ele também define
a propriedade ContentType para text/xml. Dessa forma, o navegador sabe que a resposta são dados XML em vez de
HTML. Uma nova instância da classe RssWriter é então criada. O OutputStream do objeto Response é passado como um
parâmetro para o construtor da classe RssWriter. Dessa forma, os dados XML serão gravados diretamente no fluxo de
resposta.
Em seguida, uma a uma, as tags RSS são emitidas para gerar um feed RSS, conforme mostrado na
Listagem A-5 anterior. Observe como a enumeração RssElements facilitou nosso trabalho. Vários métodos, como
WriteElementString() e WriteStartElement(), fazem uso extensivo da enumeração RssElements.
Após a gravação do feed, a instância RssWriter é fechada. Por fim, o método End() do objeto Response é chamado para
que o fluxo de resposta seja liberado para o cliente.

ÿ Observação Para simplificar, o código emite valores embutidos em código. Na maioria dos casos do mundo real, você
recuperará dados como título, URL e data de publicação de uma tabela de banco de dados.

441
Machine Translated by Google

Apêndice A ÿ Criando um XmlReader e um XmlWriter personalizados

Se você executar o formulário da web depois de escrever o código, ele deve ser semelhante à Figura A-3.

Figura A-3. Feed RSS exibido no navegador

Como você pode ver, o Firefox identificou a resposta como um feed RSS válido e exibido de uma maneira especial para facilitar a
leitura. Se você visualizar o Page Source da página, verá a marcação XML bruta, conforme mostrado na Figura A-4.

Figura A-4. XML bruto do feed RSS

Esta é a mesma marcação de feed RSS que você emitiu a partir do code-behind do formulário da web.

Resumo
Neste apêndice, você aprendeu a criar implementações personalizadas das classes XmlReader e XmlWriter.
As classes XmlReader e XmlWriter são classes abstratas. Para criar leitores e gravadores personalizados, você precisa herdar deles e
substituir várias propriedades e métodos. Dessa forma, você pode estender facilmente a funcionalidade pronta para uso exposta por essas
classes para cenários específicos.

442
Machine Translated by Google

APÊNDICE B

Recursos

Os recursos a seguir ajudarão você a aprender mais sobre XML, .NET Framework e tecnologias aliadas.

Site W3C para especificações XML


http://www.w3.org/XML

Site W3C para especificações de esquema XML


http://www.w3.org/XML/Schema

Site do W3C para informações relacionadas ao XPath


http://www.w3.org/TR/xpath

Site W3C para informações relacionadas a XSL


http://www.w3.org/Style/XSL

Site W3C para especificações SOAP


https://www.w3.org/TR/soap

Referência System.Xml
https://msdn.microsoft.com/library/system.xml.aspx

© Bipin Joshi 2017 443


B. Joshi, Beginning XML with C# 7, https://doi.org/10.1007/978-1-4842-3105-0
Machine Translated by Google

Apêndice B ÿ Recursos

.NET/ASP.NET/WCF/API da Web
https://www.microsoft.com/net
https://www.asp.net https://
docs.microsoft.com/en-us/dotnet/framework/wcf https://
docs.microsoft.com/en -us/aspnet/web-api

Wikipédia—Seção XML
http://en.wikipedia.org/wiki/XML

Web Site do Autor em .NET e Desenvolvimento Web


http://www.binaryintellect.net

Bloco de Notas XML—Editor de XML


https://github.com/microsoft/xmlnotepad

Sandcastle Help File Builder


https://github.com/EWSoftware/SHFB

Programação SQLXML
https://docs.microsoft.com/en-us/sql/relational-databases/sqlxml/sqlxml-4-0-programming concept

444
Machine Translated by Google

Índice

_Classe padrão, 348


A Default.aspx, 350
Classe de endereço, definição, 342
218 assembly
propriedade DocumentSource, 365
ADO.NET, 178 Employees.xml, execução 363–364 , 351
acesso a dados conectados, 175–176 MailMessage, 349 envio de e-mails, 349
Método ExecuteXmlReader(), 180–182 definição de controles de servidor, 342
Classe DataAdapter, SqlDataAdapter e criação de site, 343–344 atributos de
OleDbDataAdapter, 179 Inserção DataRow, mapa do site , 361 controle
205–206 Objeto DataSet, 180 acesso a dados SiteMapDataSource, 362 controle
desconectado (consulte Acesso a dados SiteMapPath, estrutura 361–362 ,
desconectado), 178 Provedor de dados OLEDB, 177 359 Visual Studio, 359 arquivo
Provedor de dados Oracle, 178 SqlCommand e Web.sitemap, 360 classe SmtpClient,
OleDbCommand, 179 SqlConnection e OleDbConnection, 349 erro de validação, 350 Web Form
178 SqlDataReader e OleDbDataReader, 179 design, 344–348 comentários XML
SqlParameter e OleDbParameter, 179 provedor de dados
SQL Server, 177 aplicativo DataSet digitado, 208
DataTable, 206, 208 declaração, 209 coluna EmployeeID,
207–208 tabela Employees, 207 método Fill(), 209
inserção, atualização e exclusão, 209–210 Solution
Explorer, 208 toolbox, 208 xsd.exe tool, 210 untyped
DataSet, 204 AppendChild() method, 49, 106–107
ASP.NET Click event handler, 348 configuration biblioteca de classes (consulte Biblioteca de classes)
system application configuration files, 365–366 SHFB, 383–385
machine arquivos de configuração, 365–366 arquivo Controle de fonte de dados XML
web.config (consulte o arquivo Web.config) Controle de menu, 357–358
Arquivo Navigation.xml, 351
transformações, 354–356
Controle TreeView, 352–353
TreeView DataBindings Editor, 352 Expressão
XPath, 356–357
Método Attributes(), 402
Site do autor, 444
Opção automática, 199

B
Pão ralado, 361
Aplicativos business-to-business (B2B), 9 método
button1_Click(), 340

© Bipin Joshi 2017 445


B. Joshi, Beginning XML with C# 7, https://doi.org/10.1007/978-1-4842-3105-0
Machine Translated by Google

ÿ ÍNDICE

C Classe DataContractSerializer, 212


desserialização, 227
Nós filhos, 35 Clique em manipulador de eventos, 228
biblioteca de classes Classe de funcionários, 228
Classe Calculator, 378–379 Método ReadObject(), 229–230
<list>, <item> e <listheader>, 381–382 parágrafos, Documento XML, 229
379–380 parâmetros, 380 valores de retorno, 380 serialização, 227
escopo e permissões, 380 < (consulte as tags > e Clique em manipulador de
<seealso>, 381 ) resumo e observações, 379 eventos, 228 personalização, 230–231
Classe de funcionários, 228
Método ReadObject(), 229–230
documento XML, 229
Documentação XML, 382–383 Controle DataGrid, 341–342
Arquivos de valores separados por vírgula (CSV), 2 Linguagem de Modificação de Dados (DML), 332
Acesso a dados conectados, 176 Classe DataReader, 179
Classes de conexão, 178 Classe DataTable, 184
Visualização do Modelo de Conteúdo, 118
Excluir () método da web, 252
Método CreateAttribute(), 49 Método DeleteSelf(), 108
Método CreateText(), 317 Método Descendants(), 399–400
Leitor avançado
Opção DiffGram, 199
XmlReader personalizado, Acesso a dados desconectado, 176–177
navegação de 430 atributos, 429 adicionando itens de menu, 201–202
variáveis de nível de classe, 427 Classe DataAdapter, acesso
Fechar () método, 428 184 , arquitetura 187–
Método MoveToAttribute(), 429 188 , 184–185
Método MoveToElement(), 429 Objetos de comando, 185
Método MoveToFirstAttribute(), 429 DataRow, 188
Classe OleDbDataReader, 430 DataRowState, 189–190
propriedades e métodos, 422–424 atributos Excluir () método, 189
de leitura, 428 valores de leitura, 430 Método Fill(), 186–187
recupera o número total de atributos, 428 Botão Salvar, 190–192
valores de retorno, 431 Método Select(), 188–189
Acesso à classe
Classe TableReader, 423–426, 431–433 DataSet, arquitetura 187–
XmlWriter personalizado
188 , 183 coleções, 183
Métodos Close() e Flush(), 436 enumerações
para, 436 propriedades e métodos, 434–435 DataRow, 188
DataRowState, 189–190
Exibição de feed RSS, 442 Tabela de dados, 184
Marcação de feed RSS, 442 Excluir () método, 189
Marcação RSS, 433–434 DiffGram formato, 195, 196
Classe RssWriter, 435, 441 Método Fill(), funcionalidade
RssWriter, construtor de, 435 186–187 , 186
Método WriteAttributeString(), 439 atributos Método GetXml(), 197
de gravação, 439 dados de gravação, 439– Método GetXmlSchema(), 197
440 elementos de gravação, 437–438 Método ReadXml(), 197–200
Botão Salvar, 190–192
Declaração XML, 440 Método Select(), 188–189
Método WriteXml(), 192, 193

D Método WriteXmlSchema(), 196–197


XML com informações de esquema, 195
classe DataAdapter, 179 XML sem esquema
Método DataBind(), 370 informações, 194, 196
Propriedade DataBindings, 357 Enumeração XmlWriteMode, 194, 196
Propriedade DataContext, 342

446
Machine Translated by Google

ÿ ÍNDICE

Método InferXmlSchema(), 203–204 análise de documentos, 8–9


Método ReadXmlSchema(), 203 DTDs e esquemas XML, 7
Tipos de acesso do Document Object gramática, regras de, 5–7 LINQ,
Model (DOM), 31 eventos de 9 documento simples, 1 aplicativo
manipulação, XmlDocument Class, 57–58 espaço de baseado na Web, 3 arquitetura
memória, 31 namespaces, 54–55 abertura, documento orientada a XML, 5 XPath, 10–11
XML existente, 33–34 acesso de leitura/gravação, 31 XSLT, 9–10 Extensível
documento XML de amostra, 32 elementos específicos Transformações de linguagem de
e nós GetElementById() método, 38–41 método folha de estilo (XSLT) aplicando
GetElementsByTagName(), 37–38 método modelos, 153–154 folhas de estilo compiladas, 171–173
SelectNodes(), 41–42 método SelectSingleNode(), 43 definição, 149 função incorporada, 169 habilitação
representação em árvore, documento XML, 30 de scripts, 169 objetos de extensão, 171 função
definição de espaço em branco, 50 carregamento GetBirthDate(), 170 tabela HTML, documento XML
de documento, 51 propriedade PreserveWhitespace, convertido, 151– 152 <xsl:escolha> e <xsl:quando>
50–53 XmlWhiteSpace vs. ramificação, 156–158 <xsl:if> ramificação, 154–156
saída após passagem, 166 parâmetros de passagem,
164 documento XML de amostra, 150 blocos de script,
folha de estilo 166–168 , 152, 162–163 Transforming
Employees.xml, 158–160 XslCompiledTransform class,
161–162 XsltArgumentList class, 165 Extensible Style
Sheet Language (XSL), 21 DTDs externos, 136 esquemas
Classes XmlSignificantWhiteSpace, 53 classe externos, 137
XmlDocument, 56 navegação de documento XML, 35–
36 documento XML, partes de, 29–30 modificação de
documentos XML adicionando novo conteúdo, 48–49
variável CurrentNodeIndex, 46 excluindo conteúdo
existente, 47–48 arquivo Employees.xml, 44
conteúdo existente, 46–47 controles de
preenchimento, 45 métodos auxiliares, 50 botões
de navegação, 45 classes System.Xml, 44 classes
XML DOM, 31 classe XmlNodeChangedEventArgs,
56

F
Método auxiliar FillControls(), 403, 406
Método FillControls(), 50, 403
Definição do tipo de documento (DTD), 112 Método FillEmployees(), 187, 190
analisadores baseados em DOM, 8 Primogênito, 35
Construção funcional, 394

ÿÿÿÿÿÿÿE

Elements() método, 397 G


E-mails, 218 Método GetBytes(), 62
A enumeração EmployeeType, 218 Método GetElementById(), 38, 41, 114
Analisadores baseados em eventos, 8 Método GetElementsByTagName(), 38
Método ExecuteStream(), 317 Método GetEmployees(), 390
Método ExecuteXmlReader(), 180–182 Método GetObjectData(), 234
Marcação de aplicativo extensível Método GetValue(), 235
Linguagem (XAML), 21, 335 Visualização de gráfico, 118

Benefícios da Extensible Markup Language


(XML), 2–3 arquitetura clássica, 4
definição, 1 H
Linguagem de Marcação de Hipertexto (HTML), 1

447
Machine Translated by Google

ÿ ÍNDICE

Projeção de transformação de
Eu, J, K árvore XML, cenários 418–
Opção InferSchema, 200 419 , forma 415 , 416–417
Método InferXmlSchema(), 203–204
DTD embutido, 136 ÚltimoFilho, 35
Esquema embutido, 137 Caminho de localização, 86
Inserir () método da web, 251

M
eu
Classe de gerente, 221, 223
Arquitetura Language Integrated Query Controle de menu, 357–358
(LINQ), 387–388 valores de atributo, Menu Editor de DataBindings, 357
400–402 seleção de dados, 393 método Método MoveToAttribute(), 69
Descendants(), 399–400 classe Método MoveToElement(), 69
Employee, 389–390 dados de filtragem, Método MoveToFirstAttribute(), 69
392–393 método GetEmployees(), 390 Método MoveToNextAttribute(), 69
dados de agrupamento, 390–391 Método MyHandler(), 415
manipulação de namespace, 394
namespaces, 412–413 operação de
classificação, 392 operadores de consulta N
padrão, 388 métodos estáticos, 394 Tabela de nomes,
criação de método Validate(), 413 65 .NET Core,
Employees.xsd, 414 mensagem de erro, 12 .NET Framework
415 arquivo XSD Schema, 414 construção Base Class Library, 12
visual, 394 Padrões W3C, 394 espaços definições, 11 vs. .NET
em branco, 394 enumerações Core, 12 Stack of, 11 XML
LoadOptions e SaveOptions, 409 ADO.NET, 17 ASP.NET
LoadOptions.None, 410 Web Forms, 17–18
LoadOptions.Preserve Whitespace, assemblies e
410 hierarquia de classe XML, 395 namespaces, 13–14 modelo clássico
modificação de dados XML de análise XML, 14 arquivos de configuração,
15–17 documentação, 19–21 modelo de
análise baseado em LINQ, 14 serialização,
18 SQL Server, 21 marcação XAML, 21
método NewEmployeesRow(), 210 método
NewRow(), 188 NextSibling, 35 métodos
Nodes(), 397 testes Node, 86 analisadores
não validadores, 9

detalhes do funcionário, 406


arquivo Employees.xml, 402
método auxiliar FillControls(), 403
carregamento, 403 novo elemento do
funcionário, 404 método Remove(), 405
método Save(), 406 método
SetElementValue(), 405 classe XElement,
404, 407– 408 Controle TreeView da classe ÿÿÿÿÿÿÿO

XmlDocument, 397–398 Classe XElement,


Vinculação e incorporação de objetos
395–397 fragmentos XML, 393 nós XML,
Banco de dados (OLEDB), 177
renomeação, 394 transformação XML, 394
Método OnDeserializing(), 236
Conectividade de banco de dados aberta (ODBC), 177
Método OpenRead(), 62
Função OPENXML, 313–315
Método auxiliar OutputResults(), 391

448
Machine Translated by Google

ÿ ÍNDICE

Método SelectNodes(), 41–43


P, Q Método SelectSingleNode(), 43, 47
Classe de parâmetro, 179 Instruções SELECT
ParentNode, 35 Modo AUTOMÁTICO, 306-307
Predicados, 87 Explica o modo, 309–312
AnteriorIrmão, 35 modo PATH, 309
Janela de consulta, 306
R Modo RAW, 307–308
cláusula ROOT, 313
Método ReadAttributeValue(), 430
Cláusula XMLSCHEMA, 308–309
Método ReadElementContentAsBase64(), 84 Serialização
Método ReadOuterXml(), 70
serialização profunda, 212
Opção ReadSchema, 200
serialização rasa, 212
Método ReadString(), 70
Método SetElementValue(), 405
Método ReadToDescendant(), 67
Método SignOut(), 374
Método ReadToFollowing(), 67
Protocolo Simples de Acesso a Objetos (SOAP), 3, 211
Método ReadToNextSibling(), 68
Controle SiteMapDataSource, 362
Método ReadXml(), 197–200
Controle SiteMapPath, 361
Método ReadXmlSchema(), 203
Método Skip(), 68
Syndication realmente simples (RSS), 433
Classe SmtpClient, 349
Sistema de gerenciamento de banco de
Classe SoapFormatter, 212, 231
dados relacional (RDBMS), 177 desserialização
RemoveChild () método, 48
Clique em manipulador de
Método ReplaceChild(), 47
eventos, personalização 232 ,
REpresentational State Transfer (REST) serialização 235–236
características, 289
Clique em manipulador de
HTTP, 290
eventos, 232 personalização, 234–235, 237
Classe de funcionários, 231
Alterações do WCF,
Método Split(), 219
290 aplicativo cliente, 294
objeto SqlCommand, 182
configuração, 293–294
Classe SqlDataAdapter, 192
Excluir () método, 293, 298
servidor SQL
Classe EmployeeManager, 291–293
Cláusula FOR XML (consulte instruções SELECT)
Método GetAsync(), 296
Função OPENXML, 313–315
Interface IEmployeeManager, 290
Classes
Método Insert(), 293, 297–298
SQLXML, 315
Carregar manipulador de eventos,
Objetos DataSet, 319–320
295 propriedades, 291
DiffGram, 326–328
Método SelectByID(), 296
Consultas SELECT, 316–319
Objeto StringContent, 298
Classe SqlXmlAdapter, 320–322
Método Update(), 298
Modelos XML, 325–326
marcação XML, 296
Modelos XSLT, 322–324
Aplicativo
tipo de dados XML
cliente da API da Web, criação
Função CONVERTER, 330
302–303 , 299
DML, 332
Operações CRUD, 300
método exist(), 331
DataContractSerializer, 304
inserção e atualização, 329 método
SelectAll() e Post(), 301–302 retornando
query(), 331 criação de tabela, 329
o nome da tabela/coluna, 431
Segurança baseada em função, 371
Transact-SQL, 329
Propriedade RowState, 190
método value(), 331
XQuery, 333
S Programação SQLXML, 444
Padrões, 240
Sandcastle help file builder (SHFB), 383–385, 444
Iniciar Visualização, 118
Método da web SelectByID(), 251

449
Machine Translated by Google

ÿ ÍNDICE

comportamento de herança, 367


T recuperando strings de conexão de banco de dados, 370
Propriedade TransformFile, 354–356 recuperando valores, 368 estado de sessão, 374–375
Analisadores baseados em árvore, 8 armazenando strings de conexão de banco de dados, 369–
Controle TreeView, 352–353, 398 370 armazenando valores, 368 estrutura, 367
Editor de DataBindings TreeView, 356

serviços web
EM ASMX, 242
Cláusula UNION ALL, 311 métodos de chamada da web
Método UpdateLabel(), 50 Excluir () método da web, 259 lista
Método web Update(), 252 suspensa, 257
Inserir () método da web, 258
Método SelectAll(), 258
EM
Método da web SelectByID(), 258
Validando analisadores, 9 Método web Update(), 259
Documentos válidos, 7 componente, 239
Estúdio visual
Funcionalidade CRUD
criações de bibliotecas de classes, 26–28 abordagem de código primeiro,
Designer, 337 249 informações de string de conexão
Classe MessageBox, 25 de banco de dados, 249
execuções de amostra, 25 Excluir () método da web, 252
Visual Studio 2017, 21 Classe de Entidade Empregada, 250
Aplicativo do Windows, Visual Studio IDE, 23 Mesa de empregados, 250
Formulário do Windows, controle de botão, 23 Entity Framework, 247–248
Aplicativo de formulários do Windows, 22 Inserir () método da web, 251
Classe de contexto Northwind, 250
Método web SelectAll(), 251
EM Método da web SelectByID(), 251, 253
Site W3C, 443 páginas Método web Update(), 252
de erro personalizadas Propriedade Description, 247
do arquivo Web.config, Arquivo EmployeeManager.asmx.cs, 242 Modelo
páginas administrativas, 376 de projeto vazio, 241 Método HelloWorld(), 246
manipulador de eventos Click, 376 página de ajuda, 243 Botão invocar, 245
Default.aspx, 376 HttpException, Propriedade Namespace, 243 proxy, 240, 254–
377 atributo de modo, 377 256 cabeçalhos SOAP, 260–263 solicitação, 245,
especificação, 377 atributo 259 resposta, 246 serialização XML, 264–265
statusCode, 377 atributo statusCode, parâmetro de string, 247 no Visual Studio, 241
378 organização de formulários da diretiva @WebService, 242 WSDL, 244 ligação,
web, 376 funcionário listagem, 370– 267 elementos, 266 serviço web
371 Autenticação de formulário EmployeeManager, 265–266 tipos de porta,
267 modelo de solicitação e resposta , 266
definições de tipo, 266 organização WS-I,
Authenticate event handler, 372–373 243
<authentication>, 372 Click event handler, 373
Default.aspx e Login.aspx, 372, 373 defaultUrl
attribute, 372 login page, 374 loginUrl attribute, 372
Page_Load event handler, 373 role-based security,
371 Método SignOut(), 374 atributos de usuários,
372

450
Machine Translated by Google

ÿ ÍNDICE

Descrição dos Serviços Web Método WriteStartAttribute(), 439


Language (WSDL), 240, 270 Método WriteStartDocument(), 74
Web Services Interoperability (WSI), 243 Método WriteStartElement(), 74–75, 438
Wikipedia, 444 Windows Método WriteXml(), 192, 196
Communication Foundation (WCF), 227 Método WriteXmlSchema(), 196–197
ADO.NET Entity Data Model, 274 abordagem code-first,
274 DataContractSerializer, 288 definição, 269 classe
EmployeeDataContract , 273 Classe EmployeeService, X, Y, Z classe
275–276 EmployeeWCFService, 271 arquivo XAttribute, 398 classe
App.config de hospedagem, 277 comportamento, XElement, 398 objeto
277 detalhes do funcionário, 281 Lista de funcionários, XmlDataProvider, 341 esquema
280 EmployeeWCFServiceClient, 279 XML reduzido de dados (XDR), 112
EmployeeWCFServiceHost, 276 Comunicação XmlDocument, esquema anexado de
baseada em HTTP, 278 IIS, 285–287 Método Main() . validação de documentos 29 XML, 146
________________________ DTDs, 112 nó explicitamente, 143
Tipos de suporte de aplicativos Presentation função OnValidationError, 144 SOM
Foundation (WPF), 339 método button1_Click(), Add( ) método, 134 tipo complexo, 128,
340 atributo Class, 339 manipulador de eventos 132–133 classes SOM principais, 126
Click, 339, 340 elementos, 339 namespaces, esquema de criação, 129–130 DTD e
339 caixa de ferramentas, 337–338 Visual esquemas XML, 136–142 nome,
Studio, 335 arquivo Employees.xml, 336 M tipo simples para, 128, 131 notas,
ainWindow.xaml file, 336–337 marcação XAML, tipo simples para, 128, 132 objeto
335 XAML responsável pela marcação, 338– hierarquia, 127 números de
339 dados XML, 340–342 Wireless Application telefone, tipo simples para, 128, 131–
Protocol (WAP), 3 Wireless Markup Language 132 elemento raiz, 134 tipo complexo
(WML), 3 World Wide Web Consortium (W3C), de nível superior, 133 aplicativo do
1 Método WriteAttributeString(), 75, 439 Método Windows, 128 método Write(), 135
WriteBase64(), 82 classe XmlQualifiedName , 131
classe XmlSchemaAnnotated, 127
classe XmlSchema, 127 classes
XmlSchemaComplexType , 127
classe XmlSchemaParticle, 127
classe XmlSchemaSet, 134 classe
XmlSchemaSimpleType, 127, 131
criação de estrutura Exibição de
modelo de conteúdo, 118, 121 DTD,
Employees.xml, 113 classe Employee, 125
arquivo Employees.xml, 112 EmployeeType,
119 Graph View, 118, 121 NameSimpleType,
120 Start View, 118, 122 Visual Studio ID
E, 122–123 Editor XML, 118 Ferramenta de
definição de esquema XML, 123 Explorador
de esquema XML, 118, 120 tipos de dados
XSD, 115

451
Machine Translated by Google

ÿ ÍNDICE

Validação de documentos XML (cont.) Método ReadSubTree(), 66–67


Ferramenta xsd.exe, 124 XSD Método ReadToDescendant(), 67
Schema, Employees.xml, 118–119 XSD Schemas, Método ReadToFollowing(), 67
115 XDR Schema, 112 classe XmlDocument , Método ReadToNextSibling(), 68
142–143 XPathNavigator, 145 Editor XML, 118 Método Skip(), 68
XmlNode, 31 classe XmlNodeReader, 60 Esquema Classe XmlNodeReader, 60
XML, 111 esquema de definição de esquema XML Classe XmlTextReader, 60
(XSD), 112 Explorador de esquema XML, 118 classe Classe XmlValidatingReader, 60
XmlSerializer, 212 desserialização manipulador de eventos Definição de
Click, 214 tipos complexos, 220–221 atributo EmpCode, XmlTextWriter,
217 classe Employee, 213 eventos, 216–217 classe 59 classes de
Manager , 223–224 modificação, 217 serialização codificação, 75 colunas de
exportação, 73–75 dados de
exportação, 72–73 formatação de
documento XML, suporte de namespace
76–78 , 78–80 dados não textuais
Formato Base64, 83
leitura de dados Base64, 83–84
gravação de dados Base64,
formato de saída 82 , 76
Classe SQLConnection, 73
Aplicativo Windows Forms, 71
Método WriteStartDocument(), 74
Método WriteStartElement(), 74
aplicação, 212–213 nomes Classe XmlValidatingReader, 60
de elemento de array, 227 array XmlWhiteSpace, 53
variável, 227 tipos complexos, classe abstrata XNode, 395
218–221 customização, 224– Propriedade XPath, 356
226 nomes de elemento, 226 Eixos do modelo de
dados XPath, 86
Classe de funcionários, 213 funções retornam
Objeto empregado, 215 valores booleanos, 88 conjuntos
identificadores de enumeração, 227 de nós, 88 funcionam em
herança, 221–223 objetos nulos, números, 88 trabalham em
227 membros públicos, 226 strings, 88 XML interno e
externo, 97
formato XML, 213 Propriedades InnerXml e OuterXml, caminho de
elemento raiz XML, 226 localização 97–99 , 86 testes de nó, 86 predicados, 87
XmlSignificantWhitespace, 53
Definição de
XmlTextReader, Método SelectAncestors(), 96
59 árvore de carregamento, Método SelectChildren(), 96
63–64 carregamento de documento XML, 62 Método Select(), 92–94
Método MoveToAttribute(), 69 Método SelectSingleNode(), 94–95
Método MoveToFirstAttribute(), 69 Expressão XPath, 96
Método MoveToNextAttribute(), 69 XPathNavigator
movimentação entre atributos, 69 anexando novos nós, 106–107
namespaces, 66 tabela de nomes, 65 Método CreateNavigator(), 91 criação,
atributos de leitura, elementos e texto, 63 89–90, 105 declarando XmlDocument,
105 excluindo nós, 108–109
Método ReadElementString(), 65 leitura
de documentos XML, 61 Função auxiliar DisplayDetails(),
Método ReadOuterXml(), 70 101–102
Método ReadString(), 70 Método GetAttribute(), 92

452
Machine Translated by Google

ÿ ÍNDICE

Propriedade HasChildren, 92 TreeView, 90


nós modificadores, 107–108 Aplicativo Windows Forms, 90
documento XML modificador, 104–105 Método WriteSubtree(), 102–104
Método MoveToParent(), 92 XmlReader, 100
Método MoveToRoot(), 91 XmlWriter, 102
Método ReadSubtree(), 100–101 arquivo Expressões XPath, 94, 356–357
resultante, 103 documento XML resultante, Propriedade XPath, 356
104 detalhes de recuperação, 105–106 Expressões XQuery, 333
Tipos de dados XSD, 115
Salvar () método, 109 Classe XslCompiledTransform, 363

453

Você também pode gostar