Compartir a través de


Crear la capa de acceso a datos

Por Erik Reitan

En esta serie de tutoriales se le enseñarán los conceptos básicos de la creación de una aplicación ASP.NET Web Forms mediante ASP.NET 4.5 y Microsoft Visual Studio Express 2013 para la Web. Como acompañamiento a esta serie de tutoriales, hay disponible un proyecto con código fuente de C# de Visual Studio 2013.

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 "Creación del proyecto" y forma parte de la serie de tutoriales de Wingtip Toy Store. Cuando haya completado este tutorial, habrá creado un grupo de clases de acceso a datos en la carpeta Models del proyecto.

Temas que se abordarán:

  • Cómo crear los modelos de datos.
  • Cómo inicializar 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:

  • Entity Framework Code First
  • LocalDB
  • Anotaciones de datos

Creación de los modelos de datos

Entity Framework es un marco de asignación relacional de objetos (ORM). Permite trabajar con datos relacionales como objetos y 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, después, recuperar y manipular datos como objetos fuertemente tipados. LINQ proporciona patrones para consultar y actualizar datos. El uso de Entity Framework le permite centrarse en la creación del resto de la aplicación, en lugar de 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 le permite crear tipos personalizados propios mediante la agrupación de 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. Después, 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 Web Forms. Posteriormente, 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 de Web Forms. Entity Framework se puede instalar, desinstalar y actualizar como un paquete NuGet.

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

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

Clases de entidad

Las clases que crea para definir el esquema de los datos se denominan clases de entidad. Si no está familiarizado con el diseño de bases 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 relacional de objetos ligera 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 de productos contendrá definiciones para cada producto. El nombre de cada uno de los miembros de la clase de productos será ProductID, ProductName, Description, ImagePath, UnitPrice, CategoryID y Category. La clase de categorías 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ías será CategoryID, CategoryName, Description y 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 Models y seleccione Agregar ->Nuevo elemento.

    Screenshot of the Solution Explorer window with the Models folder highlighted and the dropdown menus Add and New Item selected.

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

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

    Screenshot of the Add New Item window showing the Installed pane on the left with Visual C# open and Code selected.

  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 la clase se mostrará en el editor.

  5. Reemplace el código predeterminado por el 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, pero esta vez asigne el nombre Category.cs a la nueva clase 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 ha mencionado anteriormente, la clase Category representa el tipo de producto que la aplicación está diseñada para vender (como "Cars", "Boats", "Rockets", etc.), y la clase Product representa los productos individuales (juguetes) en la base de datos. Cada instancia de un objeto Product corresponderá con una fila dentro de una tabla de una base de datos relacional, y cada propiedad de la clase Product se asignará a una columna de la tabla de la base de datos relacional. Más adelante en este tutorial, revisará los datos de productos que contiene 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 y especificar cómo se modela cuando se crea la base de datos.

Context (Clase)

Para empezar a usar las clases para el acceso a los datos, debe definir una clase de contexto. Como se ha mencionado anteriormente, la clase de contexto administra las clases de entidad (como la clase Product y la clase Category) 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 Models y, luego, seleccione Agregar ->Nuevo elemento.
    Se abrirá 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 que contiene 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 espacio de nombres System.Data.Entity para que tenga acceso a toda la funcionalidad básica de Entity Framework, que incluye la capacidad de consultar, insertar, actualizar y eliminar datos mediante el uso de objetos fuertemente tipados.

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

Clase de inicializador

Tendrá que ejecutar 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 los productos y categorías.

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

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

  2. Reemplace el código predeterminado que contiene 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 reemplaza y se establece. Cuando se establece la propiedad Seed, 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. Esto se debe a que el código anterior usa una implementación de la clase DropCreateDatabaseIfModelChanges 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 quiere que la base de datos se vuelva a crear cada vez que ejecute la aplicación, puede usar la clase DropCreateDatabaseAlways en lugar de la clase DropCreateDatabaseIfModelChanges. Sin embargo, para esta serie de tutoriales, use la clase DropCreateDatabaseIfModelChanges.

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

Create the Data Access Layer - Models Folder

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 debe agregar el código que inicializa el modelo. En el archivo Web.config debe agregar información que indica a la aplicación qué base de datos debe 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 le permite controlar la configuración de la aplicación web ASP.NET.

Actualización del archivo Global.asax

Para inicializar los modelos de datos cuando se inicie la aplicación, debe actualizar el controlador Application_Start 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 método Application_Start 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:

Su explorador debe admitir HTML5 para visualizar 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á la primera vez que se acceda a los datos. Los dos espacios de nombres adicionales son necesarios para tener acceso al objeto Database y al objeto ProductDatabaseInitializer.

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 proporciona 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 compile en el directorio de datos de la aplicación (App_Data), en lugar de usar 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 sección <connectionStrings> del archivo Web.config de la manera siguiente:

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

Compilación de la aplicación

Para asegurarse de que todas las clases y los cambios en la aplicación web funcionan, debe compilar la aplicación.

  1. En el menú Depurar, seleccione Compilar WingtipToys.
    Se muestra la ventana Resultados y, si todo salió bien, verá un mensaje succeeded.

    Create the Data Access Layer - Output Windows

Si se produce un error, vuelva a realizar los pasos anteriores. La información de la ventana Resultados 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, ha creado el modelo de datos y ha agregado el código que se usará para inicializar 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 a partir de las clases de entidad que ha creado en este tutorial.

Recursos adicionales

Información general sobre Entity Framework
Guía para principiantes de ADO.NET Entity Framework
Desarrollo Code First con Entity FrameworkAPI fluida para relaciones de Code First
Anotaciones de datos de Code First
Mejoras de productividad para Entity Framework