msdn01_capa.JPG

Clique aqui para ler todos os artigos desta edição

 

Desenvolvendo Aplicativos com .NET Compact Framework, SQL Server CE e Replication

por John Papa

O handheld e outros dispositivos portáteis são comercializados com um conjunto de recursos cada vez maior. Esses dispositivos agora podem se comunicar em redes privadas e pela Internet através de modems Wi-Fi e sem fio associados a telefones celulares. Os dispositivos portáteis não são conhecidos apenas pelo uso pessoal. Muitas empresas estão se beneficiando da mobilidade que eles oferecem, e a crescente demanda por uma melhor portabilidade significa um aumento da demanda de aplicativos móveis. Por exemplo, um aplicativo que permita aos funcionários atualizar estoques, registrar pedidos, atualizar o perfil do cliente e executar outras tarefas baseadas em dados centralizados no campo. Nesta coluna, desenvolverei um aplicativo móvel que pode ser usado para examinar e atualizar o estoque de uma empresa. O aplicativo, destinado a dispositivos portáteis baseados em Windows® CE ou Microsoft® Windows Powered Pocket PC, pode funcionar como um aplicativo independente ou como parte de um aplicativo de abrangência empresarial. Usarei o Microsoft .NET Compact Framework existente no Visual Studio® .NET 2003 para desenvolver o aplicativo para computadores de bolso. Um dos ingredientes fundamentais deste aplicativo consiste na possibilidade de sincronizar e armazenar o estoque inteiro de produtos no dispositivo portátil, dentro de uma instância do SQL Server™ CE. O banco de dados do SQL Server CE do Pocket PC conterá uma versão reproduzida do banco de dados Northwind, a partir do banco de dados de back-end do SQL Server. Considerando que o banco de dados existe em dois lugares, usarei o recurso de reprodução unificada do SQL Server para manter a integridade dos dados de ambos os bancos de dados. Para começar, explicarei como definir os pré-requisitos para o cenário da reprodução unificada e descreverei as diversas maneiras de personalizá-la. Após discutir sobre a configuração do ambiente, mostrarei o funcionamento do aplicativo e como ele tira proveito tanto do SQL Server CE como da natureza desconectada do ADO.NET. Como em qualquer aplicativo, o tratamento de exceções é essencial. Porém, quando se trata de desenvolvimento para dispositivos móveis, a necessidade de um sólido tratamento de exceções aumenta significativamente. Por fim, concluirei explicando como detectar os diferentes erros que podem ocorrer entre o handheld e o banco de dados de back-end e como informá-los.  

Configuração

Antes de seguir adiante, é importante conhecer os pré-requisitos para o desenvolvimento de um aplicativo que utilize o .NET Compact Framework e SQL Server CE em um dispositivo portátil. Agora, o .NET Compact Framework vem instalado com o Visual Studio .NET 2003. Os componentes de dispositivo do SQL Server CE são instalados com o Visual Studio .NET 2003. Os componentes de servidor do SQL Server podem ser baixados diretamente do site da Web da Microsoft junto com todos os pacotes de serviço (os quais recomendo instalar). Uma vez instalados o .NET Compact Framework, o SQL Server CE e a versão mais recente dos pacotes de serviço do SQL Server e do SQL Server CE, você já tem meio caminho andado. Embora a instalação seja fácil, a configuração da reprodução unificada exige bastante atenção. Ao desenhar o aplicativo, é importante recuar e examinar como as diversas partes móveis interagem. Por exemplo, um passo simples, porém freqüentemente esquecido, é certificar-se de que os serviços do SQL Server e do SQL Server Agent estejam rodando no servidor de back-end. No aplicativo de exemplo de estoque, o banco de dados Northwind, em uma instância SQL Server de back-end, é o armazenamento central de dados que contém todas as informações, incluindo produtos, pedidos e clientes. O aplicativo permite que os dispositivos portáteis armazenem as informações de estoque do banco de dados Northwind no servidor de banco de dados de back-end. Os dois bancos de dados se comunicam através do processo de reprodução unificada, utilizando o SQL Server CE Server Agent para passar as informações de um lado para o outro.

 

SQL Server CE Server Agent
O SQL Server CE Server Agent consiste em uma DLL ISAPI instalada em um diretório virtual do Microsoft Internet Information Services (IIS). Ele permite que os dois bancos de dados se comuniquem entre si usando o protocolo HTTP para retransmitir as mensagens. Para configurar o SQL Server CE Server Agent, é mais fácil usar o assistente SQL Server CE Virtual Directory Creation Wizard. Um método mais avançado é configurá-lo manualmente. Criei uma pasta em meu servidor chamada “C:\Projects\SQLCE” e copiei o agente DLL (sscesa20.dll) da pasta de instalação do SQL Server CE em \Program Files\Microsoft SQL Server CE 2.0\Server e o registrei lá. Em seguida, criei um diretório virtual no IIS chamado SQLCE com os direitos de execução HTTP e o apontei para a nova localização da pasta de conteúdo do SQL Server CE Server Agent.  Ao se preparar para a reprodução unificada via SQL Server CE Server Agent, é imprescindível criar as permissões apropriadas e o diretório virtual na pasta NTFS. As pastas NTFS de permissões e o diretório virtual devem permitir que o aplicativo portátil acesse o agente. Você pode usar uma outra autenticação diferente do modo anônimo, mas devido à simplicidade de meu exemplo permiti o acesso anônimo ao diretório virtual através da conta IUSR_machine name. Como os arquivos serão gravados durante a sincronização da reprodução unificada, concedi acesso à gravação nas contas IUSR_machine name e na pasta. Mais uma vez, o acesso anônimo é ótimo para aplicativos de exemplo, mas você precisará de um método de autenticação mais seguro em aplicativos reais.

Publicação da Reprodução
  

Uma vez estabelecidos os meios para que as instâncias SQL Server e SQL Server CE se comuniquem entre si, você precisa de algo para elas conversarem. Inicialmente, cria-se uma publicação no back-end do SQL Server para definir os parâmetros do processo de reprodução unificada. A publicação define os dados que estarão disponíveis e como solucionar qualquer possibilidade de conflito de reprodução. A lista de acesso à publicação define quem possui permissão para se inscrever nela. O tipo de autenticação utilizado para se inscrever é definido pela configuração do SQL Server. Ao iniciar o assistente Create Publication Wizard, cria-se uma publicação que pode ser acessada clicando-se com o botão direito do mouse em um banco de dados e selecionando-se New | Publication no menu pop-up. Como a maior parte do assistente é intuitiva, vou me concentrar nas configurações mais críticas. 
Existem três tipos de publicações que podem ser criadas: instantânea (snapshot), transacional (transactional) e unificada (merge) (observe a Figura 1). Como o aplicativo de estoque precisa permitir que tanto o banco de dados de back-end quanto o banco de dados do handheld atualizem os dados e sincronizem entre si, uma publicação unificada dá conta do recado. Além disso, a reprodução unificada é a única forma de reprodução que o SQL Server CE suporta.

 

image001.gif

Figura 1 Três tipos de publicação

 

O próximo passo do assistente é indicar que tipo de assinante está apto a se inscrever para publicação. Certifique-se de selecionar “Devices running SQL Server CE”. Agora, você precisa indicar quais artigos serão publicados. Um artigo pode ser uma tabela, tela ou até mesmo uma stored procedure. No caso do aplicativo de exemplo, escolhi publicar todas as informações (para simplificar); no entanto, seria adequado um subconjunto de tabelas, já que as únicas informações que pretendo utilizar nos dispositivos portáteis estão relacionadas ao estoque.
  O aspecto negativo de se configurar uma publicação para usar uma reprodução unificada é que todos os artigos selecionados para publicação que ainda não possuírem uma restrição de chave própria ou primária para a propriedade ROWGUIDCOL terão uma coluna adicional chamada rowguid. Junto com esta coluna, será adicionado um índice definido como tipo de uniqueidentifier (tão logo o instantâneo da publicação seja tirado). O valor de uniqueidentifier é gerado automaticamente para as novas linhas do artigo. Esse valor é utilizado para vincular as linhas dos artigos do banco de dados de back-end às do SQL Server CE.
  As publicações também podem filtrar o artigo verticalmente (colunas restritivas do artigo) ou horizontalmente (linhas restritivas do artigo). Esses procedimentos podem ser definidos tanto no assistente Create Publication Wizard como nas propriedades da publicação. Para o aplicativo de exemplo, não criei nenhum filtro e determinei que todas as tabelas se tornariam artigos publicados. Uma vez finalizado o assistente, ele gerará a publicação.
  Quando a publicação é gerada, o SQL Server também cria vários outros objetos por trás dos bastidores a fim de oferecer suporte ao processo de reprodução. Por exemplo, são criados acionadores em todos os artigos publicados, os quais irão rastrear as modificações feitas nos artigos do servidor. Essas modificações são enviadas por meio de telas às tabelas MSmerge_contents e MSmerge_tombstone, que também são criadas pela publicação. As inserções e atualizações são rastreadas na tabela MSmerge_contents, enquanto as exclusões são rastreadas na tabela MSmerge_tombstone. Essas telas, acionadores e tabelas são criados para oferecer suporte ao processo de reprodução definido pela publicação. O SQL Server CE utiliza um processo semelhante.
  Depois de criar a minha publicação, a qual nomei NorthwindPub, defini as contas do SQL Server que poderiam acessar a publicação através da Lista de Acesso à Publicação (Publication Access List). Para o meu exemplo, criei um login de SQL Server chamado SqlTestUser e concedi privilégios ao banco de dados Northwind. Em seguida, cliquei na guia Publication Access List (Lista de Acesso à Publicação) na caixa de propriedades de publicação do meu NorthwindPub. Lá, adicionei o SqlTestUser à lista de contas válidas que podem acessar a publicação (observe a Figura 2).

 

image002.gif

Figura 2 Propriedades de publicação Northwind

 

A etapa final é criar o primeiro instantâneo. Antes que um novo assinante possa aceitar as modificações, elas precisam conter todas as tabelas e dados contidos nos artigos definidos da publicação. Isso significa que, ao criar pela primeira vez um instantâneo e, em seguida, reproduzi-lo ao assinante, todas as tabelas do banco de dados serão criadas e preenchidas no banco de dados do assinante (neste caso, o banco de dados do SQL Server CE). Nas sincronizações subseqüentes, as modificações serão enviadas apenas entre os bancos de dados do editor e do assinante.
  Uma vez configurado o SQL Server CE Server Agent, gerada a publicação, definidos todos os tipos de autenticação e estabelecido o primeiro instantâneo, pode-se iniciar o processo de reprodução. Agora, tudo o que eu preciso é de um assinante. Entre no aplicativo de exemplo de estoque.


O aplicativo
O aplicativo Inventory é um aplicativo Smart Device escrito em C#. Como eu possuo um Pocket PC, direcionei o aplicativo para este dispositivo, mas ele poderia ter sido adaptado para uma opção de dispositivo mais genérica baseada em Windows CE. Os requisitos básicos do aplicativo incluem a sincronização de dados de estoque do banco de dados de back-end Northwind com o dispositivo portátil. Quando um produto é selecionado, seu preço e os níveis de estoque são exibidos em caixas de texto, para que o usuário possa editá-los.
  Uma das grandes vantagens de desenvolver com o .NET Compact Framework é o fato de que você não precisa de um dispositivo baseado em Windows CE no qual se basear. Em vez disso, você pode desenvolver e depurar usando o emulador do Pocket PC. As figuras mostradas nesta coluna representam um aplicativo executado em modo de depuração no emulador.

image003.gif

Figura 3 Resultado da sincronização

Ao iniciar o aplicativo, ele se encarrega de ver se o banco de dados Northwind existe na instância do SQL Server CE. Na primeira vez que o aplicativo é executado, os dados ainda não foram reproduzidos totalmente para o SQL Server CE e, por esse motivo, as caixas de entrada estão vazias. Quando o usuário pressiona a tecla Synch, o aplicativo inicia o processo de reprodução unificada e traz o primeiro instantâneo da publicação do NorthwindPub. Como o primeiro instantâneo contém todos os dados do Northwind, ele levará mais tempo para sincronizar do que as sincronizações subseqüentes. Depois de enviada a publicação ao assinante, é exibida uma caixa de mensagem com o resultado da sincronização (observe a Figura 3). É exibida a quantidade de linhas que foram modificadas pelo editor no primeiro instantâneo, juntamente com os conflitos do editor e com as modificações do assinante. Essa sincronização inicial também cria o arquivo de banco de dados do SQL Server CE para armazenar os dados Northwind no dispositivo baseado em Windows CE.

image004.gif
Figura 4 Dados exibidos

  
Uma vez criado o banco de dados, o aplicativo exibe a lista de categorias em um controle combobox. A Figura 4 mostra como o produto Chai é selecionado na categoria Beverages (Refrigerantes) e exibe seus níveis de estoque e o preço unitário. A esta altura, o usuário pode navegar e/ou editar os produtos nas diversas categorias. À medida que o usuário efetua as modificações nos níveis de estoque, os dados são salvos no DataSet local. Basicamente, o DataSet é carregado a partir do banco de dados do SQL Server CE local, após a sincronização. Em seguida, o DataSet é vinculado aos controles do produto e rastreia todas as modificações feitas nos dados pelo usuário.

image005.gif
Figura 5 Modificando o valor do estoque

Quando o usuário pressiona a tecla Save, todas as modificações são salvas no banco de dados do SQL Server CE local, usando o ADO.NET. As modificações armazenadas no DataSet são enviadas ao banco de dados através do método Update de um SqlCeDataAdapter. Em seguida, se o usuário pressionar a tecla Synch, as modificações feitas no banco de dados do SQL Server CE local (o assinante) serão enviadas ao banco de dados de back-end (o editor) e, caso tenha sido feita alguma modificação no editor, ela será enviada ao assinante através da reprodução unificada. A Figura 5 mostra o resultado depois que o usuário modifica um único valor de estoque e que nenhuma modificação foi feita nos dados do banco de dados de back-end.

 

O código
O código responsável por esses procedimentos requer a inclusão de vários namespaces no projeto. Os códigos básicos exigidos por um aplicativo para dispositivos inteligentes (smart devices) são System, System.Drawing, System.Collections, System.ComponentModel e System.Windows.Forms.
Os namespaces adicionais que incluí são utilizados para gravar um arquivo de banco de dados no dispositivo portátil, para estabelecer a comunicação com um banco de dados do SQL Server CE e para armazenar os dados do produto em um DataSet. Desse modo, os namespaces a seguir também são obrigatórios neste projeto:

using System.Data.SqlServerCe;
using System.Data;
using System.Data.Common;
using System.Text;
using System.Xml;

Quando o aplicativo é iniciado pela primeira vez, a conexão com o banco de dados é definida por meio de um objeto SqlCeConnection. Esse objeto funciona exatamente como o objeto SqlConnection, a não ser pelo fato de que ele é usado para se comunicar com bancos de dados do SQL Server CE. Tanto sua fonte de dados como string de conexão são muito mais simples do que a string de conexão do objeto SqlConnection, já que ele precisa apenas apontar para o arquivo do banco de dados. Um SqlCeDataAdapter também é estabelecido, com suas propriedades SelectCommand e UpdateCommand definidas com suas instruções correspondentes SELECT e UPDATE SQL. As propriedades DeleteCommand e InsertCommand não precisam ser definidas, já que esse aplicativo não permite que o usuário execute tais ações nos dados do estoque.
  Quando o aplicativo é iniciado, ele tenta carregar as informações do banco de dados local. Primeiro, ele recupera a lista de categorias por meio de SqlCeCommand e SqlCeDataReader. O construtor da classe executa e define os objetos ADO.NET que foram utilizados em todo o aplicativo. O SqlCeCommand define a instrução SQL para recuperar as categorias. Após abrir o SqlCeConnection e chamar seu método ExecuteReader, o SqlCeCommand retorna um SqlCeDataReader que aponta para os dados da categoria. Em seguida, SqlCeDataReader é repetido para adicionar as informações de categoria à combobox cboCategory (observe a Listagem 1).

Listagem 1. Carregando as categorias

// Get the categories via a DataReader
SqlCeCommand oCmd = new SqlCeCommand("SELECT CategoryID,
    CategoryName FROM Categories " +
    " ORDER BY CategoryName", this.m_oCn);
this.m_oCn.Open();
SqlCeDataReader oDR = oCmd.ExecuteReader();
// Clear the category list and fill it
cboCategory.Items.Clear();
while (oDR.Read())
{
    cboCategory.Items.Add(new
        Category((string)oDR["CategoryName"],
        (int)oDR["CategoryID"]));
}
oDR.Close();
this.m_oCn.Close();
// Select the first category
cboCategory.SelectedIndex = 0;

Repare que, na Listagem 1, os itens são adicionados à combobox por meio da inclusão de uma instância da classe Category. A classe Category possui propriedades públicas CategoryName e CategoryID. A CategoryName é exibida na combobox e a CategoryID é usada para identificar uma categoria própria. Ela também possui um método ToString que anula o método ToString padrão de um objeto para retornar a CategoryName. Essa é a maneira como o controle da combobox cboCategory sabe qual valor exibir.
  Os produtos também são recuperados quando o aplicativo é carregado, armazenando a lista inteira de produtos em um DataSet. Como o SqlCeDataAdapter já foi definido no construtor da classe para recuperar todos os produtos, a chamada ao método Fill do SqlCeDataAdapter carregará o DataSet, conforme apresentado aqui:

this.m_oDA.Fill(this.m_oDs, "Products");

Repare como, na Listagem 1, depois de carregadas as categorias combobox cboCategory, a primeira categoria da lista é selecionada. Esse processo induz a chamada do método FilterProducts, que filtra os produtos DataSet para mostrar apenas os produtos que pertencem à categoria selecionada. A Listagem 2 mostra o código principal no método FilterProducts, que captura primeiro a categoria selecionada. Isso é feito distribuindo-se a propriedade SelectedItem do controle da combobox cboCategory para a classe Category. Em seguida, cria-se um objeto DataView que filtra os produtos DataSet pelo valor CategoryID selecionado. O DataView oDV não remove os dados do DataSet; em vez disso, ele filtra o DataSet para que somente os produtos solicitados sejam acessíveis através do DataView.
  Em seguida, todos os vínculos dos controles são removidos e redefinidos para o novo objeto DataView. Esse processo permite que o aplicativo carregue todos os produtos de uma vez em um DataSet e mostre apenas um subconjunto filtrado dos dados para o usuário. Lembre-se de que qualquer modificação provisória feita pelo usuário nas informações ainda será rastreada, independentemente de os dados estarem visíveis ou não no DataView ativo. Por exemplo, vamos supor que um usuário modifique o nível de renovação de pedido do produto Chai sob a categoria Beverages (Refrigerantes) e, em seguida, modifique a categoria para Seafood (Frutos do Mar). Embora o produto Chai não esteja mais no DataView ativo, ele ainda estará no DataSet. A modificação provisória ainda existe e continuará a existir até que o usuário pressione a tecla Cancel (que chamará o método RejectChanges do DataSet) ou a tecla Save.
 

Listagem 2. Filtrando os produtos

// Get the selected category and cast it back to the Category class
Category oCat = (Category)cboCategory.SelectedItem;
// Create a DataView that filters the Products DataTable by the
// selected category
DataView oDV = new DataView(this.m_oDs.Tables["Products"],
    "CategoryID = " +
    Convert.ToString(oCat.CategoryID), "ProductName",
    DataViewRowState.CurrentRows);
// Clear all bindings
cboProduct.DataBindings.Clear();
txtUnitPrice.DataBindings.Clear();
txtUnitsInStock.DataBindings.Clear();
txtUnitsOnOrder.DataBindings.Clear();
txtReorderLevel.DataBindings.Clear();
// Bind the DataView to the controls
cboProduct.DataSource = oDV;
cboProduct.DisplayMember = "ProductName";
cboProduct.ValueMember = "ProductID";
txtUnitPrice.DataBindings.Add("Text", oDV, "UnitPrice");
txtUnitsInStock.DataBindings.Add("Text", oDV, "UnitsInStock");
txtUnitsOnOrder.DataBindings.Add("Text", oDV, "UnitsOnOrder");
txtReorderLevel.DataBindings.Add("Text", oDV, "ReorderLevel");
// Select the first Product
cboProduct.SelectedIndex = 0;

Quando o usuário pressiona a tecla Save, o primeiro passo é certificar-se de que a última edição feita por ele tenha sido capturada. Isso é assegurado por meio da captura do número da linha visível nos controles vinculados. Em seguida, esse número é utilizado como referência para a linha atual na tabela Products do DataSet para que EndEdit possa ser chamado. Este método informa ao DataRow que a edição da linha foi concluída e que, se houver alguma modificação, esta deve ser capturada:

//— Make sure the last edit was captured.
int nPosition = BindingContext[this.m_oDs.Tables["Products"]].Position;
this.m_oDs.Tables["Products"].Rows[nPosition].EndEdit();

No aplicativo de estoque, a gravação ocorre em três níveis diferentes: localmente no DataSet, do DataSet para o banco de dados do SQL Server CE local ou entre o banco de dados do SQL Server CE local e o banco de dados de back-end do SQL Server, através da reprodução unificada.
Quando o usuário pressiona a tecla Synch, o processo de reprodução unificada é iniciado. A Listagem 3 mostra o código que instancia o objeto SqlCeReplication (parte do namespace System.Data.SqlServerCe) e define todas as propriedades necessárias para que a reprodução unificada ocorra entre os bancos de dados de back-end e local. A propriedade Publisher é atribuída ao nome do servidor onde se encontra a publicação. As propriedades PublisherLogin e PublisherPassword são atribuídas às contas do SQL Server que eu criei e às quais concedi permissões para publicação. As propriedades InternetUrl, InternetLogin e InternetPassword são utilizadas com o intuito de informar ao assinante o local para onde enviar a solicitação de reprodução unificada. Para que a reprodução unificada ocorra, o assinante precisa se comunicar através do SQL Server CE Server Agent que, neste aplicativo, está localizado em http://lancelot/sqlce/sscesa20.dll.
A propriedade Subscriber do objeto SqlCeReplication é definida como o nome do assinante e visa identificar qual assinante está solicitando a inicialização do processo de reprodução unificada. Poderiam existir vários assinantes, todos executando os mais variados dispositivos Pocket PC. Assim, o nome do assinante pode ajudar o editor a rastrear suas modificações. As propriedades Publication e PublicationDatabase devem ser definidas como os nomes da publicação e do banco de dados, respectivamente, com as quais o dispositivo deseje replicar. Por fim, a propriedade SubscriberConnectionString deve ser definida de modo que aponte para a instância do banco de dados do SQL Server CE, a fim de que o processo de reprodução possa ler as modificações do banco de dados do SQL Server CE e enviá-las ao banco de dados. Uma vez definidas as propriedades, pode-se chamar o método Synchronize do objeto SqlCeReplication e carregar os dados para o aplicativo
:

// Perform the synchronization and load the data
oRpl.Synchronize();
LoadData(false);


Listagem 3. Iniciando a reprodução unificada

//Set up the Replication properties
oRpl = new SqlCeReplication();
oRpl.Publisher = "lancelot";
oRpl.PublisherLogin = "SqlTestUser";
oRpl.PublisherPassword = "hmmm";
oRpl.InternetUrl = "http://lancelot/sqlce/sscesa20.dll";
oRpl.InternetLogin = "iusr_lancelot";
oRpl.InternetPassword = "";
oRpl.Subscriber = "CESubscriberTest";
oRpl.Publication = "NorthwindPub";
oRpl.PublisherDatabase = "Northwind";
oRpl.SubscriberConnectionString =
"Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0;Data Source=" +
this.m_sDataSource;

Tratamento de exceções
  
O trabalho realizado no desenvolvimento de um projeto de Aplicativo para Dispositivos Inteligentes (Smart Device Application) no Visual Studio .NET pode ser dividido em duas categorias principais: configuração da reprodução unificada e criação do aplicativo. Embora a configuração da reprodução unificada exija um planejamento cuidadoso, o processo é claro. Desenvolver aplicativos para dispositivos portáteis é bem parecido com o desenvolvimento de aplicativos Windows Forms. No entanto, o processo de depuração de erros no aplicativo é de arrancar os cabelos. As ferramentas de depuração são bastante úteis, mas, como ocorre em qualquer aplicativo, elas são tão úteis quanto as informações que produzem a partir das exceções. Uma estratégia de tratamento de exceções sólida é tão essencial ao desenvolvimento de aplicativos para dispositivos portáteis quanto em qualquer outro tipo de projeto.
  Quando você considera todos os ajustes de configuração, condições de segurança, autenticação, pré-requisitos de instalação e questões sobre conectividade, está sujeito a muitos erros. É importante que você seja capaz de avaliar as causas desses erros e se concentrar nas soluções e, normalmente, o objeto SqlCeException contém informações úteis nesse sentido.
  A Listagem 4 mostra o método ShowSqlCeErrors que se repete nos objetos SqlCeError, o que representa os diversos erros ocorridos. O método ShowSqlCeErrors faz loops pelos objetos SqlCeError e algumas de suas propriedades recuperam os detalhes dos erros. Em seguida, ele formata as informações de erro em uma string e exibe ao usuário cada erro dentro da exceção através do objeto MessageBox. Esse método é bastante simples de implementar e eu o recomendo como forma de economizar o tempo de procura de causas das exceções.

Listagem 4. Exibindo exceções com elegância

private void ShowSqlCeErrors(SqlCeException exSql)
{  
    StringBuilder oSBMsg = new StringBuilder();
    int i = 0;    foreach (SqlCeError err in exSql.Errors)
    {
        i++;               
        oSBMsg.Append("\n Error # " + i + " of " +
            exSql.Errors.Count);
        oSBMsg.Append("\n Error Code: " + err.HResult);
        oSBMsg.Append("\n Message   : " + err.Message);
        oSBMsg.Append("\n Minor Err.: " + err.NativeError);
        oSBMsg.Append("\n Source    : " + err.Source);   
        for (int j = 0; j < err.NumericErrorParameters.Length; j++)
        {
            if (err.NumericErrorParameters[j] != 0)
            {
                oSBMsg.Append("\n Numeric Parameter: " +
                    err.NumericErrorParameters[j]);
            }
        } 
        for (int k = 0; k < err.ErrorParameters.Length; k++)
        {
            if (err.ErrorParameters[k] != string.Empty)
            {
                oSBMsg.Append("\n Error Parameter  : " +
                    err.ErrorParameters[k]);
            }
        }
        MessageBox.Show(oSBMsg.ToString());
        oSBMsg.Remove(0, oSBMsg.Length);
    }
}


Conclusão
  Neste exemplo, mostrei como implementar um aplicativo para dispositivos baseados em Windows CE que permite aos usuários tirar proveito de dados desconectados. Os dados podem ser armazenados, visualizados e manipulados no próprio dispositivo portátil e, em seguida, ressincronizados com o banco de dados de back-end através da rede local ou até mesmo da Internet. Um dos principais protagonistas deste aplicativo é o ADO.NET. A sua natureza desconectada e a base XML permitem que os dados sejam extremamente portáteis e, ao mesmo tempo, altamente funcionais. Com essas técnicas e ferramentas, o desenvolvimento de soluções empresariais que incluam dispositivos móveis encontra-se ao alcance dos desenvolvedores.