Entity Framework 6 – Mapeando procedures para inclusão, atualização e exclusão de entidades.

Nas versões anteriores do modelo Code First era possível o uso de stored procedures apenas na execução de queries. Atualmente, no Entity Framework 6 (ainda como Release Candidate) é possível o mapeamento de stored procedures para execução de comandos de inclusão, alteração e exclusão de registros.

logo

Instalação

Para adicionar as referências do Entity Framework 6 (que estava na versão Release Candidate durante a escrita deste post), digite a seguinte linha de comando em seu package manager console:

install-package entityframework -pre

Restrições

1 – O mapeamento de stored procedures pode ser feito apenas via Fluent API.

2 – Não se pode utilizar um mapeamento mesclado (exemplo, “o comando de exclusão mapeado com stored procedures e os comandos e inclusão e alteração gerados dinamicamente pelo Entity Framework”).

Exemplo

Para o exemplo de uso de comandos mapeados por stored procedures utilizaremos uma estrutura de classes bastante simples, com um relacionamento muitos-para-muitos entre as classes. A estrutura utilizada no exemplo pode ser vista abaixo:

classDiagram

O código necessário para criar essa estrutura é demonstrado a seguir.

public class Book {

    public int Id { get; set; }

    public string Title { get; set; }

    public int Year { get; set; }

    public ICollection<Author> Authors { get; set; }
}

public class Author {

    public int Id { get; set; }

    public string Name { get; set; }

    public DateTime DateBirth { get; set; }

    public ICollection<Book> Books { get; set; }
}

Para mapear os comandos com stored procedures é apenas preciso indicar o nome das stored procedures, e se necessário, as propriedades com seus respectivos parâmetros.

modelBuilder.Entity<Author>()
            .MapToStoredProcedures(e => {
                e.Insert(c => c.HasName("Author_Insert"));
                e.Update(c => c.HasName("Author_Update"));
                e.Delete(c => c.HasName("Author_Delete"));
            });

O código necessário para execução das stored procedures é listado abaixo:

CREATE PROCEDURE Author_Insert
    @Name nvarchar(max),
    @DateBirth datetime
AS
BEGIN
    INSERT Authors(Name, DateBirth)
    VALUES (@Name, @DateBirth)

    SELECT scope_identity() AS Id

END
GO

CREATE PROCEDURE Author_Update
    @Id int,
    @Name nvarchar(max),
    @DateBirth datetime
AS
BEGIN
    UPDATE Authors
    SET Name = @Name, DateBirth = @DateBirth
    WHERE (Id = @Id)
END
GO

CREATE PROCEDURE Author_Delete
    @Id int
AS
BEGIN
    DELETE Authors
    WHERE (Id = @Id)
END
GO

Desta forma bastante simples mapeamos comandos do EF com stored procedures. Um cenário bastante avançado, é o cenário no qual somos obrigados a mapear relacionamentos muitos-para-muitos. Neste cenário podemos utilizar os recursos de mapeamento de comandos por stored procedures para customizarmos o comportamento da inserção de dados.

Para mapearmos comandos de relacionamentos muitos-para-muitos a lógica de uso do EF não muda. Precisamos mapear as stored procedures com as entidades e criá-las no banco de dados.

O mapeamento das stored procedures deve seguir a seguinte estrutura:

modelBuilder.Entity<Author>()
            .HasMany<Book>(b => b.Books)
            .WithMany(a => a.Authors)
            .MapToStoredProcedures(s =>
                s.Insert(i => i.HasName("AuhtorBook_Insert")
                    .LeftKeyParameter(a => a.Id, "AuthorId")
                    .RightKeyParameter(b => b.Id, "BookId"))
                    .Delete(d => d.HasName("AuhtorBook_Delete")
                    .LeftKeyParameter(a => a.Id, "AuthorId")
                    .RightKeyParameter(b => b.Id, "BookId")));

E o código das stored procedures pode seguir a seguinte lógica:

CREATE PROCEDURE AuhtorBook_Insert
    @AuthorId int,
    @BookId int
AS
BEGIN
    INSERT AuthorBooks(Author_Id, Book_Id)
    VALUES (@AuthorId, @BookId)
END
GO

CREATE PROCEDURE AuhtorBook_Delete
    @AuthorId int,
    @BookId int
AS
BEGIN
    DELETE AuthorBooks
    WHERE ((Author_Id = @AuthorId) AND (Book_Id = @BookId))
END
GO

Todo o código C# utilizado no exemplo pode ser conferido abaixo.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;

namespace ConsoleApplicationEFProc {
    class Program {
        static void Main(string[] args) {

            CreateIfNotExists();

            var newAuthor = new Author();

            newAuthor.Name = "Carl Sagan";
            newAuthor.DateBirth = new DateTime(1934, 11, 9);

            Insert(newAuthor);

            Update(newAuthor);

            Delete(newAuthor);

            Book newBook = new Book();
            newBook.Title = "Cosmos";
            newBook.Year = 1980;
            newBook.Authors = new Collection<Author>();
            newBook.Authors.Add(newAuthor);

            Insert(newBook);
        }

        static void CreateIfNotExists() {

            using (var context = new DataContext()) {

                context.Database.CreateIfNotExists();
            }
        }

        static void Insert(Book book) {

            using (var context = new DataContext()) {

                context.Books.Add(book);

                context.SaveChanges();
            }
        }

        static void Insert(Author author) {

            using (var context = new DataContext()) {

                context.Authors.Add(author);

                context.SaveChanges();
            }
        }

        static void Update(Author author) {

            using (var context = new DataContext()) {

                context.Entry(author).State = EntityState.Modified;

                context.SaveChanges();
            }
        }

        static void Delete(Author author) {

            using (var context = new DataContext()) {

                context.Entry(author).State = EntityState.Deleted;

                context.SaveChanges();
            }
        }
    }

    public class Book {

        public int Id { get; set; }

        public string Title { get; set; }

        public int Year { get; set; }

        public ICollection<Author> Authors { get; set; }
    }

    public class Author {

        public int Id { get; set; }

        public string Name { get; set; }

        public DateTime DateBirth { get; set; }

        public ICollection<Book> Books { get; set; }
    }

    public class DataContext : DbContext {

        public DbSet<Author> Authors { get; set; }

        public DbSet<Book> Books { get; set; }

        public DataContext() :
            base(@"Connection string") {

            Database.SetInitializer(
                           new DropCreateDatabaseIfModelChanges<DataContext>());
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {

            modelBuilder.Entity<Author>()
                        .HasMany<Book>(b => b.Books)
                        .WithMany(a => a.Authors)
                        .MapToStoredProcedures(s =>
                            s.Insert(i => i.HasName("AuhtorBook_Insert")
                                .LeftKeyParameter(a => a.Id, "AuthorId")
                                .RightKeyParameter(b => b.Id, "BookId"))
                                .Delete(d => d.HasName("AuhtorBook_Delete")
                                .LeftKeyParameter(a => a.Id, "AuthorId")
                                .RightKeyParameter(b => b.Id, "BookId")));

            modelBuilder.Entity<Author>()
                        .MapToStoredProcedures(e => {
                            e.Insert(c => c.HasName("Author_Insert"));
                            e.Update(c => c.HasName("Author_Update"));
                            e.Delete(c => c.HasName("Author_Delete"));
                        });

            base.OnModelCreating(modelBuilder);
        }
    }
}

Por

MSc. Fernando Henrique Inocêncio Borba Ferreira

Microsoft Most Valuable Professional – Visual C#

Referências:

http://entityframework.codeplex.com/wikipage?title=Code%20First%20Insert%2fUpdate%2fDelete%20Stored%20Procedure%20Mapping

http://blogs.msdn.com/b/adonet/archive/2013/08/21/ef6-release-candidate-available.aspx

http://www.nuget.org/packages/EntityFramework

http://channel9.msdn.com/Shows/Visual-Studio-Toolbox/Entity-Framework-5-and-6

Anúncios

5 Responses to Entity Framework 6 – Mapeando procedures para inclusão, atualização e exclusão de entidades.

  1. Boa Fê!

    O mapeamento de store procedures é um recurso bem requisitado por muitos desenvolvedores e você forneceu uma ótima explicação! Claro e direto ao ponto, show de bola!

    Abraços.

  2. Alexandre says:

    Muito bom Fernando. Parabéns pelo artigo.

    Estou iniciando os testes com o EF6 aqui e os seus posts como sempre dando uma força enorme.

    Abraço.

  3. Joel Rodrigues says:

    Parabéns pelo post e obrigado por compartilhar essas informações. Aqui na empresa, sem dúvida, vai ser um ponto a mais a favor do EF, pois o pessoal está acostumado a usar procedures para as operações de CRUD.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: