sábado, 22 de octubre de 2016

CRUD Kendo Grid ASP.NET MVC, con Entity Framework a SQL Server - Part 1

Introducción

En esta serie de post veremos cómo crear un aplicativo WEB AJAX utilizando ASP.NET MVC, para realizar operaciones CRUD (crear, leer, actualizar y eliminar registros de una o más tablas), empleando una grilla Kendo UI (http://demos.telerik.com/kendo-ui/grid/index) y JQuery. Además, veremos cómo crear una base de datos y acceder a esta desde nuestra aplicación web utilizando el ORM EntityFramework.
El objetivo es mostrar lo simple que es crear una gran aplicación para realizar operaciones CRUD sobre nuestras tablas utilizado estas tecnologías, las cuales hacen que nuestra tarea de programación sea mucho más fácil y rápida.
Para esto comenzaremos en este primer post como crear una base de datos y probar la conexión desde nuestro aplicativo web consumiendo datos desde la base de datos SQL Server a través de EntityFramework y mostrar el resultado en una vista con Razor. Nuestra pequeña base de datos está representada por una tabla de Empleados y una tabla de Puestos, donde a cada empleado le corresponde un puesto dentro de una empresa hipotética.
En el segundo post construiremos un proxy para enmascarar las funcionalidades de CRUD, lo cual nos permite una serie de ventajas, como por ejemplo facilitar pasos a producción, simplicidad del código, realizar modificaciones con mucha facilidad, hacer una aplicación más modular, proveer abstracción, etc.
En el tercer post, y último de esta serie, veremos cómo crear nuestra grilla AJAX con Kendo UI y cada una de las operaciones CRUD utilizando WebAPI REST. Además daré algunos tips de cómo hacer que nuestra grilla sea más eficiente en cuanto a usabilidad y optimizar tiempos de respuestas, configurando esta grilla para que pueda paginar nuestros registros, así poder administrar tablas con gran cantidad de datos, filtrar y ordenar, con lo cual obtendremos una grilla al tope de la categoría.

Ver este video aquí

Primero: Crear base de datos e insertar los datos básicos necesarios

Con el siguiente script SQL crearemos una base de datos en nuestra instancia SQL Server:
USE [master]
GO

/****** Object:  Database [dbGridKendo]    Script Date: 22-10-2016 20:13:06 ******/
CREATE DATABASE [dbGridKendo]
 CONTAINMENT = NONE
 ON  PRIMARY
( NAME = N'dbGridKendo', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL13.SQLDEVELOP\MSSQL\DATA\dbGridKendo.mdf' , SIZE = 8192KB , MAXSIZE =UNLIMITED, FILEGROWTH = 65536KB )
 LOG ON
( NAME = N'dbGridKendo_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL13.SQLDEVELOP\MSSQL\DATA\dbGridKendo_log.ldf' , SIZE = 8192KB , MAXSIZE= 2048GB , FILEGROWTH = 65536KB )
GO

ALTER DATABASE [dbGridKendoSET COMPATIBILITY_LEVEL = 130
GO

IF (= FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))
begin
EXEC [dbGridKendo].[dbo].[sp_fulltext_database] @action = 'enable'
end
GO

ALTER DATABASE [dbGridKendoSET ANSI_NULL_DEFAULT OFF
GO

ALTER DATABASE [dbGridKendoSET ANSI_NULLS OFF
GO

ALTER DATABASE [dbGridKendoSET ANSI_PADDING OFF
GO

ALTER DATABASE [dbGridKendoSET ANSI_WARNINGS OFF
GO

ALTER DATABASE [dbGridKendoSET ARITHABORT OFF
GO

ALTER DATABASE [dbGridKendoSET AUTO_CLOSE OFF
GO

ALTER DATABASE [dbGridKendoSET AUTO_SHRINK OFF
GO

ALTER DATABASE [dbGridKendoSET AUTO_UPDATE_STATISTICS ON
GO

ALTER DATABASE [dbGridKendoSET CURSOR_CLOSE_ON_COMMIT OFF
GO

ALTER DATABASE [dbGridKendoSET CURSOR_DEFAULT  GLOBAL
GO

ALTER DATABASE [dbGridKendoSET CONCAT_NULL_YIELDS_NULL OFF
GO

ALTER DATABASE [dbGridKendoSET NUMERIC_ROUNDABORT OFF
GO

ALTER DATABASE [dbGridKendoSET QUOTED_IDENTIFIER OFF
GO

ALTER DATABASE [dbGridKendoSET RECURSIVE_TRIGGERS OFF
GO

ALTER DATABASE [dbGridKendoSET  DISABLE_BROKER
GO

ALTER DATABASE [dbGridKendoSET AUTO_UPDATE_STATISTICS_ASYNC OFF
GO

ALTER DATABASE [dbGridKendoSET DATE_CORRELATION_OPTIMIZATION OFF
GO

ALTER DATABASE [dbGridKendoSET TRUSTWORTHY OFF
GO

ALTER DATABASE [dbGridKendoSET ALLOW_SNAPSHOT_ISOLATION OFF
GO

ALTER DATABASE [dbGridKendoSET PARAMETERIZATION SIMPLE
GO

ALTER DATABASE [dbGridKendoSET READ_COMMITTED_SNAPSHOT OFF
GO

ALTER DATABASE [dbGridKendoSET HONOR_BROKER_PRIORITY OFF
GO

ALTER DATABASE [dbGridKendoSET RECOVERY FULL
GO

ALTER DATABASE [dbGridKendoSET  MULTI_USER
GO

ALTER DATABASE [dbGridKendoSET PAGE_VERIFY CHECKSUM 
GO

ALTER DATABASE [dbGridKendoSET DB_CHAINING OFF
GO

ALTER DATABASE [dbGridKendoSET FILESTREAM( NON_TRANSACTED_ACCESS = OFF )
GO

ALTER DATABASE [dbGridKendoSET TARGET_RECOVERY_TIME = 60 SECONDS
GO

ALTER DATABASE [dbGridKendoSET DELAYED_DURABILITY = DISABLED
GO

ALTER DATABASE [dbGridKendoSET QUERY_STORE = OFF
GO

USE [dbGridKendo]
GO

ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 0;
GO

ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET MAXDOP = PRIMARY;
GO

ALTER DATABASE SCOPED CONFIGURATION SET LEGACY_CARDINALITY_ESTIMATION = OFF;
GO

ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET LEGACY_CARDINALITY_ESTIMATION= PRIMARY;
GO

ALTER DATABASE SCOPED CONFIGURATION SET PARAMETER_SNIFFING = ON;
GO

ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET PARAMETER_SNIFFING = PRIMARY;
GO

ALTER DATABASE SCOPED CONFIGURATION SET QUERY_OPTIMIZER_HOTFIXES = OFF;
GO

ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET QUERY_OPTIMIZER_HOTFIXES =PRIMARY;
GO

ALTER DATABASE [dbGridKendoSET  READ_WRITE
GO

El siguiente paso es crear las tablas (no olvides mantener el siguiente orden de ejecución de los scripts):
Tabla Puestos
USE [dbGridKendo]
GO

/****** Object:  Table [dbo].[Puestos]    Script Date: 22-10-2016 20:14:35 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Puestos](
       [Id] [uniqueidentifierROWGUIDCOL  NOT NULL,
       [Nombre] [varchar](128) NULL,
       [Descripcion] [textNULL,
 CONSTRAINT [PK_PuestosPRIMARY KEY CLUSTERED
(
       [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

ALTER TABLE [dbo].[Puestos] ADD  CONSTRAINT [DF_Puestos_Id]  DEFAULT (newid()) FOR[Id]
GO

Tabla Empleados
USE [dbGridKendo]
GO

/****** Object:  Table [dbo].[Empleados]    Script Date: 22-10-2016 20:14:27 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Empleados](
       [Id] [uniqueidentifierROWGUIDCOL  NOT NULL,
       [Nombre] [varchar](128) NULL,
       [Apellido] [varchar](128) NULL,
       [Edad] [intNULL,
       [Descripcion] [textNULL,
       [Salario] [decimal](24, 2) NULL,
       [frk_Puesto] [uniqueidentifierNULL,
 CONSTRAINT [PK_EmpleadosPRIMARY KEY CLUSTERED
(
       [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

ALTER TABLE [dbo].[Empleados] ADD  CONSTRAINT [DF_Empleados_Id]  DEFAULT (newid())FOR [Id]
GO

ALTER TABLE [dbo].[Empleados]  WITH CHECK ADD  CONSTRAINT [FK_Empleados_Puestos]FOREIGN KEY([frk_Puesto])
REFERENCES [dbo].[Puestos] ([Id])
GO

ALTER TABLE [dbo].[Empleados] CHECK CONSTRAINT [FK_Empleados_Puestos]
GO

Insertar datos básicos en la tabla de Puestos
USE [dbGridKendo]
GO

INSERT INTO [dbo].[Puestos]
           ([Id]
           ,[Nombre]
           ,[Descripcion])
     VALUES
           (NEWID()
           ,'Desarrollador'
           ,'Encargado de programar los requerimientos funcionales.');

INSERT INTO [dbo].[Puestos]
           ([Id]
           ,[Nombre]
           ,[Descripcion])
     VALUES
           (NEWID()
           ,'Jefe de Proyectos'
           ,'Encargado de realizar estimación de esfuerzo y seguimiento a los proyectos.');

GO

Crear Proyecto Visual Studio 2015

Creamos una solución vacía Visual Studio (Blank Solution) con nombre Administrador:

Ahora crearemos un proyecto de tipo Librería (Class Library) dentro de nuestra solución vacía y lo llamaremos Administrador.Data. Este proyecto es para encapsular el acceso a datos y permitirnos crear un proxy en nuestro siguiente post:


Ahora crearemos un proyecto tipo Web (ASP.NET Web Application) que llamaremos Administrador.WebApp:

Seleccionamos la opción MVC, chequeamos la opción Web API y cambiamos el tipo de autenticación en Change Authentication (solo para simplificar el proyecto MVC):


Seleccionamos la opción No Authentication:

Agregamos la referencia Administrador.Data al proyecto Administrador.WebApp:



Limpiamos nuestro proyecto Administrador.Data eliminando el archivo Class1.cs:

A nuestro proyecto Administrador.Data agregamos la carpeta llamada Models:

En la carpeta Model de nuestro proyecto Administrador.Data agregamos un ítem ADO.NET Entity Data Model y lo llamamos ModelGrillaKendo:


Seleccionamos la opción EF Designer from database:

Configuramos la conexión a la instancia SQL Server haciendo click en New Connection:

Seleccione Data Source Microsoft SQL Server y click en Continue:

Seleccione el Server Name. Si la instancia es local puede utilizar o de directorio puede utilizar Windows Authenticacion y seleccione la base de datos. Clic en Ok (puedes hacer Test Connection antes para asegurar de que la conexión es la correcta):

El nombre de tu conexión debe ser dbGridKendoEntities. Si no estás usando contraseña solo da click en Next, si utilizas contraseña debes marcar la opción “Yes, include the sensitive…”:

Selecciona Entity Framework 6.x. Da click en Next:

Selecciona las tablas Empleados y Puestos, y da click en Finish:

Una vez generado el diagrama y las clases necesarias para realizar las operaciones en la base de datos podrás ver el siguiente resultado en el proyecto Administrador.Data:

Un paso importantísimo es copiar la cadena de conexión desde el archivo App.config del proyecto Administrador.Data al archivo Web.config en nuestro proyecto Administrador.WebApp:
App.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="dbGridKendoEntities"connectionString="metadata=res://*/Models.ModelGrillaKendo.csdl|res://*/Models.ModelGrillaKendo.ssdl|res://*/Models.ModelGrillaKendo.msl;provider=System.Data.SqlClient;providerconnection string=&quot;data source=MSINBK\SQLDEVELOP;initial catalog=dbGridKendo;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;"providerName="System.Data.EntityClient" />
  </connectionStrings>
</configuration>

Web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  …
  <connectionStrings>
    <add name="dbGridKendoEntities"connectionString="metadata=res://*/Models.ModelGrillaKendo.csdl|res://*/Models.ModelGrillaKendo.ssdl|res://*/Models.ModelGrillaKendo.msl;provider=System.Data.SqlClient;providerconnection string=&quot;data source=MSINBK\SQLDEVELOP;initial catalog=dbGridKendo;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;"providerName="System.Data.EntityClient" />
  </connectionStrings>
</configuration>

En nuestro proyecto Adminsitrador.WebApp dentro de la carpeta Models creamos una carpeta llama View:

Dentro de la carpeta recién creada agregamos la clase Puesto.cs:

Editamos el código del archivo Puesto.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web;

namespace Administrador.WepApp.Models.Views
{
    public class Puesto
    {
        public System.Guid Id { getset; }
        public string Nombre { getset; }

        [DisplayName("Descripción")]
        public string Descripcion { getset; }
    }
}


En nuestro proyecto Administrador.WebApp en la carpeta reference hacemos click derecho y seleccionamos Manage NuGet Packages…, y en Browse buscamos la librería EntityFramework y lo instalamos:



Hacemos click en Ok:

Hacemos click en I Accept:

Hacemos click en Yes to All:

Ahora vamos a la carpeta Controllers, y en archivo HomeController agregamos el siguiente método al final de la clase HomeController:
public ActionResult Puestos()
      {
            var listaPuestos = new List<Models.Views.Puesto>();
           
            using(var _db = new Administrador.Data.Models.dbGridKendoEntities())
            {
                foreach(var item in _db.Puestos)
                {
                    listaPuestos.Add
                    (
                        new Models.Views.Puesto
                        {
                            Descripcion = item.Descripcion,
                            Id = item.Id,
                            Nombre = item.Nombre
                        }
                    );
                }
            }

            return View(listaPuestos);
        }

Ahora en la carpeta Views/Home hacemos click derecho y agregamos una vista:

Llamamos a nuestra vista Puestos en el campo View Name y  damos click en Add:

Copiamos reemplazando el siguiente código en la vista:
@model IEnumerable<Administrador.WepApp.Models.Views.Puesto>
@{
    ViewBag.Title = "Listado de Puestos";
}
<h2>Listado de Puestos</h2>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Nombre)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Descripcion)
        </th>
    </tr>
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Nombre)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Descripcion)
        </td>      
    </tr>
}
</table>


 Ahora en la carpeta Views/Shared abrimos el archivo _Layout.cshtml y agregamos el siguiente código en la sección de links:
<div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("Home""Index""Home")</li>
                    <li>@Html.ActionLink("About""About""Home")</li>
                    <li>@Html.ActionLink("Contact""Contact""Home")</li>
                    <li>@Html.ActionLink("Puestos""Puestos""Home")</li>
                </ul>
            </div>

Para finalizar presionamos la tecla F5 y se levantara la aplicación:

Por ultimo hacemos click en el link Puestos y veremos los datos de la tabla Puestos:

Conclusión

Si bien este post se ve extenso la mayoría de sus pasos son realizar click en el botón Next de los Wizard que Visual Studio 2015 nos proporciona. Consumir datos con Entity Framework también es sencillo y familiar ya que es lo mismo que utilizar listas (List). Además actualizar el modelo de la base de datos se puede hacer solo con un par click como veremos en el siguiente post.

No hay comentarios:

Publicar un comentario