Quando utilizamos o Entity Framework para realizar as principais operações relacionadas a bancos de dados de uma aplicação (CRUD), grande parte do trabalho de escrever instruções SQL é poupada, uma vez que o EF fica responsável por essa parte do processo e executa tudo internamente, sem que o desenvolvedor precise escrever as instruções de banco de dados diretamente.
Considerando o caso mais simples, e também o mais comum, que é a realização das operações de CRUD (Create, Retrieve, Update and Delete, ou seja, Criação, Leitura, Atualização e Exclusão de registros), basta que efetuemos o mapeamento da tabela e a partir daí podemos fazer uso da arquitetura completamente orientada a objetos do EF. As operações SQL não precisam ser escritas diretamente na aplicação, bastando utilizar métodos como Add e Remove nas próprias classes geradas no mapeamento.
As instruções SQL são geradas pelo próprio frameworks e executadas sem que o desenvolvedor precise se preocupar em escrever inserts, updates, deletes ou selects.
Porém, existem casos em que empresas (ou desenvolvedores individualmente) optam por utilizar stored procedures para realizar essas operações. Nesses procedures geralmente são inseridas regras de negócio, validações e processos de automatização.
Esse tipo de prática é mais comum em projetos que utilizam datasets tipados, que em sua configuração já preveem essa situação e permitem que o desenvolvedor selecione quais procedures existentes no banco serão responsáveis por tais operações.
Como o Entity Framework é o mecanismo de acesso a dados mais moderno e atualizado atualmente do .NET Framework, é natural que projetos sejam migrados ou que novos projetos façam uso dessa tecnologia. Porém, existem casos em que é necessário manter esse formato de utilização de procedures para realizar o CRUD e, como era de se esperar, o EF possui suporte para esse tipo de situação.
Para tornar a explicação mais clara, trabalharemos aqui com um exemplo prático, onde criaremos uma tabela e três stored procedures para mapear as operações de insert, update e delete. A Listagem 1 contém o código completo do banco utilizado no exemplo.
Listagem1: Estrutura do banco utilizado
Codigo		int primary key,
Descricao	varchar(100) unique,
Preco		decimal(10,2)
create procedure usp_Produtos_Insert
@Codigo		int,
@Descricao	varchar(100),
@Preco		decimal(10,2)
if not exists(select Codigo from Produtos where Codigo = @Codigo)
insert into Produtos(Codigo, Descricao, Preco) values (@Codigo, @Descricao + ' - NOVO', @Preco)
create procedure usp_Produtos_Update
update Produtos set Descricao = @Descricao + ' - ATUALIZADO', Preco=@Preco
create procedure usp_Produtos_Delete
@Codigo		int
Mapeando as tabelas e procedures
Com o banco pronto, criaremos uma aplicação ASP.NET MVCe adicionaremos um ADO.NET Entity DataModel na pasta Models, um procedimento comum nesse tipo de aplicação.
Ao criar o Data Model, além de selecionar a tabela Produtos na seção Tables, devemos selecionar também os três procedures na seção Stored Procedures and Functions, como mostra a Figura 1.
Figura 1: Importando os procedures na criação do Data Model
Nota: essa importação também pode ser feita posteriormente à criação do Data Model, bastando clicar com a direita na parte branca do modelo e no menu Update Model from Database.
Feito isso, o próximo passo é criar as funções que serão responsáveis por executar esses procedures, ou seja, realizar o mapeamento dos procedures em funções, assim como as tabelas são mapeadas como classes e coleções dessas.
Para isso, clica-se com a direita na parte branca do modelo, e então no menu Add New > Function Import, conforme a Figura 2.
Figura 2: Mapeando os procedures
Na janela que se abrirá (Figura 3), selecione um dos procedures e dê um nome à função equivalente. Repita o procedimento para os três procedures criados.
Figura 3: Criando função para mapear procedure
Agora que já temos as funções criadas, podemos ligá-las à tabela mapeada de Produtos. Clicando com a direita sobre a entidade Produtos no Data Model, devemos usar a opção Stored Procedure Mapping, como vemos na Figura 4.
Figura 4: Mapear procedures para a entidade
Na parte inferior do Visual Studio, surgirá uma aba chamada Mapping Details, que pode ser vista na Figura 5. Nela encontramos os campos onde devemos selecionar as funções que serão responsáveis pelas operações de insert, update e delete. Clicando em cada uma delas, devemos escolher uma das funções que criamos anteriormente.
Figura 5: Selecionar as funções de insert, update e delete
Quando selecionarmos cada função, alguns detalhes serão exibidos logo abaixo (Figura 6). Analisando esses detalhes, observamos o relacionamento entre os parâmetros do procedure e a propriedade equivalente na entidade. Em geral mantemos o padrão, mas se for necessário, pode-se escolher qualquer campo com tipo adequado para preencher os parâmetros.
Figura 6: Relacionamento entre as propriedades da entidade e os parâmetros do procedure
Nota: a operação de select não é mapeada da mesma forma, uma vez que esse as consultas geralmente são efetuadas com LINQ.
Para testar, podemos criar um controller para o model Produtos e acessar no browser. Ao inserirmos um novo produto e voltarmos para a página de listagem, veremos que a descrição que inserimos foi modificada, recebendo " – NOVO" no final (Figura 7). Isso foi configurado em nosso stored procedure de insert, apenas para exemplificar o funcionamento deste mapeamento.
Figura 7: Registro inserido através do stored procedure
Se alteramos o registro, veremos que a descrição receberá " – ATUALIZADO" no final, o que foi definido no store procedure de update, no início deste artigo.
Na prática, como já foi dito, nos procedures pode haver implementação de regras de negócio, validações e automatizações, dependendo de cada necessidade.