Compartilhar via


Atualizar o TableAdapter para usar JOINs (C#)

por Scott Mitchell

Baixar PDF

Ao trabalhar com um banco de dados, é comum solicitar dados distribuídos em várias tabelas. Para recuperar dados de duas tabelas diferentes, podemos usar uma subconsulta correlacionada ou uma operação JOIN. Neste tutorial, comparamos as subconsultas correlacionadas e a sintaxe JOIN antes de examinar como criar um TableAdapter que inclua um JOIN em sua consulta principal.

Introdução

Com bancos de dados relacionais, os dados com os quais estamos interessados em trabalhar geralmente são distribuídos em várias tabelas. Por exemplo, ao exibir informações do produto, provavelmente queremos listar os nomes de cada categoria e fornecedor correspondentes de cada produto. A tabela Products contém valores CategoryID e SupplierID, mas os nomes reais das categorias e fornecedores estão nas tabelas Categories e Suppliers, respectivamente.

Para recuperar informações de outra tabela relacionada, podemos usar subconsultas correlacionadas ou JOINs. Uma subconsulta correlacionada é uma consulta aninhada SELECT que faz referência a colunas na consulta externa. Por exemplo, no tutorial Criando uma Camada de Acesso a Dados , usamos duas subconsultas correlacionadas na ProductsTableAdapter consulta principal para retornar os nomes de categoria e fornecedor para cada produto. Um JOIN é um constructo SQL que mescla linhas relacionadas de duas tabelas diferentes. Usamos um JOIN no tutorial Consulta de Dados com o Controle SqlDataSource para exibir informações de categoria ao lado de cada produto.

O motivo pelo qual nos abstivemos de usar JOIN s com os TableAdapters é devido a limitações no assistente do TableAdapter para gerar automaticamente as instruções correspondentes de INSERT, UPDATE e DELETE. Mais especificamente, se a consulta principal do TableAdapter contiver qualquer JOIN, o TableAdapter não poderá criar automaticamente as instruções SQL ad hoc ou procedimentos armazenados para suas propriedades InsertCommand, UpdateCommand e DeleteCommand.

Neste tutorial, compararemos e contrastaremos brevemente subconsultas correlacionadas e JOIN s antes de explorar como incluir JOIN s em uma consulta principal ao criar um TableAdapter.

Comparando e contrastando subconsultas correlacionadas eJOIN s

Lembre-se de que o ProductsTableAdapter criado no primeiro tutorial no Northwind DataSet usa subconsultas correlacionadas para recuperar a categoria correspondente e o nome do fornecedor de cada produto. A consulta principal de ProductsTableAdapter é apresentada abaixo.

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

As duas subconsultas correlacionadas – (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) e (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) – são consultas SELECT que retornam um único valor por produto, como uma coluna adicional na lista de colunas da instrução externa SELECT.

Como alternativa, um JOIN pode ser usado para retornar o fornecedor e o nome da categoria de cada produto. A consulta a seguir retorna a mesma saída que a acima, mas usa JOIN s no lugar de subconsultas:

SELECT ProductID, ProductName, Products.SupplierID, Products.CategoryID, 
       QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, 
       ReorderLevel, Discontinued,
       Categories.CategoryName, 
       Suppliers.CompanyName as SupplierName
FROM Products
    LEFT JOIN Categories ON
        Categories.CategoryID = Products.CategoryID
    LEFT JOIN Suppliers ON
        Suppliers.SupplierID = Products.SupplierID

JOIN mescla os registros de uma tabela com registros de outra tabela com base em determinados critérios. Na consulta acima, por exemplo, o LEFT JOIN Categories ON Categories.CategoryID = Products.CategoryID instrui o SQL Server a mesclar cada registro de produto com o registro de categoria cujo valor CategoryID corresponda ao valor CategoryID do produto. O resultado mesclado nos permite trabalhar com os campos de categoria correspondentes para cada produto (como CategoryName).

Observação

JOIN s são comumente usados ao consultar dados de bancos de dados relacionais. Se você não estiver familiarizado com a JOIN sintaxe ou precisar aprimorar um pouco seu uso, recomendaria o tutorial de SQL Join do W3Schools. Também vale a pena ler as JOIN seções Conceitos Básicos e Conceitos Básicos da Subconsulta dos Manuais Online do SQL.

Como JOIN e subconsultas correlacionadas podem ser usadas para recuperar dados relacionados de outras tabelas, muitos desenvolvedores ficam coçando a cabeça e se perguntando qual abordagem usar. Todos os gurus do SQL com quem falei disseram aproximadamente a mesma coisa, que isso realmente não importa em termos de desempenho, pois o SQL Server produzirá planos de execução praticamente idênticos. O conselho deles, então, é usar a técnica com a qual você e sua equipe estão mais confortáveis. Merece ser observado que, após darem este conselho, esses especialistas expressam imediatamente sua preferência por JOIN em vez de subconsultas correlacionadas.

Ao criar uma Camada de Acesso a Dados usando Conjuntos de Dados Tipados, as ferramentas funcionam melhor quando se usam subconsultas. Em particular, o assistente de TableAdapter não gerará automaticamente as instruções correspondentes INSERT, UPDATE e DELETE se a consulta principal contiver qualquer JOIN, mas gerará automaticamente essas instruções quando subconsultas correlacionadas forem usadas.

Para explorar essa deficiência, crie um Conjunto de Dados Tipado temporário na ~/App_Code/DAL pasta. Durante o assistente de Configuração do TableAdapter, escolha usar instruções SQL ad hoc e insira a seguinte SELECT consulta (consulte a Figura 1):

SELECT ProductID, ProductName, Products.SupplierID, Products.CategoryID, 
       QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, 
       ReorderLevel, Discontinued,
       Categories.CategoryName, 
       Suppliers.CompanyName as SupplierName
FROM Products
    LEFT JOIN Categories ON
        Categories.CategoryID = Products.CategoryID
    LEFT JOIN Suppliers ON
        Suppliers.SupplierID = Products.SupplierID

Captura de tela mostrando a janela do Assistente de Configuração do TableAdaptor com uma consulta inserida que contém JOINs.

Figura 1: Insira uma consulta principal que contenha JOIN s (Clique para exibir a imagem em tamanho real)

Por padrão, o TableAdapter criará automaticamente as instruções INSERT, UPDATE e DELETE com base na consulta principal. Se você clicar no botão Avançado, poderá ver que esse recurso está habilitado. Apesar dessa configuração, o TableAdapter não será capaz de criar as instruções INSERT, UPDATE e DELETE porque a consulta principal contém um JOIN.

Captura de tela mostrando a janela de Opções Avançadas com a caixa de seleção

Figura 2: inserir uma consulta principal que contém JOIN s

Clique em "Finalizar" para completar o assistente. Neste ponto, o Designer do DataSet incluirá um único TableAdapter com um DataTable com colunas para cada um dos campos retornados na lista de colunas da consulta SELECT. Isso inclui o CategoryName e SupplierName, como mostra a Figura 3.

A DataTable inclui uma coluna para cada campo retornado na lista de colunas

Figura 3: A DataTable inclui uma coluna para cada campo retornado na lista de colunas

Embora o DataTable tenha as colunas apropriadas, o TableAdapter não tem valores para suas InsertCommand, UpdateCommand e DeleteCommand propriedades. Para confirmar isso, clique no TableAdapter no Designer e vá para a janela Propriedades. Lá, você verá que as propriedades InsertCommand, UpdateCommand, e DeleteCommand estão definidas como (Nenhum).

As propriedades InsertCommand, UpdateCommand e DeleteCommand são definidas como (Nenhuma)

Figura 4: As propriedades InsertCommand, UpdateCommand, e DeleteCommand são definidas como (Nenhum) (Clique para exibir a imagem em tamanho real)

Para contornar essa deficiência, podemos fornecer manualmente as instruções SQL e os parâmetros para as propriedades InsertCommand, UpdateCommand e DeleteCommand por meio da janela Propriedades. Como alternativa, poderíamos começar configurando a consulta principal do TableAdapter para não incluir nenhum(a) JOIN. Isso permitirá que as instruções INSERT, UPDATE e DELETE sejam geradas automaticamente para nós. Depois de concluir o assistente, podemos atualizar manualmente o TableAdapter s SelectCommand da janela Propriedades para que ele inclua a JOIN sintaxe.

Embora essa abordagem funcione, é muito frágil ao usar consultas SQL ad hoc, pois, sempre que a consulta principal do TableAdapter é reconfigurada através do assistente, as instruções INSERT, UPDATE, e DELETE geradas automaticamente são recriadas. Isso significa que todas as personalizações feitas posteriormente seriam perdidas se clicamos com o botão direito do mouse no TableAdapter, escolhemos Configurar no menu de contexto e concluímos o assistente novamente.

A fragilidade das instruções INSERT, UPDATE e DELETE geradas automaticamente pelo TableAdapter é, felizmente, limitada a instruções SQL ad hoc. Se o TableAdapter usar procedimentos armazenados, você poderá personalizar os SelectCommand, InsertCommand, UpdateCommand ou DeleteCommand e executar novamente o assistente de Configuração do TableAdapter sem medo de que os procedimentos armazenados sejam modificados.

Nas próximas etapas, criaremos um TableAdapter que, inicialmente, usa uma consulta principal que omite qualquer JOIN s para que os procedimentos armazenados de inserção, atualização e exclusão correspondentes sejam gerados automaticamente. Em seguida, nós vamos atualizar o SelectCommand para que use um JOIN que retorne colunas adicionais de tabelas relacionadas. Por fim, criaremos uma classe de Camada de Lógica De Negócios correspondente e demonstraremos o uso do TableAdapter em uma página da Web ASP.NET.

Etapa 1: Criar o TableAdapter usando uma consulta principal simplificada

Para este tutorial, adicionaremos um TableAdapter e um DataTable fortemente tipado para a Employees tabela no NorthwindWithSprocs DataSet. A tabela Employees contém um campo ReportsTo que especifica o EmployeeID do gerente do funcionário. Por exemplo, a funcionária Anne Dodsworth tem um ReportTo valor de 5, que é o EmployeeID de Steven Buchanan. Consequentemente, Anne se reporta a Steven, seu empresário. Além de apresentar o valor de ReportsTo de cada funcionário, também podemos querer obter o nome do gerente. Isso pode ser feito usando um JOIN. Mas usar um JOIN ao criar inicialmente o TableAdapter impede que o assistente gere automaticamente os recursos correspondentes de inserção, atualização e exclusão. Portanto, começaremos criando um TableAdapter cuja consulta principal não contém s JOIN . Em seguida, na Etapa 2, atualizaremos o procedimento armazenado de consulta principal para recuperar o nome do gerente por meio de um JOIN.

Comece abrindo o NorthwindWithSprocs DataSet na ~/App_Code/DAL pasta. Clique com o botão direito do mouse no Designer, selecione a opção Adicionar no menu de contexto e escolha o item de menu TableAdapter. Isso iniciará o assistente de configuração do TableAdapter. Como a Figura 5 descreve, peça ao assistente para criar novos procedimentos armazenados e clique em Avançar. Para uma revisão sobre como criar novos procedimentos armazenados a partir do assistente do TableAdapter, consulte o tutorial Criando Novos Procedimentos Armazenados para TableAdapters do Conjunto de Dados Tipado.

Selecione a opção Criar novos procedimentos armazenados

Figura 5: Selecione a opção Criar novos procedimentos armazenados (clique para exibir a imagem em tamanho real)

Use a seguinte SELECT instrução para a consulta principal do TableAdapter:

SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country
FROM Employees

Como essa consulta não inclui nenhum JOIN, o assistente TableAdapter criará automaticamente procedimentos armazenados com instruções correspondentes INSERT, UPDATE, e DELETE, bem como um procedimento armazenado para executar a consulta principal.

A etapa a seguir nos permite nomear os procedimentos armazenados do TableAdapter. Use os nomes Employees_Select, Employees_Inserte Employees_Update, Employees_Deleteconforme mostrado na Figura 6.

Nomear os procedimentos armazenados do TableAdapter

Figura 6: Nomeie os Procedimentos Armazenados do TableAdapter (clique para exibir a imagem em tamanho real)

A etapa final nos solicita o nome dos métodos de TableAdapter. Use Fill e GetEmployees como os nomes dos métodos. Além disso, mantenha marcada a caixa de seleção Criar métodos para enviar atualizações diretamente para o banco de dados (GenerateDBDirectMethods).

Nomeie os métodos Fill e GetEmployees do TableAdapter

Figura 7: Nomeie os métodos do TableAdapter Fill e GetEmployees (Clique para exibir a imagem em tamanho real)

Depois de concluir o processo assistido, tire um momento para examinar os procedimentos armazenados no banco de dados. Você deve ver quatro novos: Employees_Select, Employees_Insert, Employees_Update e Employees_Delete. Em seguida, inspecione o EmployeesDataTable e o EmployeesTableAdapter que foram criados. A DataTable contém uma coluna para cada campo retornado pela consulta principal. Clique no TableAdapter e vá para a janela Propriedades. Lá, você verá que as propriedades InsertCommand, UpdateCommand e DeleteCommand estão configuradas corretamente para chamar os procedimentos armazenados correspondentes.

O TableAdapter inclui recursos de inserção, atualização e exclusão

Figura 8: o TableAdapter inclui recursos de inserção, atualização e exclusão (clique para exibir a imagem em tamanho real)

Com os procedimentos armazenados de inserção, atualização e exclusão criados automaticamente e as propriedades InsertCommand, UpdateCommand e DeleteCommand configuradas corretamente, estamos prontos para personalizar os procedimentos armazenados SelectCommand para retornar informações adicionais sobre o gerente de cada funcionário. Especificamente, precisamos atualizar o procedimento armazenado Employees_Select para usar um JOIN e retornar os valores de FirstName e LastName do gerente. Depois que o procedimento armazenado for atualizado, precisaremos atualizar o DataTable para que ele inclua essas colunas adicionais. Abordaremos essas duas tarefas nas etapas 2 e 3.

Etapa 2: Personalizando o procedimento armazenado para incluir umJOIN

Comece acessando o Gerenciador de Servidores, detalhando a pasta Procedimentos Armazenados do banco de dados Northwind e abrindo o Employees_Select procedimento armazenado. Se você não vir esse procedimento armazenado, clique com o botão direito do mouse na pasta Procedimentos Armazenados e escolha Atualizar. Atualize o procedimento armazenado para que ele use um LEFT JOIN para retornar o nome e o sobrenome do gerente:

SELECT Employees.EmployeeID, Employees.LastName, 
       Employees.FirstName, Employees.Title, 
       Employees.HireDate, Employees.ReportsTo, 
       Employees.Country,
       Manager.FirstName as ManagerFirstName, 
       Manager.LastName as ManagerLastName
FROM Employees
    LEFT JOIN Employees AS Manager ON
        Employees.ReportsTo = Manager.EmployeeID

Depois de atualizar a SELECT instrução, salve as alterações acessando o menu Arquivo e escolhendo Salvar Employees_Select. Como alternativa, você pode clicar no ícone Salvar na barra de ferramentas ou clicar em Ctrl+S. Depois de salvar suas alterações, clique com o botão direito do Employees_Select mouse no procedimento armazenado no Gerenciador de Servidores e escolha Executar. Isso executará o procedimento armazenado e mostrará seus resultados na janela Saída (consulte a Figura 9).

Os resultados dos procedimentos armazenados são exibidos na janela de saída

Figura 9: Os resultados dos procedimentos armazenados são exibidos na janela de saída (clique para exibir a imagem em tamanho real)

Etapa 3: Atualizando as colunas da DataTable

Neste ponto, o Employees_Select procedimento armazenado retorna ManagerFirstName e ManagerLastName valores, mas estas EmployeesDataTable colunas estão ausentes. Essas colunas ausentes podem ser adicionadas ao DataTable de duas maneiras:

  • Manualmente - clique com o botão direito do mouse no DataTable no Designer de Conjunto de Dados e, no menu Adicionar, escolha Coluna. Em seguida, você pode nomear a coluna e definir suas propriedades adequadamente.
  • Automaticamente - o assistente de Configuração de TableAdapter atualizará as colunas do DataTable para refletir os campos retornados pelo SelectCommand procedimento armazenado. Ao usar instruções SQL ad hoc, o assistente também removerá as propriedades InsertCommand, UpdateCommand e DeleteCommand, já que agora SelectCommand contém um JOIN. Mas ao usar procedimentos armazenados, essas propriedades de comando permanecem intactas.

Exploramos a adição manual de colunas DataTable em tutoriais anteriores, incluindo Mestre/Detalhe usando uma lista com marcadores de registros mestres com uma Lista de Dados de Detalhes e Arquivos de Carregamento, e examinaremos esse processo novamente em mais detalhes em nosso próximo tutorial. Para este tutorial, no entanto, vamos usar a abordagem automática por meio do assistente de Configuração do TableAdapter.

Comece clicando com o botão direito do EmployeesTableAdapter mouse e selecionando Configurar no menu de contexto. Isso apresenta o Assistente de Configuração do TableAdapter, que lista os procedimentos armazenados usados para selecionar, inserir, atualizar e excluir, juntamente com seus valores de retorno e parâmetros (se houver). A Figura 10 mostra este assistente. Aqui, podemos ver que o Employees_Select procedimento armazenado agora retorna os campos ManagerFirstName e ManagerLastName.

O Assistente mostra a lista de colunas atualizada para o procedimento armazenado Employees_Select

Figura 10: O assistente mostra a lista de colunas atualizada para o Employees_Select procedimento armazenado (clique para exibir a imagem em tamanho real)

Conclua o assistente clicando em Concluir. Ao retornar ao Designer de Conjunto de Dados, o EmployeesDataTable inclui duas colunas adicionais: ManagerFirstName e ManagerLastName.

O EmployeesDataTable contém duas novas colunas

Figura 11: O EmployeesDataTable contém duas novas colunas (clique para exibir a imagem em tamanho real)

Para ilustrar que o procedimento armazenado atualizado Employees_Select está em vigor e que os recursos de inserção, atualização e exclusão do TableAdapter ainda estão funcionais, vamos criar uma página da Web que permite aos usuários exibir e excluir funcionários. No entanto, antes de criarmos essa página, precisamos primeiro criar uma nova classe na Camada lógica de negócios para trabalhar com funcionários do NorthwindWithSprocs DataSet. Na Etapa 4, criaremos uma EmployeesBLLWithSprocs classe. Na Etapa 5, usaremos essa classe de uma página ASP.NET.

Etapa 4: Implementando a camada lógica de negócios

Crie um novo arquivo de classe na ~/App_Code/BLL pasta chamada EmployeesBLLWithSprocs.cs. Essa classe imita a semântica da classe existente EmployeesBLL , apenas essa nova fornece menos métodos e usa o NorthwindWithSprocs DataSet (em vez do Northwind DataSet). Adicione o código a seguir à classe EmployeesBLLWithSprocs .

using System;
using System.Data;
using System.Configuration;
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 NorthwindWithSprocsTableAdapters;
[System.ComponentModel.DataObject]
public class EmployeesBLLWithSprocs
{
    private EmployeesTableAdapter _employeesAdapter = null;
    protected EmployeesTableAdapter Adapter
    {
        get
        {
            if (_employeesAdapter == null)
                _employeesAdapter = new EmployeesTableAdapter();
            return _employeesAdapter;
        }
    }
    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, true)]
    public NorthwindWithSprocs.EmployeesDataTable GetEmployees()
    {
        return Adapter.GetEmployees();
    }
    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Delete, true)]
    public bool DeleteEmployee(int employeeID)
    {
        int rowsAffected = Adapter.Delete(employeeID);
        // Return true if precisely one row was deleted, otherwise false
        return rowsAffected == 1;
    }
}

A EmployeesBLLWithSprocs propriedade da Adapter classe retorna uma instância dos NorthwindWithSprocs Conjuntos de Dados.EmployeesTableAdapter Isso é usado pelas classes GetEmployees e DeleteEmployee métodos. O método GetEmployees chama o método correspondente EmployeesTableAdapterGetEmployees, que invoca o procedimento armazenado Employees_Select e preenche seus resultados em um EmployeeDataTable. O DeleteEmployee método da mesma forma chama o método EmployeesTableAdapter s Delete, que invoca o Employees_Delete procedimento armazenado.

Etapa 5: Trabalhando com os dados na camada de apresentação

Com a EmployeesBLLWithSprocs classe concluída, estamos prontos para trabalhar com dados de funcionários por meio de uma página ASP.NET. Abra a página JOINs.aspx na pasta AdvancedDAL e arraste um GridView da Caixa de Ferramentas para o Designer, definindo sua propriedade ID como Employees. Em seguida, no smart tag do GridView, vincule o grid a um novo controle ObjectDataSource chamado EmployeesDataSource.

Configure o ObjectDataSource para usar a classe EmployeesBLLWithSprocs e, nas guias SELECT e DELETE, verifique se os métodos GetEmployees e DeleteEmployee estão selecionados nas listas suspensas. Clique em Concluir para concluir a configuração do ObjectDataSource.

Configurar o ObjectDataSource para usar a classe EmployeesBLLWithSprocs

Figura 12: Configurar o ObjectDataSource para usar a EmployeesBLLWithSprocs classe (clique para exibir a imagem em tamanho real)

Fazer com que o ObjectDataSource use os métodos GetEmployees e DeleteEmployee

Figura 13: Fazer com que o ObjectDataSource use os GetEmployees métodos e DeleteEmployee (clique para exibir a imagem em tamanho real)

O Visual Studio adicionará um BoundField ao GridView para cada uma das EmployeesDataTable colunas s. Remova todos esses BoundFields, exceto por Title, LastName, FirstName, ManagerFirstName e ManagerLastName e renomeie as propriedades HeaderText dos últimos quatro BoundFields para Sobrenome, Primeiro Nome, Nome do Gerente e Sobrenome do Gerente, respectivamente.

Para permitir que os usuários excluam funcionários desta página, precisamos fazer duas coisas. Primeiro, instrua o GridView a fornecer recursos de exclusão marcando a opção Habilitar Exclusão em sua tag inteligente. Em segundo lugar, altere a propriedade OldValuesParameterFormatString do ObjectDataSource do valor definido pelo assistente ObjectDataSource (original_{0}) para seu valor padrão ({0}). Depois de fazer essas alterações, a marcação declarativa de GridView e ObjectDataSource deve ser semelhante à seguinte:

<asp:GridView ID="Employees" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="EmployeeID" DataSourceID="EmployeesDataSource">
    <Columns>
        <asp:CommandField ShowDeleteButton="True" />
        <asp:BoundField DataField="Title" 
            HeaderText="Title" 
            SortExpression="Title" />
        <asp:BoundField DataField="LastName" 
            HeaderText="Last Name" 
            SortExpression="LastName" />
        <asp:BoundField DataField="FirstName" 
            HeaderText="First Name" 
            SortExpression="FirstName" />
        <asp:BoundField DataField="ManagerFirstName" 
            HeaderText="Manager's First Name" 
            SortExpression="ManagerFirstName" />
        <asp:BoundField DataField="ManagerLastName" 
            HeaderText="Manager's Last Name" 
            SortExpression="ManagerLastName" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="EmployeesDataSource" runat="server" 
    DeleteMethod="DeleteEmployee" OldValuesParameterFormatString="{0}" 
    SelectMethod="GetEmployees" TypeName="EmployeesBLLWithSprocs">
    <DeleteParameters>
        <asp:Parameter Name="employeeID" Type="Int32" />
    </DeleteParameters>
</asp:ObjectDataSource>

Teste a página visitando-a por meio de um navegador. Como mostra a Figura 14, a página listará cada funcionário e seu nome de gerente (supondo que eles tenham um).

O JOIN na procedura armazenada Employees_Select retorna o nome do gerente

Figura 14: O JOIN no procedimento armazenado retorna o nome do gerente Employees_Select (clique para exibir a imagem em tamanho real)

Clicar no botão Excluir inicia o fluxo de trabalho de exclusão, que culmina na execução do Employees_Delete procedimento armazenado. No entanto, a tentativa de instrução DELETE no procedimento armazenado falha devido a uma violação de restrição de chave estrangeira (consulte a Figura 15). Especificamente, cada funcionário tem um ou mais registros na Orders tabela, fazendo com que a exclusão falhe.

Excluir um funcionário que tenha pedidos correspondentes resulta em uma violação de restrição de chave estrangeira

Figura 15: Excluir um funcionário que possui ordens correspondentes resulta em violação de restrição de chave estrangeira (Clique para visualizar imagem em tamanho real)

Para permitir que um funcionário seja excluído, você pode:

Eu deixo isso como um exercício para o leitor.

Resumo

Ao trabalhar com bancos de dados relacionais, é comum que as consultas extraam seus dados de várias tabelas relacionadas. Subconsultas correlacionadas e JOIN s fornecem duas técnicas diferentes para acessar dados de tabelas relacionadas em uma consulta. Em tutoriais anteriores, geralmente usamos subconsultas correlacionadas porque o TableAdapter não pode gerar automaticamente as instruções INSERT, UPDATE e DELETE para consultas envolvendo JOINs. Embora esses valores possam ser fornecidos manualmente, ao usar instruções SQL ad hoc, todas as personalizações serão sobrescritas quando o assistente de Configuração do TableAdapter for concluído.

Felizmente, os TableAdapters criados usando procedimentos armazenados não sofrem da mesma fragilidade que aqueles criados com instruções SQL ad hoc. Portanto, é viável criar um TableAdapter cuja consulta principal usa um JOIN ao usar procedimentos armazenados. Neste tutorial, vimos como criar um TableAdapter desse tipo. Começamos usando uma consulta sem JOINSELECT para a consulta principal do TableAdapter, para que os procedimentos armazenados correspondentes de inserção, atualização e exclusão fossem criados automaticamente. Com a configuração inicial do TableAdapter concluída, aprimoramos o SelectCommand procedimento armazenado para usar um JOIN e executar novamente o assistente de Configuração do TableAdapter para atualizar as colunas EmployeesDataTable.

Executar novamente o assistente de Configuração tableAdapter atualizou automaticamente as EmployeesDataTable colunas para refletir os campos de dados retornados pelo Employees_Select procedimento armazenado. Como alternativa, poderíamos ter adicionado essas colunas manualmente à DataTable. Exploraremos manualmente a adição de colunas ao DataTable no próximo tutorial.

Divirta-se programando!

Sobre o autor

Scott Mitchell, autor de sete livros asp/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 Horas. Ele pode ser alcançado em mitchell@4GuysFromRolla.com.

Agradecimentos Especiais a

Esta série de tutoriais foi revisada por muitos revisores úteis. Os principais revisores deste tutorial foram Hilton Geisenow, David Suru e Teresa Murphy. Interessado em revisar meus próximos artigos do MSDN? Se assim for, deixe-me uma linha em mitchell@4GuysFromRolla.com.