Creación de la capa de acceso a datos

por Erik Reitan

Esta serie de tutoriales le enseñará los conceptos básicos para crear una aplicación con formularios web ASP.NET utilizando ASP.NET 4.5 y Microsoft Visual Studio Express 2013 para Web. Un proyecto de Visual Studio 2013 con código fuente de C# está disponible para acompañar a esta serie de tutoriales.

En este tutorial se describe cómo crear, acceder y revisar datos de una base de datos mediante ASP.NET Web Forms y Entity Framework Code First. Este tutorial se basa en el tutorial anterior "Crear el proyecto" y forma parte de la serie de tutoriales wingtip Toy Store. Cuando haya completado este tutorial, habrá creado un grupo de clases de acceso a datos que se encuentran en la carpeta Models del proyecto.

Lo que aprenderá:

  • Cómo crear los modelos de datos.
  • Cómo inicializar y poblar la base de datos.
  • ¿Cómo actualizar y configurar la aplicación para admitir la base de datos?

Estas son las características introducidas en el tutorial:

  • Código de Entity Framework First
  • LocalDB
  • Anotaciones de datos

Creación de modelos de datos

Entity Framework es un marco de asignación relacional de objetos (ORM). Permite trabajar con datos relacionales como objetos, lo que elimina la mayoría del código de acceso a datos que normalmente tendría que escribir. Con Entity Framework, puede emitir consultas mediante LINQ y, a continuación, recuperar y manipular datos como objetos fuertemente tipados. LINQ proporciona patrones para consultar y actualizar datos. El uso de Entity Framework permite centrarse en la creación del resto de la aplicación, en lugar de centrarse en los aspectos básicos del acceso a los datos. Más adelante en esta serie de tutoriales, le mostraremos cómo usar los datos para rellenar las consultas de navegación y productos.

Entity Framework admite un paradigma de desarrollo denominado Code First. Code First permite definir los modelos de datos mediante clases. Una clase es una construcción que permite crear sus propios tipos personalizados agrupando variables de otros tipos, métodos y eventos. Puede asignar clases a una base de datos existente o usarlas para generar una base de datos. En este tutorial, creará los modelos de datos escribiendo clases de modelo de datos. A continuación, permitirá que Entity Framework cree la base de datos sobre la marcha a partir de estas nuevas clases.

Comenzará creando las clases de entidad que definen los modelos de datos para la aplicación de Formularios Web Forms. A continuación, creará una clase de contexto que administra las clases de entidad y proporciona acceso a los datos a la base de datos. También creará una clase de inicializador que usará para rellenar la base de datos.

Entity Framework y referencias

De forma predeterminada, Entity Framework se incluye al crear una nueva aplicación web ASP.NET mediante la plantilla formularios web . Entity Framework se puede instalar, desinstalar y actualizar como un paquete NuGet.

Este paquete NuGet incluye los siguientes ensamblados de runtime en tu proyecto:

  • EntityFramework.dll: todo el código de Common Runtime usado por Entity Framework
  • EntityFramework.SqlServer.dll: proveedor de Microsoft SQL Server para Entity Framework

Clases de entidad

Las clases que cree para definir el esquema de los datos se denominan clases de entidad. Si no está familiarizado con el diseño de la base de datos, piense en las clases de entidad como definiciones de tabla de una base de datos. Cada propiedad de la clase especifica una columna de la tabla de la base de datos. Estas clases proporcionan una interfaz ligera y objeto-relacional entre el código orientado a objetos y la estructura de tabla relacional de la base de datos.

En este tutorial, empezará agregando clases de entidad simples que representan los esquemas de productos y categorías. La clase products contendrá definiciones para cada producto. El nombre de cada uno de los miembros de la clase de producto será ProductID, ProductName, Description, ImagePath, UnitPrice, y CategoryIDCategory. La clase de categoría contendrá definiciones para cada categoría a la que un producto pueda pertenecer, como Car, Boat o Plane. El nombre de cada uno de los miembros de la clase de categoría será CategoryID, CategoryName, Descriptiony Products. Cada producto pertenecerá a una de las categorías. Estas clases de entidad se agregarán a la carpeta Models existente del proyecto.

  1. En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Modelos y seleccione Agregar ->Nuevo elemento.

    Captura de pantalla de la ventana Explorador de soluciones con la carpeta Modelos resaltada y los menús desplegables Agregar y Nuevo elemento seleccionados.

    Se muestra el cuadro de diálogo Agregar nuevo elemento .

  2. En Visual C# en el panel Instalado de la izquierda, seleccione Código.

    Captura de pantalla de la ventana Agregar nuevo elemento que muestra el panel Instalado a la izquierda con Visual C# abierto y Código seleccionado.

  3. Seleccione Clase en el panel central y asigne a esta nueva clase el nombre Product.cs.

  4. Haga clic en Agregar.
    El nuevo archivo de clase se muestra en el editor.

  5. Reemplace el código predeterminado por el código siguiente:

    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. Cree otra clase repitiendo los pasos del 1 al 4, sin embargo, asigne un nombre a la nueva clase Category.cs y reemplace el código predeterminado por el código siguiente:

    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; }
        }
    }
    

Como se mencionó anteriormente, la Category clase representa el tipo de producto que la aplicación está diseñada para vender (como "Coches", "Barcos", "Cohetes", etc.), y la Product clase representa los productos individuales (juguetes) en la base de datos. Cada instancia de un Product objeto corresponderá a una fila dentro de una tabla de base de datos relacional y cada propiedad de la clase Product se asignará a una columna de la tabla de base de datos relacional. Más adelante en este tutorial, revisará los datos del producto contenidos en la base de datos.

Anotaciones de datos

Es posible que haya observado que determinados miembros de las clases tienen atributos que especifican detalles sobre el miembro, como [ScaffoldColumn(false)]. Se trata de anotaciones de datos. Los atributos de anotación de datos pueden describir cómo validar la entrada del usuario para ese miembro, especificar el formato para él y especificar cómo se modela cuando se crea la base de datos.

Clase Contexto

Para empezar a usar las clases para el acceso a datos, debe definir una clase de contexto. Como se mencionó anteriormente, la clase de contexto administra las clases de entidad (como la Product clase y la Category clase) y proporciona acceso a los datos a la base de datos.

Este procedimiento agrega una nueva clase de contexto de C# a la carpeta Models .

  1. Haga clic con el botón derecho en la carpeta Modelos y seleccione Agregar ->Nuevo elemento.
    Se muestra el cuadro de diálogo Agregar nuevo elemento .

  2. Seleccione Clase en el panel central, asígnele el nombre ProductContext.cs y haga clic en Agregar.

  3. Reemplace el código predeterminado contenido en la clase por el código siguiente:

    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; }
        }
    }
    

Este código agrega el System.Data.Entity espacio de nombres para que tengas acceso a toda la funcionalidad básica del Entity Framework, que incluye la capacidad de consultar, insertar, actualizar y eliminar datos trabajando con objetos fuertemente tipificados.

La ProductContext clase representa el contexto de base de datos de producto de Entity Framework, que controla la captura, el almacenamiento y la actualización Product de instancias de clase en la base de datos. La ProductContext clase deriva de la DbContext clase base proporcionada por Entity Framework.

Clase Inicializadora

Tendrá que ejecutar alguna lógica personalizada para inicializar la base de datos la primera vez que se usa el contexto. Esto permitirá agregar datos de inicialización a la base de datos para que pueda mostrar inmediatamente productos y categorías.

Este procedimiento agrega una nueva clase de inicializador de C# a la carpeta Models .

  1. Cree otro Class en la carpeta Models y asígnelo el nombre ProductDatabaseInitializer.cs.

  2. Reemplace el código predeterminado contenido en la clase por el código siguiente:

    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 puede ver en el código anterior, cuando se crea e inicializa la base de datos, la propiedad Seed se sobrescribe y se establece. Cuando se establece la Seed propiedad , los valores de las categorías y los productos se usan para rellenar la base de datos. Si intenta actualizar los datos de inicialización modificando el código anterior una vez creada la base de datos, no verá ninguna actualización al ejecutar la aplicación web. El motivo es que el código anterior usa una implementación de la DropCreateDatabaseIfModelChanges clase para reconocer si el modelo (esquema) ha cambiado antes de restablecer los datos de inicialización. Si no se realizan cambios en las clases de entidad Category y Product , la base de datos no se reinicializará con los datos de inicialización.

Nota:

Si desea que la base de datos se vuelva a crear cada vez que ejecutó la aplicación, podría usar la DropCreateDatabaseAlways clase en lugar de la DropCreateDatabaseIfModelChanges clase . Sin embargo, para esta serie de tutoriales, use la DropCreateDatabaseIfModelChanges clase .

En este punto de este tutorial, tendrá una carpeta Models con cuatro clases nuevas y una clase predeterminada:

Creación de la capa de acceso a datos: carpeta Models

Configuración de la aplicación para usar el modelo de datos

Ahora que ha creado las clases que representan los datos, debe configurar la aplicación para que use las clases. En el archivo Global.asax , se agrega código que inicializa el modelo. En el archivo Web.config agrega información que indica a la aplicación qué base de datos usará para almacenar los datos representados por las nuevas clases de datos. El archivo Global.asax se puede usar para controlar los eventos o métodos de la aplicación. El archivo Web.config permite controlar la configuración de la aplicación web de ASP.NET.

Actualización del archivo Global.asax

Para inicializar los modelos de datos cuando se inicie la aplicación, actualizará el Application_Start controlador en el archivo Global.asax.cs .

Nota:

En el Explorador de soluciones, puede seleccionar el archivo Global.asax o el archivo Global.asax.cs para editar el archivo Global.asax.cs .

  1. Agregue el código siguiente resaltado en amarillo al Application_Start método en el archivo 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:

El explorador debe admitir HTML5 para ver el código resaltado en amarillo al ver esta serie de tutoriales en un explorador.

Como se muestra en el código anterior, cuando se inicia la aplicación, la aplicación especifica el inicializador que se ejecutará durante la primera vez que se accede a los datos. Los dos espacios de nombres adicionales son necesarios para tener acceso al Database objeto y al ProductDatabaseInitializer objeto .

Modificación del archivo Web.Config

Aunque Entity Framework Code First generará una base de datos automáticamente en una ubicación predeterminada cuando la base de datos se rellene con datos de inicialización, agregar su propia información de conexión a la aplicación le proporcionará el control de la ubicación de la base de datos. Especifique esta conexión de base de datos mediante una cadena de conexión en el archivo Web.config de la aplicación en la raíz del proyecto. Al agregar una nueva cadena de conexión, puede dirigir la ubicación de la base de datos (wingtiptoys.mdf) para que se cree en el directorio de datos de la aplicación (App_Data), en lugar de su ubicación predeterminada. Realizar este cambio le permitirá buscar e inspeccionar el archivo de base de datos más adelante en este tutorial.

  1. En el Explorador de soluciones, busque y abra el archivo Web.config .

  2. Agregue la cadena de conexión resaltada en amarillo a la <connectionStrings> sección del archivo Web.config de la siguiente manera:

    <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>
    

Cuando la aplicación se ejecuta por primera vez, compilará la base de datos en la ubicación especificada por la cadena de conexión. Pero antes de ejecutar la aplicación, vamos a compilarla primero.

Compilación de la aplicación

Para asegurarse de que todas las clases y los cambios en la Aplicación Web funcionen correctamente, debe compilar la aplicación.

  1. En el menú Depurar, seleccione Compilar WingtipToys.
    Se muestra la ventana Salida, y si todo ha ido bien, ve un mensaje de éxito.

    Creación de la capa de acceso a datos: Salida de Windows

Si se produce un error, vuelva a comprobar los pasos anteriores. La información de la ventana Salida indicará qué archivo tiene un problema y dónde se requiere un cambio en el archivo. Esta información le permitirá determinar qué parte de los pasos anteriores deben revisarse y corregirse en el proyecto.

Resumen

En este tutorial de la serie, usted ha creado el modelo de datos y ha agregado el código que se usará para inicializar y poblar la base de datos. También ha configurado la aplicación para usar los modelos de datos cuando se ejecuta la aplicación.

En el siguiente tutorial, actualizará la interfaz de usuario, agregará navegación y recuperará datos de la base de datos. Esto hará que la base de datos se cree automáticamente en función de las clases de entidad que creó en este tutorial.

Recursos adicionales

Introducción a Entity Framework
Guía para principiantes del marco de entidades de ADO.NET
Desarrollo de Code First con Entity FrameworkCode First Relationships Fluent API
Anotaciones de datos de Code First
Mejoras de productividad para Entity Framework