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 o Conjunto de Dados Tipado TableAdapters pode ser configurado 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 o ASP.NET 2.0 ou ao criar um site do ASP.NET 2.0 em torno de um modelo de dados existente, é provável que o banco de dados já contenha os procedimentos armazenados de que precisamos. Como alternativa, talvez você prefira 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 gerenciadas inteiramente em 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: Adicionando 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 Products
colunas da tabela para aquelas que têm um valor específico CategoryID
. Na janela Gerenciador de Servidores, 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 Stored Procedures 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: Clique com o botão direito do mouse na pasta Procedimentos Armazenados e adicione um novo procedimento armazenado (clique para exibir a imagem em tamanho completo)
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ário para criar o procedimento armazenado. É nosso trabalho detalhar 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á 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
, alterando os parâmetros de entrada de procedimentos armazenados ou instruções SQL e clicando 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 foi 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 conjunto de NorthwindWithSprocs
dados tipado ProductsTableAdapter
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 e escolha Adicionar consulta para iniciar o assistente de configuração de consulta do 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. A seleção de 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 procedimento armazenado (clique para exibir a Products_SelectByCategoryID
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 do 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 recebe o 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 dos nomes desses métodos. Deixe as opções Preencher uma DataTable e Retornar uma DataTable marcadas, mas renomeie os métodos para FillByCategoryID
e GetProductsByCategoryID
. Em seguida, clique em Avançar para revisar um resumo das tarefas que o assistente executará. Se tudo estiver correto, clique em Concluir.
Figura 6: Nomeie os métodos FillByCategoryID
e GetProductsByCategoryID
(clique para exibir a imagem em tamanho real)
Observação
Os métodos TableAdapter que acabamos de criar FillByCategoryID
e 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 parâmetros do Products_SelectByCategory
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: Adicionando 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 Lógica de Negócios. Abra o arquivo de ProductsBLLWithSprocs
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 contenha 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 mestre/detalhes usando DropDownLists em tutoriais anteriores. Para obter uma visão mais aprofundada da implementação de um relatório mestre/detalhado, consulte o tutorial Filtragem mestre/detalhada 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
Categories
e sua AutoPostBack
propriedade como true
. Em seguida, em sua marca inteligente, associe o DropDownList a um novo ObjectDataSource chamado CategoriesDataSource
. Configure o ObjectDataSource para que ele recupere seus dados do CategoriesBLL
método da GetCategories
classe. Defina as listas suspensas nas guias UPDATE, INSERT e DELETE como (Nenhum) .
Figura 7: Recuperar dados do método da GetCategories
classe (clique para exibir a CategoriesBLL
imagem em tamanho real)
Figura 8: Defina as listas suspensas 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 DropDownList e ObjectDataSource deve ser semelhante ao 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 como ID
ProductsByCategory
e, em 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 será usado apenas 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 classe (clique para exibir a ProductsBLLWithSprocs
imagem em tamanho completo)
Figura 10: Recuperar dados do método (clique para exibir a GetProductsByCategoryID(categoryID)
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 o Categories
DropDownList como a origem do parâmetro (clique para exibir a categoryID
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. Sinta-se à vontade para personalizar esses campos como achar melhor.
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: encapsular 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 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 classes ADO.NET como
SqlTransaction
, e - Adicionando os comandos de transação T-SQL diretamente no procedimento armazenado
O tutorial Modificações de banco de dados de encapsulamento 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 na 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, uma construção nova no SQL Server 2005. Assim como acontece com try...catch
os blocos em C#, o bloco SQL TRY...CATCH
executa as instruções no TRY
bloco. Se alguma instrução gerar um erro, o controle será imediatamente transferido para o CATCH
bloco.
Se não houver erros ao executar as instruções SQL que compõem 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
bloco in CATCH
retornará o banco de dados ao seu estado anterior ao início da transação. O procedimento armazenado também gera um erro usando o comando RAISERROR, 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.
Vejamos um exemplo concreto. Existe uma restrição de chave estrangeira entre as Categories
tabelas e , Products
o que significa que cada CategoryID
campo na tabela deve ser mapeado Products
para um CategoryID
valor na Categories
tabela. Qualquer ação que viole essa restrição, como tentar excluir uma categoria que tenha produtos associados, resulta em uma violação de restrição de chave estrangeira. Para verificar isso, revisite 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 junto com os botões Edit e Delete (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, podemos criar um procedimento armazenado que aceite um @CategoryID
parâmetro de entrada e, quando invocado, exclua explicitamente todos os produtos associados e, em seguida, a categoria especificada.
Nossa primeira tentativa de 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, não o faz sob a égide de uma transação. Imagine que haja alguma outra restrição de chave estrangeira que Categories
proíba 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 essa categoria, esse procedimento armazenado removeria todos os seus produtos enquanto a categoria permanecesse, pois ainda tem registros relacionados em alguma outra tabela.
No entanto, se o procedimento armazenado fosse encapsulado dentro do escopo de uma transação, as exclusões na Products
tabela seriam revertidas em face de uma exclusão com falha no 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, a DAL está configurada no momento 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
No início deste 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 Northwind DataSet, selecione o CategoriesTableAdapter
e vá para a janela Propriedades. A janela Propriedades lista o InsertCommand
, UpdateCommand
, DeleteCommand
e SelectCommand
usado pelo TableAdapter, bem como seu nome e informações de conexão. Expanda a DeleteCommand
propriedade para ver seus detalhes. Como mostra a Figura 15, a DeleteCommand
propriedade s CommandType
é definida como Text, que a instrui a enviar o texto na CommandText
propriedade como uma consulta SQL ad hoc.
Figura 15: Selecione CategoriesTableAdapter
o no designer para exibir suas propriedades na janela Propriedades
Para alterar essas configurações, selecione o texto (DeleteCommand) na 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 como StoredProcedure
e digite o nome do procedimento armazenado para o CommandText
(dbo.Categories_Delete
). Se você inserir as propriedades nesta ordem - primeiro o CommandType
e depois o CommandText
- o Visual Studio preencherá automaticamente a coleção Parameters. Se você não inserir essas propriedades nesta ordem, terá que adicionar manualmente os parâmetros por meio do Editor de coleção de parâmetros. Em ambos os casos, é prudente clicar nas reticências na propriedade Parameters para abrir o Editor de coleção de parâmetros para verificar se as alterações corretas nas configurações de parâmetro 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: Certifique-se de que as configurações de parâmetros estejam corretas
Depois que a DAL for atualizada, 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 App_Data
MDF e LDF para 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 pelas App_Data
cópias de backup.
Resumo
Embora o assistente do TableAdapter gere automaticamente procedimentos armazenados para nós, há momentos em que talvez já tenhamos esses procedimentos armazenados criados ou queiramos 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, vimos 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 usado para iniciar, confirmar e reverter transações de dentro de um procedimento armazenado.
Boa programação!
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 Hilton Geisenow, S ren Jacob Lauritsen e Teresa Murphy. Interessado em revisar meus próximos artigos do MSDN? Em caso afirmativo, envie-me uma mensagem para mitchell@4GuysFromRolla.com.
Comentários
https://aka.ms/ContentUserFeedback.
Brevemente: Ao longo de 2024, vamos descontinuar progressivamente o GitHub Issues como mecanismo de feedback para conteúdos e substituí-lo por um novo sistema de feedback. Para obter mais informações, veja:Submeter e ver comentários