Criar a camada de acesso a dados

por Erik Reitan

Esta série de tutoriais ensinará os conceitos básicos da criação de um aplicativo ASP.NET Web Forms usando ASP.NET 4.5 e Microsoft Visual Studio Express 2013 para Web. Um projeto Visual Studio 2013 com código-fonte C# está disponível para acompanhar esta série de tutoriais.

Este tutorial descreve como criar, acessar e examinar dados de um banco de dados usando o ASP.NET Web Forms e o Entity Framework Code First. Este tutorial se baseia no tutorial anterior "Criar o Projeto" e faz parte da série de tutoriais wingtip toy store. Ao concluir este tutorial, você criará um grupo de classes de acesso a dados que estão na pasta Modelos do projeto.

O que você aprenderá:

  • Como criar os modelos de dados.
  • Como inicializar e propagar o banco de dados.
  • Como atualizar e configurar o aplicativo para dar suporte ao banco de dados.

Estes são os recursos introduzidos no tutorial:

  • Entity Framework Code First
  • LocalDB
  • Anotações de dados

Criando os modelos de dados

O Entity Framework é uma estrutura ORM (mapeamento relacional de objeto). Ele permite que você trabalhe com dados relacionais como objetos, eliminando a maior parte do código de acesso a dados que você normalmente precisaria gravar. Usando o Entity Framework, você pode emitir consultas usando LINQ e, em seguida, recuperar e manipular dados como objetos fortemente tipado. O LINQ fornece padrões para consultar e atualizar dados. O uso do Entity Framework permite que você se concentre na criação do restante do aplicativo, em vez de se concentrar nos conceitos básicos de acesso a dados. Posteriormente nesta série de tutoriais, mostraremos como usar os dados para preencher consultas de navegação e produtos.

O Entity Framework dá suporte a um paradigma de desenvolvimento chamado Code First. O Code First permite que você defina seus modelos de dados usando classes. Uma classe é um constructo que permite que você crie seus próprios tipos personalizados através do agrupamento de variáveis de outros tipos, métodos e eventos. Você pode mapear classes para um banco de dados existente ou usá-las para gerar um banco de dados. Neste tutorial, você criará os modelos de dados escrevendo classes de modelo de dados. Em seguida, você permitirá que o Entity Framework crie o banco de dados em tempo real a partir dessas novas classes.

Você começará criando as classes de entidade que definem os modelos de dados para o aplicativo Web Forms. Em seguida, você criará uma classe de contexto que gerencia as classes de entidade e fornece acesso a dados ao banco de dados. Você também criará uma classe de inicializador que usará para preencher o banco de dados.

Estrutura e referências de entidade

Por padrão, o Entity Framework é incluído quando você cria um novo aplicativo Web ASP.NET usando o modelo de Web Forms. O Entity Framework pode ser instalado, desinstalado e atualizado como um pacote NuGet.

Este pacote NuGet inclui os seguintes assemblies de runtime em seu projeto:

  • EntityFramework.dll – todo o código de runtime comum usado pelo Entity Framework
  • EntityFramework.SqlServer.dll – o provedor do Microsoft SQL Server para o Entity Framework

Classes de entidade

As classes que você cria para definir o esquema dos dados são chamadas de classes de entidade. Se você for novo no design do banco de dados, pense nas classes de entidade como definições de tabela de um banco de dados. Cada propriedade na classe especifica uma coluna na tabela do banco de dados. Essas classes fornecem uma interface leve e relacional de objeto entre o código orientado a objeto e a estrutura de tabela relacional do banco de dados.

Neste tutorial, você começará adicionando classes de entidade simples que representam os esquemas para produtos e categorias. A classe de produtos conterá definições para cada produto. O nome de cada um dos membros da classe de produto será ProductID, ProductName, Description, ImagePath, UnitPrice, , CategoryIDe Category. A classe de categoria conterá definições para cada categoria à qual um produto pode pertencer, como Carro, Barco ou Avião. O nome de cada um dos membros da classe de categoria será CategoryID, CategoryName, Descriptione Products. Cada produto pertencerá a uma das categorias. Essas classes de entidade serão adicionadas à pasta Modelos existente do projeto.

  1. Em Gerenciador de Soluções, clique com o botão direito do mouse na pasta Modelos e selecione Adicionar ->Novo Item.

    Captura de tela da janela Gerenciador de Soluções com a pasta Modelos realçada e os menus suspensos Adicionar e Novo Item selecionados.

    A caixa de diálogo Adicionar novo item é exibida.

  2. Em Visual C# no painel Instalado à esquerda, selecione Código.

    Captura de tela da janela Adicionar Novo Item mostrando o painel Instalado à esquerda com Visual C# aberto e Código selecionado.

  3. Selecione Classe no painel médio e nomeie esta nova classe Product.cs.

  4. Clique em Adicionar.
    O novo arquivo de classe é exibido no editor.

  5. Substitua o código padrão pelo código a seguir:

    using System.ComponentModel.DataAnnotations;
    
    namespace WingtipToys.Models
    {
        public class Product
        {
            [ScaffoldColumn(false)]
            public int ProductID { get; set; }
    
            [Required, StringLength(100), Display(Name = "Name")]
            public string ProductName { get; set; }
    
            [Required, StringLength(10000), Display(Name = "Product Description"), DataType(DataType.MultilineText)]
            public string Description { get; set; }
    
            public string ImagePath { get; set; }
    
            [Display(Name = "Price")]
            public double? UnitPrice { get; set; }
    
            public int? CategoryID { get; set; }
    
            public virtual Category Category { get; set; }
        }
    }
    
  6. Crie outra classe repetindo as etapas 1 a 4, no entanto, nomeie a nova classe Category.cs e substitua o código padrão pelo seguinte código:

    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    
    namespace WingtipToys.Models
    {
        public class Category
        {
            [ScaffoldColumn(false)]
            public int CategoryID { get; set; }
    
            [Required, StringLength(100), Display(Name = "Name")]
            public string CategoryName { get; set; }
    
            [Display(Name = "Product Description")]
            public string Description { get; set; }
    
            public virtual ICollection<Product> Products { get; set; }
        }
    }
    

Conforme mencionado anteriormente, a Category classe representa o tipo de produto que o aplicativo foi projetado para vender (como "Carros", "Barcos", "Foguetes" e assim por diante) e a Product classe representa os produtos individuais (brinquedos) no banco de dados. Cada instância de um Product objeto corresponderá a uma linha dentro de uma tabela de banco de dados relacional e cada propriedade da classe Product será mapeada para uma coluna na tabela de banco de dados relacional. Posteriormente neste tutorial, você examinará os dados do produto contidos no banco de dados.

Anotações de dados

Talvez você tenha notado que determinados membros das classes têm atributos que especificam detalhes sobre o membro, como [ScaffoldColumn(false)]. São anotações de dados. Os atributos de anotação de dados podem descrever como validar a entrada do usuário para esse membro, especificar a formatação para ele e especificar como ele é modelado quando o banco de dados é criado.

Classe Context

Para começar a usar as classes para acesso a dados, você deve definir uma classe de contexto. Conforme mencionado anteriormente, a classe de contexto gerencia as classes de entidade (como a Product classe e a Category classe) e fornece acesso a dados ao banco de dados.

Este procedimento adiciona uma nova classe de contexto C# à pasta Modelos .

  1. Clique com o botão direito do mouse na pasta Modelos e selecione Adicionar ->Novo Item.
    A caixa de diálogo Adicionar novo item é exibida.

  2. Selecione Classe no painel central, nomeie-a como ProductContext.cs e clique em Adicionar.

  3. Substitua o código padrão contido na classe pelo seguinte código:

    using System.Data.Entity;
    namespace WingtipToys.Models
    {
        public class ProductContext : DbContext
        {
            public ProductContext() : base("WingtipToys")
            {
            }
            public DbSet<Category> Categories { get; set; }
            public DbSet<Product> Products { get; set; }
        }
    }
    

Esse código adiciona o System.Data.Entity namespace para que você tenha acesso a toda a funcionalidade principal do Entity Framework, que inclui a capacidade de consultar, inserir, atualizar e excluir dados trabalhando com objetos fortemente tipado.

A ProductContext classe representa o contexto do banco de dados de produto do Entity Framework, que manipula a busca, o armazenamento e a atualização Product de instâncias de classe no banco de dados. A ProductContext classe deriva da DbContext classe base fornecida pelo Entity Framework.

Classe Initializer

Você precisará executar alguma lógica personalizada para inicializar o banco de dados na primeira vez que o contexto for usado. Isso permitirá que os dados de semente sejam adicionados ao banco de dados para que você possa exibir imediatamente produtos e categorias.

Este procedimento adiciona uma nova classe de inicializador C# à pasta Modelos .

  1. Crie outra Class na pasta Modelos e nomeie-a como ProductDatabaseInitializer.cs.

  2. Substitua o código padrão contido na classe pelo seguinte código:

    using System.Collections.Generic;
    using System.Data.Entity;
    
    namespace WingtipToys.Models
    {
      public class ProductDatabaseInitializer : DropCreateDatabaseIfModelChanges<ProductContext>
      {
        protected override void Seed(ProductContext context)
        {
          GetCategories().ForEach(c => context.Categories.Add(c));
          GetProducts().ForEach(p => context.Products.Add(p));
        }
    
        private static List<Category> GetCategories()
        {
          var categories = new List<Category> {
                    new Category
                    {
                        CategoryID = 1,
                        CategoryName = "Cars"
                    },
                    new Category
                    {
                        CategoryID = 2,
                        CategoryName = "Planes"
                    },
                    new Category
                    {
                        CategoryID = 3,
                        CategoryName = "Trucks"
                    },
                    new Category
                    {
                        CategoryID = 4,
                        CategoryName = "Boats"
                    },
                    new Category
                    {
                        CategoryID = 5,
                        CategoryName = "Rockets"
                    },
                };
    
          return categories;
        }
    
        private static List<Product> GetProducts()
        {
          var products = new List<Product> {
                    new Product
                    {
                        ProductID = 1,
                        ProductName = "Convertible Car",
                        Description = "This convertible car is fast! The engine is powered by a neutrino based battery (not included)." + 
                                      "Power it up and let it go!", 
                        ImagePath="carconvert.png",
                        UnitPrice = 22.50,
                        CategoryID = 1
                   },
                    new Product 
                    {
                        ProductID = 2,
                        ProductName = "Old-time Car",
                        Description = "There's nothing old about this toy car, except it's looks. Compatible with other old toy cars.",
                        ImagePath="carearly.png",
                        UnitPrice = 15.95,
                         CategoryID = 1
                   },
                    new Product
                    {
                        ProductID = 3,
                        ProductName = "Fast Car",
                        Description = "Yes this car is fast, but it also floats in water.",
                        ImagePath="carfast.png",
                        UnitPrice = 32.99,
                        CategoryID = 1
                    },
                    new Product
                    {
                        ProductID = 4,
                        ProductName = "Super Fast Car",
                        Description = "Use this super fast car to entertain guests. Lights and doors work!",
                        ImagePath="carfaster.png",
                        UnitPrice = 8.95,
                        CategoryID = 1
                    },
                    new Product
                    {
                        ProductID = 5,
                        ProductName = "Old Style Racer",
                        Description = "This old style racer can fly (with user assistance). Gravity controls flight duration." + 
                                      "No batteries required.",
                        ImagePath="carracer.png",
                        UnitPrice = 34.95,
                        CategoryID = 1
                    },
                    new Product
                    {
                        ProductID = 6,
                        ProductName = "Ace Plane",
                        Description = "Authentic airplane toy. Features realistic color and details.",
                        ImagePath="planeace.png",
                        UnitPrice = 95.00,
                        CategoryID = 2
                    },
                    new Product
                    {
                        ProductID = 7,
                        ProductName = "Glider",
                        Description = "This fun glider is made from real balsa wood. Some assembly required.",
                        ImagePath="planeglider.png",
                        UnitPrice = 4.95,
                        CategoryID = 2
                    },
                    new Product
                    {
                        ProductID = 8,
                        ProductName = "Paper Plane",
                        Description = "This paper plane is like no other paper plane. Some folding required.",
                        ImagePath="planepaper.png",
                        UnitPrice = 2.95,
                        CategoryID = 2
                    },
                    new Product
                    {
                        ProductID = 9,
                        ProductName = "Propeller Plane",
                        Description = "Rubber band powered plane features two wheels.",
                        ImagePath="planeprop.png",
                        UnitPrice = 32.95,
                        CategoryID = 2
                    },
                    new Product
                    {
                        ProductID = 10,
                        ProductName = "Early Truck",
                        Description = "This toy truck has a real gas powered engine. Requires regular tune ups.",
                        ImagePath="truckearly.png",
                        UnitPrice = 15.00,
                        CategoryID = 3
                    },
                    new Product
                    {
                        ProductID = 11,
                        ProductName = "Fire Truck",
                        Description = "You will have endless fun with this one quarter sized fire truck.",
                        ImagePath="truckfire.png",
                        UnitPrice = 26.00,
                        CategoryID = 3
                    },
                    new Product
                    {
                        ProductID = 12,
                        ProductName = "Big Truck",
                        Description = "This fun toy truck can be used to tow other trucks that are not as big.",
                        ImagePath="truckbig.png",
                        UnitPrice = 29.00,
                        CategoryID = 3
                    },
                    new Product
                    {
                        ProductID = 13,
                        ProductName = "Big Ship",
                        Description = "Is it a boat or a ship. Let this floating vehicle decide by using its " + 
                                      "artifically intelligent computer brain!",
                        ImagePath="boatbig.png",
                        UnitPrice = 95.00,
                        CategoryID = 4
                    },
                    new Product
                    {
                        ProductID = 14,
                        ProductName = "Paper Boat",
                        Description = "Floating fun for all! This toy boat can be assembled in seconds. Floats for minutes!" + 
                                      "Some folding required.",
                        ImagePath="boatpaper.png",
                        UnitPrice = 4.95,
                        CategoryID = 4
                    },
                    new Product
                    {
                        ProductID = 15,
                        ProductName = "Sail Boat",
                        Description = "Put this fun toy sail boat in the water and let it go!",
                        ImagePath="boatsail.png",
                        UnitPrice = 42.95,
                        CategoryID = 4
                    },
                    new Product
                    {
                        ProductID = 16,
                        ProductName = "Rocket",
                        Description = "This fun rocket will travel up to a height of 200 feet.",
                        ImagePath="rocket.png",
                        UnitPrice = 122.95,
                        CategoryID = 5
                    }
                };
    
          return products;
        }
      }
    }
    

Como você pode ver no código acima, quando o banco de dados é criado e inicializado, a Seed propriedade é substituída e definida. Quando a Seed propriedade é definida, os valores das categorias e produtos são usados para preencher o banco de dados. Se você tentar atualizar os dados de semente modificando o código acima após a criação do banco de dados, não verá nenhuma atualização ao executar o aplicativo Web. O motivo é que o código acima usa uma implementação da DropCreateDatabaseIfModelChanges classe para reconhecer se o modelo (esquema) foi alterado antes de redefinir os dados de semente. Se nenhuma alteração for feita nas Category classes de entidade e Product , o banco de dados não será reinicializado com os dados de propagação.

Nota

Se você quisesse que o banco de dados fosse recriado toda vez que executou o aplicativo, poderia usar a DropCreateDatabaseAlways classe em vez da DropCreateDatabaseIfModelChanges classe . No entanto, para esta série de tutoriais, use a DropCreateDatabaseIfModelChanges classe .

Neste tutorial, você terá uma pasta Modelos com quatro novas classes e uma classe padrão:

Criar a camada de acesso a dados – pasta Modelos

Configurando o aplicativo para usar o modelo de dados

Agora que você criou as classes que representam os dados, você deve configurar o aplicativo para usar as classes. No arquivo Global.asax , você adiciona código que inicializa o modelo. No arquivo Web.config você adiciona informações que informam ao aplicativo qual banco de dados você usará para armazenar os dados representados pelas novas classes de dados. O arquivo Global.asax pode ser usado para manipular eventos ou métodos de aplicativo. O arquivo Web.config permite controlar a configuração do aplicativo Web ASP.NET.

Atualizando o arquivo Global.asax

Para inicializar os modelos de dados quando o aplicativo for iniciado, você atualizará o Application_Start manipulador no arquivo Global.asax.cs .

Nota

Em Gerenciador de Soluções, você pode selecionar o arquivo Global.asax ou o arquivo Global.asax.cs para editar o arquivo Global.asax.cs.

  1. Adicione o código a seguir realçado em amarelo ao Application_Start método no arquivo Global.asax.cs .

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Optimization;
    using System.Web.Routing;
    using System.Web.Security;
    using System.Web.SessionState;
    using System.Data.Entity;
    using WingtipToys.Models;
    
    namespace WingtipToys
    {
        public class Global : HttpApplication
        {
            void Application_Start(object sender, EventArgs e)
            {
                // Code that runs on application startup
                RouteConfig.RegisterRoutes(RouteTable.Routes);
                BundleConfig.RegisterBundles(BundleTable.Bundles);
    
                // Initialize the product database.
                Database.SetInitializer(new ProductDatabaseInitializer());
            }
        }
    }
    

Nota

Seu navegador deve dar suporte a HTML5 para exibir o código realçado em amarelo ao exibir esta série de tutoriais em um navegador.

Conforme mostrado no código acima, quando o aplicativo é iniciado, o aplicativo especifica o inicializador que será executado durante a primeira vez que os dados são acessados. Os dois namespaces adicionais são necessários para acessar o Database objeto e o ProductDatabaseInitializer objeto .

Modificando o arquivo de Web.Config

Embora o Entity Framework Code First gere um banco de dados para você em um local padrão quando o banco de dados é preenchido com dados de semente, adicionar suas próprias informações de conexão ao seu aplicativo fornece controle do local do banco de dados. Especifique essa conexão de banco de dados usando uma cadeia de conexão no arquivo Web.config do aplicativo na raiz do projeto. Ao adicionar uma nova cadeia de conexão, você pode direcionar o local do banco de dados (wingtiptoys.mdf) a ser criado no diretório de dados do aplicativo (App_Data), em vez de seu local padrão. Fazer essa alteração permitirá que você encontre e inspecione o arquivo de banco de dados mais adiante neste tutorial.

  1. Em Gerenciador de Soluções, localize e abra o arquivo Web.config.

  2. Adicione a cadeia de conexão realçada em amarelo à <connectionStrings> seção do arquivo Web.config da seguinte maneira:

    <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\aspnet-WingtipToys-20131119102907.mdf;Initial Catalog=aspnet-WingtipToys-20131119102907;Integrated Security=True"
    providerName="System.Data.SqlClient" />
    <add name="WingtipToys"
    connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\wingtiptoys.mdf;Integrated Security=True"
    providerName="System.Data.SqlClient" />
    </connectionStrings>
    

Quando o aplicativo for executado pela primeira vez, ele criará o banco de dados no local especificado pela cadeia de conexão. Mas antes de executar o aplicativo, vamos compilá-lo primeiro.

Compilando o aplicativo

Para garantir que todas as classes e alterações no aplicativo Web funcionem, você deve compilar o aplicativo.

  1. No menu Depurar , selecione Compilar WingtipToys.
    A janela Saída é exibida e, se tudo correu bem, você verá uma mensagem bem-sucedida .

    Criar a camada de acesso a dados – Janelas de saída

Se você encontrar um erro, marcar novamente as etapas acima. As informações na janela Saída indicarão qual arquivo tem um problema e onde no arquivo uma alteração é necessária. Essas informações permitirão que você determine qual parte das etapas acima precisa ser revisada e corrigida em seu projeto.

Resumo

Neste tutorial da série, você criou o modelo de dados, bem como adicionou o código que será usado para inicializar e propagar o banco de dados. Você também configurou o aplicativo para usar os modelos de dados quando o aplicativo for executado.

No próximo tutorial, você atualizará a interface do usuário, adicionará navegação e recuperará dados do banco de dados. Isso fará com que o banco de dados seja criado automaticamente com base nas classes de entidade que você criou neste tutorial.

Recursos adicionais

Visão geral do Entity Framework
Guia do iniciante para o ADO.NET Entity Framework
Code First Development with Entity FrameworkCode First Relationships Fluent API
Anotações de dados do primeiro código
Melhorias de produtividade para o Entity Framework