Compartilhar via


Criação de uma Camada de acesso a dados (C#)

por Scott Mitchell

Baixar PDF

Neste tutorial, começaremos do início e criaremos a Camada de Acesso a Dados (DAL), usando DataSets tipados, para acessar as informações em um banco de dados.

Introdução

Como desenvolvedores web, nossas vidas giram em torno de trabalhar com dados. Criamos bancos de dados para armazenar os dados, código para recuperá-los e modificá-los e páginas da web para coletá-los e resumi-los. Este é o primeiro tutorial de uma longa série que explorará técnicas para implementar esses padrões comuns no ASP.NET 2.0. Começaremos com a criação de uma arquitetura de software composta por uma DAL (Camada de Acesso a Dados) usando DataSets Tipados, uma BLL (Camada de Lógica de Negócios) que impõe regras de negócios personalizadas e uma camada de apresentação composta por ASP.NET páginas que compartilham um layout de página comum. Depois que essa base de back-end for estabelecida, passaremos para os relatórios, mostrando como exibir, resumir, coletar e validar dados de um aplicativo da web. Esses tutoriais são voltados para serem concisos e fornecem instruções passo a passo com muitas capturas de tela para orientá-lo visualmente no processo. Cada tutorial está disponível nas versões C# e Visual Basic e inclui um download do código completo usado. (Este primeiro tutorial é bastante longo, mas o resto é apresentado em partes muito mais digeríveis.)

Para esses tutoriais, usaremos uma versão do Microsoft SQL Server 2005 Express Edition do banco de dados Northwind colocada no diretório App_Data . Além do arquivo de banco de dados, a pasta App_Data também contém os scripts SQL para criar o banco de dados, caso você queira usar uma versão diferente do banco de dados. Se você usar uma versão diferente do SQL Server do banco de dados Northwind, precisará atualizar a configuração NORTHWNDConnectionString no arquivo Web.config do aplicativo. O aplicativo Web foi criado usando o Visual Studio 2005 Professional Edition como um projeto de site baseado em sistema de arquivos. No entanto, todos os tutoriais funcionarão igualmente bem com a versão gratuita do Visual Studio 2005, Visual Web Developer.

Neste tutorial, começaremos do início e criaremos a DAL (Camada de Acesso a Dados), seguida pela criação da BLL (Camada de Lógica de Negócios) no segundo tutorial e trabalharemos no layout e na navegação da página no terceiro. Os tutoriais após o terceiro serão construídos sobre a base estabelecida nos três primeiros. Temos muito o que abordar neste primeiro tutorial, então inicie o Visual Studio e vamos começar!

Etapa 1: Criando um projeto Web e conectando-se ao banco de dados

Antes de podermos criar nossa Camada de Acesso a Dados (DAL), primeiro precisamos criar um site e configurar nosso banco de dados. Comece criando um novo site de ASP.NET baseado em sistema de arquivos. Para fazer isso, vá para o menu Arquivo e escolha Novo Site, exibindo a caixa de diálogo Novo Site. Escolha o modelo ASP.NET Site, defina a lista suspensa Local como Sistema de Arquivos, escolha uma pasta para colocar o site e defina o idioma como C#.

Criar um novo site baseado em sistema de arquivos

Figura 1: Criar um novo site baseado no sistema de arquivos (clique para exibir a imagem em tamanho real)

Isso criará um novo site com uma página ASP.NET Default.aspx e uma pasta App_Data .

Com o site criado, a próxima etapa é adicionar uma referência ao banco de dados no Gerenciador de Servidores do Visual Studio. Ao adicionar um banco de dados ao Gerenciador de Servidores, você pode adicionar tabelas, procedimentos armazenados, exibições e assim por diante, tudo de dentro do Visual Studio. Você também pode exibir dados de tabela ou criar suas próprias consultas manualmente ou graficamente por meio do Construtor de consultas. Além disso, quando criarmos os Conjuntos de Dados Tipados para a DAL, precisaremos apontar o Visual Studio para o banco de dados do qual os Conjuntos de Dados Tipados devem ser construídos. Embora possamos fornecer essas informações de conexão nesse momento, o Visual Studio preenche automaticamente uma lista suspensa dos bancos de dados já registrados no Gerenciador de Servidores.

As etapas para adicionar o banco de dados Northwind ao Gerenciador de Servidores dependem se você deseja usar o banco de dados do SQL Server 2005 Express Edition na pasta App_Data ou se você tem uma configuração de servidor de banco de dados do Microsoft SQL Server 2000 ou 2005 que deseja usar.

Usando um banco de dados na pasta App_Data

Se você não tiver um servidor de banco de dados SQL Server 2000 ou 2005 para se conectar, ou simplesmente quiser evitar a necessidade de adicionar o banco de dados a um servidor de banco de dados, poderá usar a versão SQL Server 2005 Express Edition do banco de dados Northwind localizada na pasta App_Data do site baixado (NORTHWND. MDF).

Um banco de dados colocado na pasta App_Data é adicionado automaticamente ao Gerenciador de Servidores. Supondo que você tenha o SQL Server 2005 Express Edition instalado em seu computador, você deverá ver um nó chamado NORTHWND. MDF no Gerenciador de Servidores, que você pode expandir e explorar suas tabelas, exibições, procedimento armazenado e assim por diante (consulte a Figura 2).

A pasta App_Data também pode conter arquivos .mdb do Microsoft Access, que, como seus equivalentes do SQL Server, são adicionados automaticamente ao Gerenciador de Servidores. Se você não quiser usar nenhuma das opções do SQL Server, sempre poderá instalar o banco de dados e os aplicativos do Northwind Traders e soltar no diretório App_Data . No entanto, lembre-se de que os bancos de dados do Access não são tão ricos em recursos quanto o SQL Server e não foram projetados para serem usados em cenários de site. Além disso, alguns dos 35+ tutoriais utilizarão certos recursos no nível do banco de dados que não são suportados pelo Access.

Conectando-se ao banco de dados em um servidor de banco de dados do Microsoft SQL Server 2000 ou 2005

Como alternativa, você pode se conectar a um banco de dados Northwind instalado em um servidor de banco de dados. Se o servidor de banco de dados ainda não tiver o banco de dados Northwind instalado, primeiro você deverá adicioná-lo ao servidor de banco de dados executando o script de instalação incluído no download deste tutorial.

Depois de instalar o banco de dados, vá para o Gerenciador de Servidores no Visual Studio, clique com o botão direito do mouse no nó Conexões de Dados e escolha Adicionar Conexão. Se você não vir o Gerenciador de Servidores, vá para Exibir / Gerenciador de Servidores ou pressione Ctrl+Alt+S. Isso abrirá a caixa de diálogo Adicionar Conexão, onde você pode especificar o servidor ao qual se conectar, as informações de autenticação e o nome do banco de dados. Depois de configurar com êxito as informações de conexão do banco de dados e clicar no botão OK, o banco de dados será adicionado como um nó abaixo do nó Conexões de Dados. Você pode expandir o nó do banco de dados para explorar suas tabelas, exibições, procedimentos armazenados e assim por diante.

Adicionar uma conexão ao banco de dados Northwind do servidor de banco de dados

Figura 2: Adicionar uma conexão ao banco de dados Northwind do servidor de banco de dados

Etapa 2: Criando a camada de acesso a dados

Ao trabalhar com dados, uma opção é incorporar a lógica específica de dados diretamente na camada de apresentação (em um aplicativo Web, as páginas ASP.NET compõem a camada de apresentação). Isso pode assumir a forma de escrever ADO.NET código na parte de código da página ASP.NET ou usar o controle SqlDataSource da parte de marcação. Em ambos os casos, essa abordagem acopla fortemente a lógica de acesso a dados com a camada de apresentação. A abordagem recomendada, no entanto, é separar a lógica de acesso a dados da camada de apresentação. Essa camada separada é chamada de Camada de Acesso a Dados, DAL para abreviar, e normalmente é implementada como um projeto separado da Biblioteca de Classes. Os benefícios dessa arquitetura em camadas estão bem documentados (consulte a seção "Leituras adicionais" no final deste tutorial para obter informações sobre essas vantagens) e é a abordagem que adotaremos nesta série.

Todo o código específico da fonte de dados subjacente, como criar uma conexão com o banco de dados, emitir comandos SELECT, INSERT, UPDATE e DELETE e assim por diante, deve estar localizado no DAL. A camada de apresentação não deve conter nenhuma referência a esse código de acesso a dados, mas deve fazer chamadas para a DAL para toda e qualquer solicitação de dados. As Camadas de Acesso a Dados normalmente contêm métodos para acessar os dados do banco de dados subjacente. O banco de dados Northwind, por exemplo, possui tabelas de Produtos e Categorias que registram os produtos à venda e as categorias às quais eles pertencem. Em nosso DAL, teremos métodos como:

  • GetCategories(), que retornará informações sobre todas as categorias
  • GetProducts(), que retornará informações sobre todos os produtos
  • GetProductsByCategoryID(categoryID), que retornará todos os produtos que pertencem a uma categoria especificada
  • GetProductByProductID(productID), que retornará informações sobre um produto específico

Esses métodos, quando invocados, se conectarão ao banco de dados, emitirão a consulta apropriada e retornarão os resultados. A forma como retornamos esses resultados é importante. Esses métodos podem simplesmente retornar um DataSet ou DataReader preenchido pela consulta de banco de dados, mas o ideal é que esses resultados sejam retornados usando objetos fortemente tipados. Um objeto fortemente tipado é aquele cujo esquema é rigidamente definido em tempo de compilação, enquanto o oposto, um objeto de tipo flexível, é aquele cujo esquema não é conhecido até o tempo de execução.

Por exemplo, o DataReader e o DataSet (por padrão) são objetos de tipo flexível, pois seu esquema é definido pelas colunas retornadas pela consulta de banco de dados usada para preenchê-los. Para acessar uma coluna específica de uma DataTable de tipo livre, precisamos usar uma sintaxe como: DataTable. Linhas[índice]["columnName"]. A digitação flexível do DataTable neste exemplo é exibida pelo fato de que precisamos acessar o nome da coluna usando uma cadeia de caracteres ou índice ordinal. Uma DataTable fortemente tipada, por outro lado, terá cada uma de suas colunas implementadas como propriedades, resultando em um código parecido com: DataTable. Linhas[índice].columnName.

Para retornar objetos fortemente tipados, os desenvolvedores podem criar seus próprios objetos de negócios personalizados ou usar Typed DataSets. Um objeto de negócios é implementado pelo desenvolvedor como uma classe cujas propriedades geralmente refletem as colunas da tabela de banco de dados subjacente que o objeto de negócios representa. Um Conjunto de Dados Tipado é uma classe gerada para você pelo Visual Studio com base em um esquema de banco de dados e cujos membros são fortemente tipados de acordo com esse esquema. O próprio Conjunto de Dados Tipado consiste em classes que estendem as classes ADO.NET DataSet, DataTable e DataRow. Além de DataTables fortemente tipados, os DataSets tipados agora também incluem TableAdapters, que são classes com métodos para preencher as DataTables do DataSet e propagar modificações dentro das DataTables de volta para o banco de dados.

Observação

Para obter mais informações sobre as vantagens e desvantagens de usar Conjuntos de Dados Tipados versus objetos de negócios personalizados, consulte Projetando Componentes da Camada de Dados e Passando Dados por Camadas.

Usaremos DataSets fortemente tipados para a arquitetura desses tutoriais. A Figura 3 ilustra o fluxo de trabalho entre as diferentes camadas de um aplicativo que usa Typed DataSets.

Todo o código de acesso a dados é relegado ao DAL

Figura 3: Todo o código de acesso a dados é relegado ao DAL (clique para exibir a imagem em tamanho real)

Criando um conjunto de dados tipado e um adaptador de tabela

Para começar a criar nosso DAL, começamos adicionando um Conjunto de Dados Tipado ao nosso projeto. Para fazer isso, clique com o botão direito do mouse no nó do projeto no Gerenciador de Soluções e escolha Adicionar um Novo Item. Selecione a opção DataSet na lista de modelos e nomeie-a Northwind.xsd.

Escolha adicionar um novo conjunto de dados ao seu projeto

Figura 4: Escolha adicionar um novo conjunto de dados ao seu projeto (clique para exibir a imagem em tamanho real)

Depois de clicar em Adicionar, quando solicitado a adicionar o DataSet à pasta App_Code , escolha Sim. O Designer do Conjunto de Dados Tipado será exibido e o Assistente de Configuração do TableAdapter será iniciado, permitindo que você adicione seu primeiro TableAdapter ao Conjunto de Dados Tipado.

Um conjunto de dados tipado serve como uma coleção de dados fortemente tipada; ele é composto por instâncias DataTable fortemente tipadas, cada uma das quais, por sua vez, é composta por instâncias DataRow fortemente tipadas. Criaremos uma DataTable fortemente tipada para cada uma das tabelas de banco de dados subjacentes com as quais precisamos trabalhar nesta série de tutoriais. Vamos começar criando uma DataTable para a tabela Products .

Lembre-se de que DataTables fortemente tipados não incluem nenhuma informação sobre como acessar dados de sua tabela de banco de dados subjacente. Para recuperar os dados para preencher o DataTable, usamos uma classe TableAdapter, que funciona como nossa camada de acesso a dados. Para nossa DataTable de produtos, o TableAdapter conterá os métodos GetProducts(), GetProductByCategoryID(categoryID) e assim por diante que invocaremos da camada de apresentação. A função do DataTable é servir como os objetos fortemente tipados usados para passar dados entre as camadas.

O Assistente de Configuração do TableAdapter começa solicitando que você selecione com qual banco de dados trabalhar. A lista suspensa mostra esses bancos de dados no Gerenciador de Servidores. Se você não adicionou o banco de dados Northwind ao Gerenciador de Servidores, poderá clicar no botão Nova Conexão neste momento para fazer isso.

Escolha o banco de dados Northwind na lista suspensa

Figura 5: Escolha o banco de dados Northwind na lista suspensa (clique para exibir a imagem em tamanho real)

Depois de selecionar o banco de dados e clicar em Avançar, você será perguntado se deseja salvar a cadeia de conexão no arquivo Web.config . Ao salvar a cadeia de conexão, você evitará que ela seja codificada nas classes TableAdapter, o que simplifica as coisas se as informações da cadeia de conexão forem alteradas no futuro. Se você optar por salvar a cadeia de conexão no arquivo de configuração, ela será colocada na <seção connectionStrings> , que pode ser opcionalmente criptografada para melhorar a segurança ou modificada posteriormente por meio da nova página de propriedades do ASP.NET 2.0 na Ferramenta de Administração de GUI do IIS, que é mais ideal para administradores.

Salve a cadeia de conexão em Web.config

Figura 6: Salvar a cadeia de conexão em Web.config (clique para exibir a imagem em tamanho completo)

Em seguida, precisamos definir o esquema para o primeiro DataTable fortemente tipado e fornecer o primeiro método para nosso TableAdapter usar ao preencher o DataSet fortemente tipado. Essas duas etapas são realizadas simultaneamente criando uma consulta que retorna as colunas da tabela que queremos refletir em nossa DataTable. No final do assistente, daremos um nome de método a essa consulta. Depois que isso for feito, esse método poderá ser invocado em nossa camada de apresentação. O método executará a consulta definida e preencherá uma DataTable fortemente tipada.

Para começar a definir a consulta SQL, devemos primeiro indicar como queremos que o TableAdapter emita a consulta. Podemos usar uma instrução SQL ad hoc, criar um novo procedimento armazenado ou usar um procedimento armazenado existente. Para esses tutoriais, usaremos instruções SQL ad hoc.

Consultar os dados usando uma instrução SQL ad hoc

Figura 7: Consultar os dados usando uma instrução SQL ad hoc (clique para exibir a imagem em tamanho real)

Neste ponto, podemos digitar a consulta SQL manualmente. Ao criar o primeiro método no TableAdapter, você normalmente deseja que a consulta retorne as colunas que precisam ser expressas na DataTable correspondente. Podemos fazer isso criando uma consulta que retorna todas as colunas e todas as linhas da tabela Products :

Insira a consulta SQL na caixa de texto

Figura 8: Insira a consulta SQL na caixa de texto (clique para exibir a imagem em tamanho real)

Como alternativa, use o Construtor de Consultas e construa graficamente a consulta, como mostra a Figura 9.

Criar a consulta graficamente, por meio do Editor de consultas

Figura 9: Criar a consulta graficamente, por meio do Editor de Consultas (clique para exibir a imagem em tamanho real)

Depois de criar a consulta, mas antes de passar para a próxima tela, clique no botão Opções Avançadas. Em Projetos de Site, "Gerar instruções Insert, Update e Delete" é a única opção avançada selecionada por padrão; se você executar esse assistente em uma Biblioteca de Classes ou em um Projeto do Windows, a opção "Usar simultaneidade otimista" também será selecionada. Deixe a opção "Usar simultaneidade otimista" desmarcada por enquanto. Examinaremos a simultaneidade otimista em tutoriais futuros.

Selecione Somente a opção Gerar instruções de inserção, atualização e exclusão

Figura 10: Selecione apenas a opção Gerar instruções de inserção, atualização e exclusão (clique para exibir a imagem em tamanho real)

Depois de verificar as opções avançadas, clique em Avançar para prosseguir para a tela final. Aqui, somos solicitados a selecionar quais métodos adicionar ao TableAdapter. Há dois padrões para preencher dados:

  • Preencher uma DataTable com essa abordagem: é criado um método que usa uma DataTable como parâmetro e a preenche com base nos resultados da consulta. A classe DataAdapter ADO.NET, por exemplo, implementa esse padrão com seu método Fill().
  • Retornar uma DataTable com essa abordagem, o método cria e preenche a DataTable para você e a retorna como o valor de retorno dos métodos.

Você pode fazer com que o TableAdapter implemente um ou ambos os padrões. Você também pode renomear os métodos fornecidos aqui. Vamos deixar as duas caixas de seleção marcadas, embora usemos apenas o último padrão ao longo desses tutoriais. Além disso, vamos renomear o método GetData bastante genérico para GetProducts.

Se marcada, a caixa de seleção final, "GenerateDBDirectMethods", cria os métodos Insert(), Update() e Delete() para o TableAdapter. Se você deixar essa opção desmarcada, todas as atualizações precisarão ser feitas por meio do único método Update() do TableAdapter, que usa o Typed DataSet, um DataTable, um único DataRow ou uma matriz de DataRows. (Se você tiver desmarcado a opção "Generate Insert, Update, and Delete statements" nas propriedades avançadas na Figura 9, a configuração dessa caixa de seleção não terá efeito.) Vamos deixar esta caixa de seleção marcada.

Alterar o nome do método de GetData para GetProducts

Figura 11: Alterar o nome do método de GetData para GetProducts (clique para exibir a imagem em tamanho real)

Conclua o assistente clicando em Concluir. Depois que o assistente fechar, retornaremos ao DataSet Designer, que mostra a DataTable que acabamos de criar. Você pode ver a lista de colunas na DataTable de Produtos (ProductID, ProductName e assim por diante), bem como os métodos de ProductsTableAdapter (Fill() e GetProducts()).

Os Products DataTable e ProductsTableAdapter foram adicionados ao conjunto de dados tipado

Figura 12: Os produtos DataTable e ProductsTableAdapter foram adicionados ao conjunto de dados tipado (clique para exibir a imagem em tamanho real)

Neste ponto, temos um DataSet tipado com uma única DataTable (Northwind.Products) e uma classe DataAdapter fortemente tipada (NorthwindTableAdapters.ProductsTableAdapter) com um método GetProducts( ). Esses objetos podem ser usados para acessar uma lista de todos os produtos de código como:

NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
    new NorthwindTableAdapters.ProductsTableAdapter();
Northwind.ProductsDataTable products;
products = productsAdapter.GetProducts();
foreach (Northwind.ProductsRow productRow in products)
    Response.Write("Product: " + productRow.ProductName + "<br />");

Esse código não exigia que escrevêssemos um pouco de código específico de acesso a dados. Não precisávamos instanciar nenhuma classe ADO.NET, não precisávamos nos referir a nenhuma cadeia de conexão, consultas SQL ou procedimentos armazenados. Em vez disso, o TableAdapter fornece o código de acesso a dados de baixo nível para nós.

Cada objeto usado neste exemplo também é fortemente tipado, permitindo que o Visual Studio forneça IntelliSense e verificação de tipo em tempo de compilação. E o melhor de tudo é que as DataTables retornadas pelo TableAdapter podem ser associadas a ASP.NET controles Web de dados, como GridView, DetailsView, DropDownList, CheckBoxList e vários outros. O exemplo a seguir ilustra a associação da DataTable retornada pelo método GetProducts() a um GridView em apenas três linhas de código dentro do manipulador de eventos Page_Load .

AllProducts.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="AllProducts.aspx.cs"
    Inherits="AllProducts" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>View All Products in a GridView</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h2>
            All Products</h2>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             CssClass="DataWebControlStyle">
               <HeaderStyle CssClass="HeaderStyle" />
               <AlternatingRowStyle CssClass="AlternatingRowStyle" />
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

AllProducts.aspx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
public partial class AllProducts : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ProductsTableAdapter productsAdapter = new
         ProductsTableAdapter();
        GridView1.DataSource = productsAdapter.GetProducts();
        GridView1.DataBind();
    }
}

A lista de produtos é exibida em um GridView

Figura 13: A lista de produtos é exibida em um GridView (clique para exibir a imagem em tamanho real)

Embora este exemplo exija que escrevamos três linhas de código no manipulador de eventos Page_Load da nossa página de ASP.NET, em tutoriais futuros examinaremos como usar o ObjectDataSource para recuperar declarativamente os dados do DAL. Com o ObjectDataSource, não precisaremos escrever nenhum código e também teremos suporte para paginação e classificação!

Etapa 3: Adicionando métodos parametrizados à camada de acesso a dados

Neste ponto, nossa classe ProductsTableAdapter tem apenas um método, GetProducts(), que retorna todos os produtos no banco de dados. Embora ser capaz de trabalhar com todos os produtos seja definitivamente útil, há momentos em que queremos recuperar informações sobre um produto específico ou todos os produtos que pertencem a uma categoria específica. Para adicionar essa funcionalidade à nossa Camada de Acesso a Dados, podemos adicionar métodos parametrizados ao TableAdapter.

Vamos adicionar o método GetProductsByCategoryID(categoryID). Para adicionar um novo método ao DAL, retorne ao DataSet Designer, clique com o botão direito do mouse na seção ProductsTableAdapter e escolha Add Query.

Clique com o botão direito do mouse no TableAdapter e escolha Adicionar consulta

Figura 14: Clique com o botão direito do mouse no TableAdapter e escolha Adicionar consulta

Primeiro, somos questionados sobre se queremos acessar o banco de dados usando uma instrução SQL ad hoc ou um procedimento armazenado novo ou existente. Vamos optar por usar uma instrução SQL ad-hoc novamente. Em seguida, somos questionados sobre que tipo de consulta SQL gostaríamos de usar. Como queremos retornar todos os produtos que pertencem a uma categoria especificada, queremos escrever uma instrução SELECT que retorne linhas.

Escolha criar uma instrução SELECT que retorna linhas

Figura 15: Escolha criar uma instrução SELECT que retorna linhas (clique para exibir a imagem em tamanho real)

A próxima etapa é definir a consulta SQL usada para acessar os dados. Como queremos retornar apenas os produtos que pertencem a uma categoria específica, uso a mesma instrução SELECT de GetProducts(), mas adiciono a seguinte cláusula WHERE : WHERE CategoryID = @CategoryID. O parâmetro @CategoryID indica ao assistente TableAdapter que o método que estamos criando exigirá um parâmetro de entrada do tipo correspondente (ou seja, um inteiro anulável).

Insira uma consulta para retornar apenas produtos em uma categoria especificada

Figura 16: Insira uma consulta para retornar apenas produtos em uma categoria especificada (clique para exibir a imagem em tamanho real)

Na etapa final, podemos escolher quais padrões de acesso a dados usar, bem como personalizar os nomes dos métodos gerados. Para o padrão Fill, vamos alterar o nome para FillByCategoryID e para retornar um padrão de retorno DataTable (os métodos GetX), vamos usar GetProductsByCategoryID.

Escolha os nomes para os métodos TableAdapter

Figura 17: Escolher os nomes para os métodos TableAdapter (clique para exibir a imagem em tamanho completo)

Depois de concluir o assistente, o DataSet Designer inclui os novos métodos TableAdapter.

Os produtos agora podem ser consultados por categoria

Figura 18: Os produtos agora podem ser consultados por categoria

Reserve um momento para adicionar um método GetProductByProductID(productID) usando a mesma técnica.

Essas consultas parametrizadas podem ser testadas diretamente do DataSet Designer. Clique com o botão direito do mouse no método no TableAdapter e escolha Visualizar dados. Em seguida, insira os valores a serem usados para os parâmetros e clique em Visualizar.

Os produtos pertencentes à categoria Bebidas são mostrados

Figura 19: Os produtos pertencentes à categoria Bebidas são mostrados (Clique para visualizar a imagem em tamanho real)

Com o método GetProductsByCategoryID(categoryID) em nosso DAL, agora podemos criar uma página ASP.NET que exibe apenas os produtos em uma categoria especificada. O exemplo a seguir mostra todos os produtos que estão na categoria Bebidas, que têm um CategoryID de 1.

Beverages.asp

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Beverages.aspx.cs"
    Inherits="Beverages" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h2>Beverages</h2>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             CssClass="DataWebControlStyle">
               <HeaderStyle CssClass="HeaderStyle" />
               <AlternatingRowStyle CssClass="AlternatingRowStyle" />
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

Beverages.aspx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
public partial class Beverages : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ProductsTableAdapter productsAdapter = new
         ProductsTableAdapter();
        GridView1.DataSource =
          productsAdapter.GetProductsByCategoryID(1);
        GridView1.DataBind();
    }
}

Os produtos da categoria Bebidas são exibidos

Figura 20: Os produtos da categoria Bebidas são exibidos (clique para exibir a imagem em tamanho real)

Etapa 4: Inserir, atualizar e excluir dados

Há dois padrões comumente usados para inserir, atualizar e excluir dados. O primeiro padrão, que chamarei de padrão direto do banco de dados, envolve a criação de métodos que, quando invocados, emitem um comando INSERT, UPDATE ou DELETE para o banco de dados que opera em um único registro de banco de dados. Esses métodos normalmente são passados em uma série de valores escalares (inteiros, cadeias de caracteres, booleanos, DateTimes e assim por diante) que correspondem aos valores a serem inseridos, atualizados ou excluídos. Por exemplo, com esse padrão para a tabela Products , o método delete usaria um parâmetro inteiro, indicando o ProductID do registro a ser excluído, enquanto o método insert usaria uma cadeia de caracteres para o ProductName, um decimal para o UnitPrice, um inteiro para o UnitsOnStock e assim por diante.

Cada solicitação de inserção, atualização e exclusão é enviada ao banco de dados imediatamente

Figura 21: Cada solicitação de inserção, atualização e exclusão é enviada ao banco de dados imediatamente (clique para exibir a imagem em tamanho real)

O outro padrão, ao qual me referirei como padrão de atualização em lote, é atualizar um DataSet, DataTable ou coleção inteira de DataRows em uma chamada de método. Com esse padrão, um desenvolvedor exclui, insere e modifica as DataRows em uma DataTable e, em seguida, passa essas DataRows ou DataTable para um método de atualização. Em seguida, esse método enumera as DataRows passadas, determina se elas foram ou não modificadas, adicionadas ou excluídas (por meio do valor da propriedade RowState da DataRow) e emite a solicitação de banco de dados apropriada para cada registro.

Todas as alterações são sincronizadas com o banco de dados quando o método de atualização é invocado

Figura 22: Todas as alterações são sincronizadas com o banco de dados quando o método de atualização é invocado (clique para exibir a imagem em tamanho real)

O TableAdapter usa o padrão de atualização em lote por padrão, mas também dá suporte ao padrão direto do banco de dados. Como selecionamos a opção "Gerar instruções Insert, Update e Delete" nas Propriedades Avançadas ao criar nosso TableAdapter, o ProductsTableAdapter contém um método Update(), que implementa o padrão de atualização em lote. Especificamente, o TableAdapter contém um método Update() que pode ser passado o Typed DataSet, um DataTable fortemente tipado ou um ou mais DataRows. Se você deixou a caixa de seleção "GenerateDBDirectMethods" marcada ao criar o TableAdapter pela primeira vez, o padrão direto do banco de dados também será implementado por meio dos métodos Insert(), Update() e Delete().

Ambos os padrões de modificação de dados usam as propriedades InsertCommand, UpdateCommand e DeleteCommand do TableAdapter para emitir seus comandos INSERT, UPDATE e DELETE para o banco de dados. Você pode inspecionar e modificar as propriedades InsertCommand, UpdateCommand e DeleteCommand clicando no TableAdapter no DataSet Designer e indo para a janela Propriedades. (Certifique-se de ter selecionado o TableAdapter e que o ProductsTableAdapter é aquele selecionado na lista suspensa na janela Propriedades.)

O TableAdapter tem as propriedades InsertCommand, UpdateCommand e DeleteCommand

Figura 23: O TableAdapter tem as propriedades InsertCommand, UpdateCommand e DeleteCommand (clique para exibir a imagem em tamanho real)

Para examinar ou modificar qualquer uma dessas propriedades de comando de banco de dados, clique na subpropriedade CommandText , que abrirá o Construtor de Consultas.

Configurar as instruções INSERT, UPDATE e DELETE no Construtor de Consultas

Figura 24: Configurar as instruções INSERT, UPDATE e DELETE no Construtor de Consultas (clique para exibir a imagem em tamanho real)

O exemplo de código a seguir mostra como usar o padrão de atualização em lote para dobrar o preço de todos os produtos que não foram descontinuados e que têm 25 unidades em estoque ou menos:

NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
  new NorthwindTableAdapters.ProductsTableAdapter();
// For each product, double its price if it is not discontinued and
// there are 25 items in stock or less
Northwind.ProductsDataTable products = productsAdapter.GetProducts();
foreach (Northwind.ProductsRow product in products)
   if (!product.Discontinued && product.UnitsInStock <= 25)
      product.UnitPrice *= 2;
// Update the products
productsAdapter.Update(products);

O código a seguir ilustra como usar o padrão direto do banco de dados para excluir programaticamente um produto específico, atualizar um e adicionar um novo:

NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
    new NorthwindTableAdapters.ProductsTableAdapter();
// Delete the product with ProductID 3
productsAdapter.Delete(3);
// Update Chai (ProductID of 1), setting the UnitsOnOrder to 15
productsAdapter.Update("Chai", 1, 1, "10 boxes x 20 bags",
  18.0m, 39, 15, 10, false, 1);
// Add a new product
productsAdapter.Insert("New Product", 1, 1,
  "12 tins per carton", 14.95m, 15, 0, 10, false);

Criando métodos personalizados de inserção, atualização e exclusão

Os métodos Insert(), Update() e Delete() criados pelo método direto do banco de dados podem ser um pouco complicados, especialmente para tabelas com muitas colunas. Olhando para o exemplo de código anterior, sem a ajuda do IntelliSense, não fica particularmente claro qual coluna da tabela Products é mapeada para cada parâmetro de entrada para os métodos Update() e Insert( ). Pode haver momentos em que queremos atualizar apenas uma ou duas colunas, ou queremos um método Insert() personalizado que, talvez, retorne o valor do campo IDENTITY (incremento automático) do registro recém-inserido.

Para criar esse método personalizado, retorne ao Designer de Conjunto de Dados. Clique com o botão direito do mouse no TableAdapter e escolha Add Query, retornando ao assistente TableAdapter. Na segunda tela podemos indicar o tipo de consulta a ser criada. Vamos criar um método que adiciona um novo produto e, em seguida, retorna o valor do ProductID do registro recém-adicionado. Portanto, opte por criar uma consulta INSERT .

Criar um método para adicionar uma nova linha à tabela Products

Figura 25: Criar um método para adicionar uma nova linha à tabela Produtos (clique para exibir a imagem em tamanho real)

Na próxima tela, o CommandText do InsertCommand é exibido. Aumente essa consulta adicionando SELECT SCOPE_IDENTITY() no final da consulta, que retornará o último valor de identidade inserido em uma coluna IDENTITY no mesmo escopo. (Consulte a documentação técnica para obter mais informações sobre SCOPE_IDENTITY() e por que você provavelmente deseja usar SCOPE_IDENTITY() em vez de @@IDENTITY.) Certifique-se de encerrar a instrução INSERT com um ponto-e-vírgula antes de adicionar a instrução SELECT.

Aumentar a consulta para retornar o valor SCOPE_IDENTITY()

Figura 26: Aumentar a consulta para retornar o valor SCOPE_IDENTITY() (clique para exibir a imagem em tamanho real)

Por fim, nomeie o novo método InsertProduct.

Defina o novo nome do método como InsertProduct

Figura 27: Definir o novo nome do método como InsertProduct (clique para exibir a imagem em tamanho real)

Ao retornar ao DataSet Designer, você verá que o ProductsTableAdapter contém um novo método, InsertProduct. Se esse novo método não tiver um parâmetro para cada coluna na tabela Produtos, é provável que você tenha esquecido de encerrar a instrução INSERT com um ponto-e-vírgula. Configure o método InsertProduct e verifique se você tem um ponto-e-vírgula delimitando as instruções INSERT e SELECT .

Por padrão, os métodos de inserção emitem métodos que não são de consulta, o que significa que eles retornam o número de linhas afetadas. No entanto, queremos que o método InsertProduct retorne o valor retornado pela consulta, não o número de linhas afetadas. Para fazer isso, ajuste a propriedade ExecuteMode do método InsertProduct como Scalar.

Alterar a propriedade ExecuteMode para escalar

Figura 28: Alterar a propriedade ExecuteMode para Escalar (clique para exibir a imagem em tamanho real)

O código a seguir mostra esse novo método InsertProduct em ação:

NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
    new NorthwindTableAdapters.ProductsTableAdapter();
// Add a new product
int new_productID = Convert.ToInt32(productsAdapter.InsertProduct
    ("New Product", 1, 1, "12 tins per carton", 14.95m, 10, 0, 10, false));
// On second thought, delete the product
productsAdapter.Delete(new_productID);

Etapa 5: Concluindo a camada de acesso a dados

Observe que a classe ProductsTableAdapters retorna os valores CategoryID e SupplierID da tabela Products , mas não inclui a coluna CategoryName da tabela Categories ou a coluna CompanyName da tabela Suppliers , embora essas sejam provavelmente as colunas que queremos exibir ao mostrar informações do produto. Podemos aumentar o método inicial do TableAdapter, GetProducts(), para incluir os valores das colunas CategoryName e CompanyName , o que atualizará o DataTable fortemente tipado para incluir essas novas colunas também.

No entanto, isso pode representar um problema, pois os métodos do TableAdapter para inserir, atualizar e excluir dados são baseados nesse método inicial. Felizmente, os métodos gerados automaticamente para inserir, atualizar e excluir não são afetados por subconsultas na cláusula SELECT . Ao ter o cuidado de adicionar nossas consultas a Categorias e Fornecedores como subconsultas, em vez de JOIN s, evitaremos ter que retrabalhar esses métodos para modificar dados. Clique com o botão direito do mouse no método GetProducts() no ProductsTableAdapter e escolha Configurar. Em seguida, ajuste a cláusula SELECT para que ela se pareça com:

SELECT     ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
(SELECT CategoryName FROM Categories
WHERE Categories.CategoryID = Products.CategoryID) as CategoryName,
(SELECT CompanyName FROM Suppliers
WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName
FROM         Products

Atualize a instrução SELECT para o método GetProducts()

Figura 29: Atualizar a instrução SELECT para o método GetProducts() (clique para exibir a imagem em tamanho real)

Depois de atualizar o método GetProducts() para usar essa nova consulta, o DataTable incluirá duas novas colunas: CategoryName e SupplierName.

A DataTable de Produtos tem duas novas colunas

Figura 30: A DataTable de produtos tem duas novas colunas

Reserve um momento para atualizar a cláusula SELECT no método GetProductsByCategoryID(categoryID) também.

Se você atualizar a sintaxe GetProducts() SELECT usando JOIN, o DataSet Designer não poderá gerar automaticamente os métodos para inserir, atualizar e excluir dados de banco de dados usando o padrão direto de banco de dados. Em vez disso, você terá que criá-los manualmente, assim como fizemos com o método InsertProduct anteriormente neste tutorial. Além disso, você precisará fornecer manualmente os valores das propriedades InsertCommand, UpdateCommand e DeleteCommand se quiser usar o padrão de atualização em lote.

Adicionando os TableAdapters restantes

Até agora, só vimos como trabalhar com um único TableAdapter para uma única tabela de banco de dados. No entanto, o banco de dados Northwind contém várias tabelas relacionadas com as quais precisaremos trabalhar em nosso aplicativo Web. Um Conjunto de Dados Tipado pode conter várias DataTables relacionadas. Portanto, para concluir nossa DAL, precisamos adicionar DataTables para as outras tabelas que usaremos nesses tutoriais. Para adicionar um novo TableAdapter a um Conjunto de Dados Tipado, abra o Designer de Conjunto de Dados, clique com o botão direito do mouse no Designer e escolha Adicionar/TableAdapter. Isso criará um novo DataTable e TableAdapter e orientará você pelo assistente que examinamos anteriormente neste tutorial.

Reserve alguns minutos para criar os seguintes TableAdapters e métodos usando as consultas a seguir. Observe que as consultas no ProductsTableAdapter incluem as subconsultas para obter a categoria de cada produto e os nomes de fornecedores. Além disso, se você estiver acompanhando, já adicionou os métodos GetProducts() e GetProductsByCategoryID(categoryID) da classe ProductsTableAdapter.

  • ProdutosTableAdapter

    • Obter produtos:

      SELECT     ProductID, ProductName, SupplierID, 
      CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, 
      UnitsOnOrder, ReorderLevel, Discontinued, 
      (SELECT CategoryName FROM Categories WHERE
      Categories.CategoryID = Products.CategoryID) as 
      CategoryName, (SELECT CompanyName FROM Suppliers
      WHERE Suppliers.SupplierID = Products.SupplierID) 
      as SupplierName
      FROM         Products
      
    • GetProductsByCategoryID:

      SELECT     ProductID, ProductName, SupplierID, CategoryID,
      QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
      ReorderLevel, Discontinued, (SELECT CategoryName
      FROM Categories WHERE Categories.CategoryID = 
      Products.CategoryID) as CategoryName,
      (SELECT CompanyName FROM Suppliers WHERE
      Suppliers.SupplierID = Products.SupplierID)
      as SupplierName
      FROM         Products
      WHERE      CategoryID = @CategoryID
      
    • GetProductsBySupplierID:

      SELECT     ProductID, ProductName, SupplierID, CategoryID,
      QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
      ReorderLevel, Discontinued, (SELECT CategoryName
      FROM Categories WHERE Categories.CategoryID = 
      Products.CategoryID) as CategoryName, 
      (SELECT CompanyName FROM Suppliers WHERE 
      Suppliers.SupplierID = Products.SupplierID) as SupplierName
      FROM         Products
      WHERE SupplierID = @SupplierID
      
    • GetProductByProductID:

      SELECT     ProductID, ProductName, SupplierID, CategoryID,
      QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
      ReorderLevel, Discontinued, (SELECT CategoryName 
      FROM Categories WHERE Categories.CategoryID = 
      Products.CategoryID) as CategoryName, 
      (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) 
      as SupplierName
      FROM         Products
      WHERE ProductID = @ProductID
      
  • CategoriasTabelaAdaptador

    • GetCategories:

      SELECT     CategoryID, CategoryName, Description
      FROM         Categories
      
    • GetCategoryByCategoryID:

      SELECT     CategoryID, CategoryName, Description
      FROM         Categories
      WHERE CategoryID = @CategoryID
      
  • SuppliersTableAdapter

    • Obter fornecedores:

      SELECT     SupplierID, CompanyName, Address,
      City, Country, Phone
      FROM         Suppliers
      
    • GetSuppliersByCountry:

      SELECT     SupplierID, CompanyName, Address,
      City, Country, Phone
      FROM         Suppliers
      WHERE Country = @Country
      
    • GetSupplierBySupplierID:

      SELECT     SupplierID, CompanyName, Address,
      City, Country, Phone
      FROM         Suppliers
      WHERE SupplierID = @SupplierID
      
  • EmployeesTableAdapter

    • Obter funcionários:

      SELECT     EmployeeID, LastName, FirstName, Title,
      HireDate, ReportsTo, Country
      FROM         Employees
      
    • GetEmployeesByManager:

      SELECT     EmployeeID, LastName, FirstName, Title, 
      HireDate, ReportsTo, Country
      FROM         Employees
      WHERE ReportsTo = @ManagerID
      
    • GetEmployeeByEmployeeID:

      SELECT     EmployeeID, LastName, FirstName, Title,
      HireDate, ReportsTo, Country
      FROM         Employees
      WHERE EmployeeID = @EmployeeID
      

O designer de conjunto de dados após a adição dos quatro TableAdapters

Figura 31: O designer de conjunto de dados após a adição dos quatro TableAdapters (clique para exibir a imagem em tamanho real)

Adicionando código personalizado à DAL

Os TableAdapters e DataTables adicionados ao Conjunto de Dados Tipado são expressos como um arquivo de Definição de Esquema XML (Northwind.xsd). Você pode exibir essas informações de esquema clicando com o botão direito do mouse no arquivo Northwind.xsd no Gerenciador de Soluções e escolhendo Exibir Código.

O arquivo XSD (definição de esquema XML) para o conjunto de dados tipado Northwinds

Figura 32: O arquivo XSD (definição de esquema XML) para o conjunto de dados tipado Northwinds (clique para exibir a imagem em tamanho real)

Essas informações de esquema são convertidas em código C# ou Visual Basic em tempo de design quando compiladas ou em tempo de execução (se necessário), momento em que você pode percorrê-las com o depurador. Para exibir esse código gerado automaticamente, vá para o Modo de Exibição de Classe e faça uma busca detalhada nas classes TableAdapter ou Typed DataSet. Se você não vir a Visualização de Classe na tela, vá para o menu Exibir e selecione-a a partir daí ou pressione Ctrl+Shift+C. No Modo de Exibição de Classe, você pode ver as propriedades, os métodos e os eventos das classes Typed DataSet e TableAdapter. Para exibir o código de um método específico, clique duas vezes no nome do método na Exibição de Classe ou clique com o botão direito do mouse nele e escolha Ir para Definição.

Inspecione o código gerado automaticamente selecionando Ir para definição no modo de exibição de classe

Figura 33: Inspecionar o código gerado automaticamente selecionando Ir para definição na exibição de classe

Embora o código gerado automaticamente possa economizar muito tempo, o código geralmente é muito genérico e precisa ser personalizado para atender às necessidades exclusivas de um aplicativo. O risco de estender o código gerado automaticamente, no entanto, é que a ferramenta que gerou o código pode decidir que é hora de "regenerar" e substituir suas personalizações. Com o novo conceito de classe parcial do .NET 2.0, é fácil dividir uma classe em vários arquivos. Isso nos permite adicionar nossos próprios métodos, propriedades e eventos às classes geradas automaticamente sem ter que nos preocupar com o Visual Studio substituindo nossas personalizações.

Para demonstrar como personalizar o DAL, vamos adicionar um método GetProducts() à classe SuppliersRow . A classe SuppliersRow representa um único registro na tabela Suppliers ; cada fornecedor pode fornecer de zero a muitos produtos, portanto , GetProducts() retornará esses produtos do fornecedor especificado. Para fazer isso, crie um novo arquivo de classe na pasta App_Code chamada SuppliersRow.cs e adicione o seguinte código:

using System;
using System.Data;
using NorthwindTableAdapters;
public partial class Northwind
{
    public partial class SuppliersRow
    {
        public Northwind.ProductsDataTable GetProducts()
        {
            ProductsTableAdapter productsAdapter =
             new ProductsTableAdapter();
            return
              productsAdapter.GetProductsBySupplierID(this.SupplierID);
        }
    }
}

Essa classe parcial instrui o compilador que, ao criar a classe Northwind.SuppliersRow , inclua o método GetProducts() que acabamos de definir. Se você criar seu projeto e retornar ao Modo de Exibição de Classe, verá GetProducts() agora listado como um método de Northwind.SuppliersRow.

O método GetProducts() agora faz parte da classe Northwind.SuppliersRow

Figura 34: O método GetProducts() agora faz parte da classe Northwind.SuppliersRow

O método GetProducts() agora pode ser usado para enumerar o conjunto de produtos de um fornecedor específico, como mostra o código a seguir:

NorthwindTableAdapters.SuppliersTableAdapter suppliersAdapter =
    new NorthwindTableAdapters.SuppliersTableAdapter();
// Get all of the suppliers
Northwind.SuppliersDataTable suppliers =
  suppliersAdapter.GetSuppliers();
// Enumerate the suppliers
foreach (Northwind.SuppliersRow supplier in suppliers)
{
    Response.Write("Supplier: " + supplier.CompanyName);
    Response.Write("<ul>");
    // List the products for this supplier
    Northwind.ProductsDataTable products = supplier.GetProducts();
    foreach (Northwind.ProductsRow product in products)
        Response.Write("<li>" + product.ProductName + "</li>");
    Response.Write("</ul><p> </p>");
}

Esses dados também podem ser exibidos em qualquer um dos aplicativos ASP. NET da Web. A página a seguir usa um controle GridView com dois campos:

  • Um BoundField que exibe o nome de cada fornecedor e
  • Um TemplateField que contém um controle BulletedList que está associado aos resultados retornados pelo método GetProducts() para cada fornecedor.

Examinaremos como exibir esses relatórios de detalhes mestres em tutoriais futuros. Por enquanto, este exemplo foi projetado para ilustrar usando o método personalizado adicionado à classe Northwind.SuppliersRow .

SuppliersAndProducts.aspx

<%@ Page Language="C#" CodeFile="SuppliersAndProducts.aspx.cs"
    AutoEventWireup="true" Inherits="SuppliersAndProducts" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h2>
            Suppliers and Their Products</h2>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             AutoGenerateColumns="False"
             CssClass="DataWebControlStyle">
                <HeaderStyle CssClass="HeaderStyle" />
                <AlternatingRowStyle CssClass="AlternatingRowStyle" />
                <Columns>
                    <asp:BoundField DataField="CompanyName"
                      HeaderText="Supplier" />
                    <asp:TemplateField HeaderText="Products">
                        <ItemTemplate>
                            <asp:BulletedList ID="BulletedList1"
                             runat="server" DataSource="<%# ((Northwind.SuppliersRow) ((System.Data.DataRowView) Container.DataItem).Row).GetProducts() %>"
                                 DataTextField="ProductName">
                            </asp:BulletedList>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

SuppliersAndProducts.aspx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
public partial class SuppliersAndProducts : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        SuppliersTableAdapter suppliersAdapter = new
          SuppliersTableAdapter();
        GridView1.DataSource = suppliersAdapter.GetSuppliers();
        GridView1.DataBind();
    }
}

O nome da empresa do fornecedor está listado na coluna da esquerda, seus produtos à direita

Figura 35: O nome da empresa do fornecedor está listado na coluna da esquerda, seus produtos à direita (clique para exibir a imagem em tamanho real)

Resumo

Ao criar um aplicativo da web, a criação da DAL deve ser uma das primeiras etapas, ocorrendo antes de você começar a criar sua camada de apresentação. Com o Visual Studio, criar uma DAL com base em Typed DataSets é uma tarefa que pode ser realizada em 10 a 15 minutos sem escrever uma linha de código. Os tutoriais daqui para frente se basearão neste DAL. No próximo tutorial, definiremos várias regras de negócios e veremos como implementá-las em uma camada lógica de negócios separada.

Boa programação!

Leitura Adicional

Para obter mais informações sobre os tópicos discutidos neste tutorial, consulte os seguintes recursos:

Treinamento em vídeo sobre tópicos contidos neste tutorial

Sobre o autor

Scott Mitchell, autor de sete livros ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Web da Microsoft desde 1998. Scott trabalha como consultor, instrutor e escritor independente. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 horas. Ele pode ser contatado em mitchell@4GuysFromRolla.com. ou através de seu blog, que pode ser encontrado em http://ScottOnWriting.NET.

Agradecimentos especiais a

Esta série de tutoriais foi revisada por muitos revisores úteis. Os principais revisores deste tutorial foram Ron Green, Hilton Giesenow, Dennis Patterson, Liz Shulok, Abel Gomez e Carlos Santos. Interessado em revisar meus próximos artigos do MSDN? Em caso afirmativo, envie-me uma mensagem para mitchell@4GuysFromRolla.com.