Uso de procedimentos armazenados existentes para os TableAdapters do conjunto de dados tipado (C#)
por Scott Mitchell
No tutorial anterior, aprendemos a usar o Assistente tableAdapter para gerar novos procedimentos armazenados. Neste tutorial, aprendemos como o mesmo Assistente tableAdapter pode funcionar com procedimentos armazenados existentes. Também aprendemos a adicionar manualmente novos procedimentos armazenados ao nosso banco de dados.
Introdução
No tutorial anterior , vimos como os TableAdapters do Conjunto de Dados Tipados poderiam ser configurados para usar procedimentos armazenados para acessar dados em vez de instruções SQL ad hoc. Em particular, examinamos como fazer com que o assistente TableAdapter crie automaticamente esses procedimentos armazenados. Ao portar um aplicativo herdado para ASP.NET 2.0 ou ao criar um site ASP.NET 2.0 em torno de um modelo de dados existente, é provável que o banco de dados já contenha os procedimentos armazenados necessários. Como alternativa, você pode preferir criar seus procedimentos armazenados manualmente ou por meio de alguma ferramenta diferente do assistente TableAdapter que gera automaticamente seus procedimentos armazenados.
Neste tutorial, veremos como configurar o TableAdapter para usar procedimentos armazenados existentes. Como o banco de dados Northwind tem apenas um pequeno conjunto de procedimentos armazenados internos, também examinaremos as etapas necessárias para adicionar manualmente novos procedimentos armazenados ao banco de dados por meio do ambiente do Visual Studio. Vamos começar!
Observação
No tutorial Encapsulando modificações de banco de dados em uma transação , adicionamos métodos ao TableAdapter para dar suporte a transações (BeginTransaction
, CommitTransaction
e assim por diante). Como alternativa, as transações podem ser totalmente gerenciadas dentro de um procedimento armazenado, o que não requer modificações no código da Camada de Acesso a Dados. Neste tutorial, exploraremos os comandos T-SQL usados para executar instruções de um procedimento armazenado dentro do escopo de uma transação.
Etapa 1: Adicionar procedimentos armazenados ao banco de dados Northwind
O Visual Studio facilita a adição de novos procedimentos armazenados a um banco de dados. Vamos adicionar um novo procedimento armazenado ao banco de dados Northwind que retorna todas as colunas da Products
tabela para aqueles que têm um valor específico CategoryID
. Na janela Servidor Explorer, expanda o banco de dados Northwind para que suas pastas - Diagramas de Banco de Dados, Tabelas, Exibições e assim por diante - sejam exibidas. Como vimos no tutorial anterior, a pasta Procedimentos Armazenados contém os procedimentos armazenados existentes do banco de dados. Para adicionar um novo procedimento armazenado, basta clicar com o botão direito do mouse na pasta Procedimentos Armazenados e escolher a opção Adicionar Novo Procedimento Armazenado no menu de contexto.
Figura 1: Right-Click a pasta Procedimentos Armazenados e Adicionar um Novo Procedimento Armazenado (Clique para exibir a imagem em tamanho real)
Como mostra a Figura 1, selecionar a opção Adicionar Novo Procedimento Armazenado abre uma janela de script no Visual Studio com a estrutura de tópicos do script SQL necessária para criar o procedimento armazenado. É nosso trabalho elaborar esse script e executá-lo, momento em que o procedimento armazenado será adicionado ao banco de dados.
Insira o seguinte script:
CREATE PROCEDURE dbo.Products_SelectByCategoryID
(
@CategoryID int
)
AS
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued
FROM Products
WHERE CategoryID = @CategoryID
Esse script, quando executado, adicionará um novo procedimento armazenado ao banco de dados Northwind chamado Products_SelectByCategoryID
. Esse procedimento armazenado aceita um único parâmetro de entrada (@CategoryID
, do tipo int
) e retorna todos os campos para esses produtos com um valor correspondente CategoryID
.
Para executar esse CREATE PROCEDURE
script e adicionar o procedimento armazenado ao banco de dados, clique no ícone Salvar na barra de ferramentas ou pressione Ctrl+S. Depois de fazer isso, a pasta Procedimentos Armazenados é atualizada, mostrando o procedimento armazenado recém-criado. Além disso, o script na janela mudará a sutileza de CREATE PROCEDURE dbo.Products_SelectProductByCategoryID
para ALTER PROCEDURE
dbo.Products_SelectProductByCategoryID
. CREATE PROCEDURE
adiciona um novo procedimento armazenado ao banco de dados, enquanto ALTER PROCEDURE
atualiza um existente. Como o início do script foi alterado para ALTER PROCEDURE
, alterar os parâmetros de entrada de procedimentos armazenados ou instruções SQL e clicar no ícone Salvar atualizará o procedimento armazenado com essas alterações.
A Figura 2 mostra o Visual Studio depois que o Products_SelectByCategoryID
procedimento armazenado é salvo.
Figura 2: O procedimento Products_SelectByCategoryID
armazenado foi adicionado ao banco de dados (clique para exibir a imagem em tamanho real)
Etapa 2: Configurando o TableAdapter para usar um procedimento armazenado existente
Agora que o Products_SelectByCategoryID
procedimento armazenado foi adicionado ao banco de dados, podemos configurar nossa Camada de Acesso a Dados para usar esse procedimento armazenado quando um de seus métodos for invocado. Em particular, adicionaremos um GetProductsByCategoryID(categoryID)
método ao ProductsTableAdapter
no NorthwindWithSprocs
Conjunto de Dados Tipado que chama o Products_SelectByCategoryID
procedimento armazenado que acabamos de criar.
Comece abrindo o NorthwindWithSprocs
DataSet. Clique com o botão direito do ProductsTableAdapter
mouse no e escolha Adicionar Consulta para iniciar o assistente de Configuração de Consulta tableAdapter. No tutorial anterior , optamos por fazer com que o TableAdapter criasse um novo procedimento armazenado para nós. No entanto, para este tutorial, queremos conectar o novo método TableAdapter ao procedimento armazenado existente Products_SelectByCategoryID
. Portanto, escolha a opção Usar procedimento armazenado existente na primeira etapa do assistente e clique em Avançar.
Figura 3: Escolha a opção Usar procedimento armazenado existente (clique para exibir a imagem em tamanho real)
A tela a seguir fornece uma lista suspensa preenchida com os procedimentos armazenados do banco de dados. Selecionar um procedimento armazenado lista seus parâmetros de entrada à esquerda e os campos de dados retornados (se houver) à direita. Escolha o Products_SelectByCategoryID
procedimento armazenado na lista e clique em Avançar.
Figura 4: Escolher o Products_SelectByCategoryID
procedimento armazenado (clique para exibir a imagem em tamanho real)
A próxima tela nos pergunta que tipo de dados são retornados pelo procedimento armazenado e nossa resposta aqui determina o tipo retornado pelo método TableAdapter. Por exemplo, se indicarmos que os dados tabulares são retornados, o método retornará uma ProductsDataTable
instância preenchida com os registros retornados pelo procedimento armazenado. Por outro lado, se indicarmos que esse procedimento armazenado retorna um único valor, o TableAdapter retornará um object
que é atribuído ao valor na primeira coluna do primeiro registro retornado pelo procedimento armazenado.
Como o Products_SelectByCategoryID
procedimento armazenado retorna todos os produtos que pertencem a uma categoria específica, escolha a primeira resposta – Dados tabulares – e clique em Avançar.
Figura 5: Indicar que o procedimento armazenado retorna dados tabulares (clique para exibir a imagem em tamanho real)
Tudo o que resta é indicar quais padrões de método usar seguidos pelos nomes desses métodos. Deixe as opções Preencher uma DataTable e Retornar uma DataTable marcadas, mas renomeie os métodos como FillByCategoryID
e GetProductsByCategoryID
. Em seguida, clique em Avançar para examinar um resumo das tarefas que o assistente executará. Se tudo estiver correto, clique em Concluir.
Figura 6: Nomear os métodos FillByCategoryID
e GetProductsByCategoryID
(clique para exibir a imagem em tamanho real)
Observação
Os métodos TableAdapter que acabamos de criar e FillByCategoryID
GetProductsByCategoryID
, esperam um parâmetro de entrada do tipo int
. Esse valor de parâmetro de entrada é passado para o procedimento armazenado por meio de seu @CategoryID
parâmetro . Se você modificar os Products_SelectByCategory
parâmetros do procedimento armazenado, também precisará atualizar os parâmetros para esses métodos TableAdapter. Conforme discutido no tutorial anterior, isso pode ser feito de duas maneiras: adicionando ou removendo manualmente parâmetros da coleção de parâmetros ou executando novamente o assistente TableAdapter.
Etapa 3: Adicionar umGetProductsByCategoryID(categoryID)
método à BLL
Com o GetProductsByCategoryID
método DAL concluído, a próxima etapa é fornecer acesso a esse método na Camada de Lógica de Negócios. Abra o ProductsBLLWithSprocs
arquivo de classe e adicione o seguinte método:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public NorthwindWithSprocs.ProductsDataTable GetProductByCategoryID(int categoryID)
{
return Adapter.GetProductsByCategoryID(categoryID);
}
Esse método BLL simplesmente retorna o ProductsDataTable
retornado do ProductsTableAdapter
método s GetProductsByCategoryID
. O DataObjectMethodAttribute
atributo fornece metadados usados pelo assistente Configurar Fonte de Dados do ObjectDataSource. Em particular, esse método aparecerá na lista suspensa da guia SELECT.
Etapa 4: Exibindo produtos por categoria
Para testar o procedimento armazenado recém-adicionado Products_SelectByCategoryID
e os métodos DAL e BLL correspondentes, vamos criar uma página ASP.NET que contém um DropDownList e um GridView. O DropDownList listará todas as categorias no banco de dados enquanto o GridView exibirá os produtos pertencentes à categoria selecionada.
Observação
Criamos interfaces de master/detalhes usando DropDownLists em tutoriais anteriores. Para obter uma visão mais detalhada da implementação de um relatório de master/detalhes, consulte o tutorial Filtragem mestre/detalhe com um DropDownList.
Abra a ExistingSprocs.aspx
página na AdvancedDAL
pasta e arraste um DropDownList da Caixa de Ferramentas para o Designer. Defina a propriedade dropDownList como ID
e sua AutoPostBack
propriedade como true
.Categories
Em seguida, de sua marca inteligente, associe DropDownList a um novo ObjectDataSource chamado CategoriesDataSource
. Configure o ObjectDataSource para que ele recupere seus dados do método s GetCategories
da CategoriesBLL
classe. Defina as listas suspensas nas guias UPDATE, INSERT e DELETE como (Nenhum).
Figura 7: Recuperar dados do CategoriesBLL
método da classe (GetCategories
clique para exibir a imagem em tamanho real)
Figura 8: Definir o Drop-Down Listas nas guias UPDATE, INSERT e DELETE como (Nenhum) (Clique para exibir a imagem em tamanho real)
Depois de concluir o assistente ObjectDataSource, configure o DropDownList para exibir o CategoryName
campo de dados e usar o CategoryID
campo como o Value
para cada ListItem
.
Neste ponto, a marcação declarativa de DropDownList e ObjectDataSource deve ser semelhante à seguinte:
<asp:DropDownList ID="Categories" runat="server" AutoPostBack="True"
DataSourceID="CategoriesDataSource" DataTextField="CategoryName"
DataValueField="CategoryID">
</asp:DropDownList>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>
Em seguida, arraste um GridView para o Designer, colocando-o abaixo do DropDownList. Defina o GridView s ID
como ProductsByCategory
e, de sua marca inteligente, associe-o a um novo ObjectDataSource chamado ProductsByCategoryDataSource
. Configure o ProductsByCategoryDataSource
ObjectDataSource para usar a ProductsBLLWithSprocs
classe , fazendo com que ele recupere seus dados usando o GetProductsByCategoryID(categoryID)
método . Como esse GridView só será usado para exibir dados, defina as listas suspensas nas guias UPDATE, INSERT e DELETE como (Nenhum) e clique em Avançar.
Figura 9: Configurar o ObjectDataSource para usar a ProductsBLLWithSprocs
classe (clique para exibir a imagem em tamanho real)
Figura 10: Recuperar dados do GetProductsByCategoryID(categoryID)
método (clique para exibir a imagem em tamanho real)
O método escolhido na guia SELECT espera um parâmetro, portanto, a etapa final do assistente nos solicita a origem do parâmetro. Defina a lista suspensa Origem do parâmetro como Controle e escolha o Categories
controle na lista suspensa ControlID. Clique em Concluir para concluir o assistente.
Figura 11: Usar DropDownList Categories
como a origem do categoryID
parâmetro (clique para exibir a imagem em tamanho real)
Ao concluir o assistente ObjectDataSource, o Visual Studio adicionará BoundFields e um CheckBoxField para cada um dos campos de dados do produto. Fique à vontade para personalizar esses campos conforme desejar.
Visite a página por meio de um navegador. Ao visitar a página, a categoria Bebidas é selecionada e os produtos correspondentes listados na grade. Alterar a lista suspensa para uma categoria alternativa, como mostra a Figura 12, causa um postback e recarrega a grade com os produtos da categoria recém-selecionada.
Figura 12: Os produtos na categoria Produzir são exibidos (clique para exibir a imagem em tamanho real)
Etapa 5: Encapsulando instruções de um procedimento armazenado dentro do escopo de uma transação
No tutorial Encapsulando modificações de banco de dados em uma transação , discutimos técnicas para executar uma série de instruções de modificação de banco de dados dentro do escopo de uma transação. Lembre-se de que as modificações realizadas sob o guarda-chuva de uma transação são bem-sucedidas ou todas falham, garantindo a atomicidade. As técnicas para usar transações incluem:
- Usando as classes no
System.Transactions
namespace , - Fazer com que a Camada de Acesso a Dados use ADO.NET classes como
SqlTransaction
, e - Adicionando os comandos de transação T-SQL diretamente dentro do procedimento armazenado
O tutorial Encapsulando modificações de banco de dados em uma transação usou as classes ADO.NET no DAL. O restante deste tutorial examina como gerenciar uma transação usando comandos T-SQL de dentro de um procedimento armazenado.
Os três principais comandos SQL para iniciar, confirmar e reverter manualmente uma transação são BEGIN TRANSACTION
, COMMIT TRANSACTION
e ROLLBACK TRANSACTION
, respectivamente. Assim como acontece com a abordagem ADO.NET, ao usar transações de dentro de um procedimento armazenado, precisamos aplicar o seguinte padrão:
- Indique o início de uma transação.
- Execute as instruções SQL que compõem a transação.
- Se houver um erro em qualquer uma das instruções da Etapa 2, reverta a transação
- Se todas as instruções da Etapa 2 forem concluídas sem erro, confirme a transação.
Esse padrão pode ser implementado na sintaxe T-SQL usando o seguinte modelo:
BEGIN TRY
BEGIN TRANSACTION -- Start the transaction
... Perform the SQL statements that makeup the transaction ...
-- If we reach here, success!
COMMIT TRANSACTION
END TRY
BEGIN CATCH
-- Whoops, there was an error
ROLLBACK TRANSACTION
-- Raise an error with the
-- details of the exception
DECLARE @ErrMsg nvarchar(4000),
@ErrSeverity int
SELECT @ErrMsg = ERROR_MESSAGE(),
@ErrSeverity = ERROR_SEVERITY()
RAISERROR(@ErrMsg, @ErrSeverity, 1)
END CATCH
O modelo começa definindo um TRY...CATCH
bloco, um constructo novo para SQL Server 2005. Assim como acontece com try...catch
blocos em C#, o bloco SQL TRY...CATCH
executa as instruções no TRY
bloco. Se qualquer instrução gerar um erro, o controle será transferido imediatamente para o CATCH
bloco.
Se não houver erros ao executar as instruções SQL que compensam a transação, a COMMIT TRANSACTION
instrução confirmará as alterações e concluirá a transação. Se, no entanto, uma das instruções resultar em um erro, o ROLLBACK TRANSACTION
no bloco retornará CATCH
o banco de dados ao seu estado antes do início da transação. O procedimento armazenado também gera um erro usando o comando RAISERROR, o que faz com que um SqlException
seja gerado no aplicativo.
Observação
Como o TRY...CATCH
bloco é novo no SQL Server 2005, o modelo acima não funcionará se você estiver usando versões mais antigas do Microsoft SQL Server.
Vamos examinar um exemplo concreto. Existe uma restrição de chave estrangeira entre as Categories
tabelas e Products
, o que significa que cada CategoryID
campo na Products
tabela deve ser mapeado para um CategoryID
valor na Categories
tabela. Qualquer ação que viole essa restrição, como tentar excluir uma categoria que tenha produtos associados, resultará em uma violação de restrição de chave estrangeira. Para verificar isso, reveja o exemplo Atualizando e excluindo dados binários existentes na seção Trabalhando com dados binários (~/BinaryData/UpdatingAndDeleting.aspx
). Esta página lista cada categoria no sistema juntamente com os botões Editar e Excluir (consulte a Figura 13), mas se você tentar excluir uma categoria que tenha produtos associados - como Bebidas - a exclusão falhará devido a uma violação de restrição de chave estrangeira (consulte a Figura 14).
Figura 13: Cada categoria é exibida em um GridView com botões Editar e Excluir (clique para exibir a imagem em tamanho real)
Figura 14: Não é possível excluir uma categoria que tenha produtos existentes (clique para exibir a imagem em tamanho real)
Imagine, porém, que queremos permitir que as categorias sejam excluídas, independentemente de terem produtos associados. Se uma categoria com produtos for excluída, imagine que também queremos excluir seus produtos existentes (embora outra opção seja simplesmente definir seus valores de produtos CategoryID
como NULL
). Essa funcionalidade pode ser implementada por meio das regras em cascata da restrição de chave estrangeira. Como alternativa, poderíamos criar um procedimento armazenado que aceita um @CategoryID
parâmetro de entrada e, quando invocado, exclui explicitamente todos os produtos associados e, em seguida, a categoria especificada.
Nossa primeira tentativa em tal procedimento armazenado pode ser semelhante à seguinte:
CREATE PROCEDURE dbo.Categories_Delete
(
@CategoryID int
)
AS
-- First, delete the associated products...
DELETE FROM Products
WHERE CategoryID = @CategoryID
-- Now delete the category
DELETE FROM Categories
WHERE CategoryID = @CategoryID
Embora isso definitivamente exclua os produtos e a categoria associados, ele não o faz sob o guarda-chuva de uma transação. Imagine que há alguma outra restrição de chave estrangeira em Categories
que proibiria a exclusão de um valor específico @CategoryID
. O problema é que, nesse caso, todos os produtos serão excluídos antes de tentarmos excluir a categoria. O resultado líquido é que, para tal categoria, esse procedimento armazenado removeria todos os seus produtos enquanto a categoria permanecia, pois ainda tem registros relacionados em alguma outra tabela.
Se o procedimento armazenado fosse encapsulado dentro do escopo de uma transação, no entanto, as exclusões para a Products
tabela seriam revertidas em face de uma exclusão com falha em Categories
. O script de procedimento armazenado a seguir usa uma transação para garantir a atomicidade entre as duas DELETE
instruções:
CREATE PROCEDURE dbo.Categories_Delete
(
@CategoryID int
)
AS
BEGIN TRY
BEGIN TRANSACTION -- Start the transaction
-- First, delete the associated products...
DELETE FROM Products
WHERE CategoryID = @CategoryID
-- Now delete the category
DELETE FROM Categories
WHERE CategoryID = @CategoryID
-- If we reach here, success!
COMMIT TRANSACTION
END TRY
BEGIN CATCH
-- Whoops, there was an error
ROLLBACK TRANSACTION
-- Raise an error with the
-- details of the exception
DECLARE @ErrMsg nvarchar(4000),
@ErrSeverity int
SELECT @ErrMsg = ERROR_MESSAGE(),
@ErrSeverity = ERROR_SEVERITY()
RAISERROR(@ErrMsg, @ErrSeverity, 1)
END CATCH
Reserve um momento para adicionar o Categories_Delete
procedimento armazenado ao banco de dados Northwind. Consulte a Etapa 1 para obter instruções sobre como adicionar procedimentos armazenados a um banco de dados.
Etapa 6: Atualizando oCategoriesTableAdapter
Embora tenhamos adicionado o Categories_Delete
procedimento armazenado ao banco de dados, o DAL está atualmente configurado para usar instruções SQL ad hoc para executar a exclusão. Precisamos atualizar o CategoriesTableAdapter
e instruí-lo a usar o Categories_Delete
procedimento armazenado.
Observação
Anteriormente neste tutorial, estávamos trabalhando com o NorthwindWithSprocs
DataSet. Mas esse DataSet tem apenas uma única entidade, ProductsDataTable
, e precisamos trabalhar com categorias. Portanto, para o restante deste tutorial, quando falo sobre a Camada de Acesso a Dados, estou me referindo ao Northwind
DataSet, aquele que criamos pela primeira vez no tutorial Criando uma camada de acesso a dados .
Abra o Conjunto de Dados northwind, selecione o CategoriesTableAdapter
e vá para o janela Propriedades. O janela Propriedades lista o InsertCommand
, UpdateCommand
, DeleteCommand
e SelectCommand
usados pelo TableAdapter, bem como suas informações de nome e conexão. Expanda a DeleteCommand
propriedade para ver seus detalhes. Como mostra a Figura 15, a DeleteCommand
propriedade s CommandType
é definida como Texto, o que a instrui a enviar o texto na CommandText
propriedade como uma consulta SQL ad hoc.
Figura 15: Selecione o CategoriesTableAdapter
no Designer para exibir suas propriedades na janela Propriedades
Para alterar essas configurações, selecione o texto (DeleteCommand) no janela Propriedades e escolha (Novo) na lista suspensa. Isso limpará as configurações das CommandText
propriedades , CommandType
e Parameters
. Em seguida, defina a CommandType
propriedade StoredProcedure
como e digite o nome do procedimento armazenado para o CommandText
(dbo.Categories_Delete
). Se você não tiver certeza de inserir as propriedades nessa ordem , primeiro o CommandType
e depois o CommandText
Visual Studio preencherá automaticamente a coleção Parameters. Se você não inserir essas propriedades nesta ordem, precisará adicionar manualmente os parâmetros por meio da coleção Parameters Editor. Em ambos os casos, é prudente clicar nas reticências na propriedade Parameters para abrir a coleção Parameters Editor para verificar se as alterações de configurações de parâmetro corretas foram feitas (consulte a Figura 16). Se você não vir nenhum parâmetro na caixa de diálogo, adicione o @CategoryID
parâmetro manualmente (não é necessário adicionar o @RETURN_VALUE
parâmetro).
Figura 16: Verificar se as configurações de parâmetros estão corretas
Depois que o DAL for atualizado, a exclusão de uma categoria excluirá automaticamente todos os seus produtos associados e o fará sob o guarda-chuva de uma transação. Para verificar isso, retorne à página Atualizando e excluindo dados binários existentes e clique no botão Excluir para uma das categorias. Com um único clique do mouse, a categoria e todos os seus produtos associados serão excluídos.
Observação
Antes de testar o Categories_Delete
procedimento armazenado, que excluirá vários produtos junto com a categoria selecionada, talvez seja prudente fazer uma cópia de backup do banco de dados. Se você estiver usando o NORTHWND.MDF
banco de dados no App_Data
, basta fechar o Visual Studio e copiar os arquivos MDF e LDF para App_Data
alguma outra pasta. Depois de testar a funcionalidade, você pode restaurar o banco de dados fechando o Visual Studio e substituindo os arquivos MDF e LDF atuais no App_Data
pelas cópias de backup.
Resumo
Embora o assistente do TableAdapter gere automaticamente procedimentos armazenados para nós, há momentos em que podemos já ter esses procedimentos armazenados criados ou desejar criá-los manualmente ou com outras ferramentas. Para acomodar esses cenários, o TableAdapter também pode ser configurado para apontar para um procedimento armazenado existente. Neste tutorial, analisamos como adicionar manualmente procedimentos armazenados a um banco de dados por meio do ambiente do Visual Studio e como conectar os métodos do TableAdapter a esses procedimentos armazenados. Também examinamos os comandos T-SQL e o padrão de script usados para iniciar, confirmar e reverter transações de dentro de um procedimento armazenado.
Programação feliz!
Sobre o autor
Scott Mitchell, autor de sete livros do 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 contatado em mitchell@4GuysFromRolla.com. ou através de seu blog, que pode ser encontrado em http://ScottOnWriting.NET.
Agradecimentos Especiais
Esta série de tutoriais foi revisada por muitos revisores úteis. Os principais revisores deste tutorial foram Hilton Geisenow, S ren Jacob Lauritsen e Teresa Murphy. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, deixe-me uma linha em mitchell@4GuysFromRolla.com.
Comentários
https://aka.ms/ContentUserFeedback.
Em breve: Ao longo de 2024, eliminaremos os problemas do GitHub como o mecanismo de comentários para conteúdo e o substituiremos por um novo sistema de comentários. Para obter mais informações, consulteEnviar e exibir comentários de