Compartir a través de


Crear un modelo con validaciones de regla de negocios

por Microsoft

Descargar PDF

Este es el paso 3 de un tutorial de la aplicación "NerdDinner" gratuito, que le guía por cómo crear una aplicación web pequeña, pero completa, con ASP.NET MVC 1.

En el paso 3 se muestra cómo crear un modelo que podamos usar para consultar y actualizar la base de datos de nuestra aplicación NerdDinner.

Si usa ASP.NET MVC 3, le recomendamos que siga los tutoriales Introducción a MVC 3 o MVC Music Store.

Paso 3 de NerdDinner: Creación del modelo

En un marco modelo-vista-controlador, el término "modelo" hace referencia a los objetos que representan los datos de la aplicación, así como a la lógica de dominio correspondiente que integra las reglas de validación y de negocios con ellos. El modelo es de muchas maneras el "corazón" de una aplicación basada en MVC y, como veremos más adelante, determina fundamentalmente su comportamiento.

El marco ASP.NET MVC admite el uso de cualquier tecnología de acceso a datos, y los desarrolladores pueden elegir entre una variedad de opciones de datos de .NET enriquecidas para implementar sus modelos, como: LINQ to Entities, LINQ to SQL, NHibernate, LLBLGen Pro, SubSonic, WilsonORM o simplemente DataReaders o DataSets de ADO.NET sin procesar.

En el caso de nuestra aplicación NerdDinner, vamos a usar LINQ to SQL para crear un modelo sencillo que se corresponda lo más posible con el diseño de la base de datos y agregue alguna lógica personalizada de reglas de validación y de negocios. A continuación, implementaremos una clase de repositorio que ayude a abstraer la implementación de persistencia de datos del resto de la aplicación y nos permita fácilmente realizar pruebas unitarias con ella.

LINQ to SQL

LINQ to SQL es un ORM (asignador relacional de objetos) que se distribuye como parte de .NET 3.5.

LINQ to SQL proporciona una manera sencilla de asignar tablas de base de datos a clases .NET con las que podemos crear código. Con nuestra aplicación NerdDinner, la usaremos para asignar las tablas Dinners y RSVP dentro de nuestra base de datos a las clases Dinner y RSVP. Las columnas de las tablas Dinners y RSVP corresponden a las propiedades de las clases Dinner y RSVP. Cada objeto Dinner y RSVP representará una fila distinta dentro de las tablas Dinners o RSVP de la base de datos.

LINQ to SQL nos permite evitar tener que construir manualmente instrucciones SQL para recuperar y actualizar objetos Dinner y RSVP con datos de base de datos. En cambio, definiremos las clases Dinner y RSVP, cómo se asignan a o desde la base de datos y las relaciones entre ellas. LINQ to SQL se encarga entonces de generar la lógica de ejecución de SQL adecuada que se usará en tiempo de ejecución cuando interactuemos y las usemos.

Podemos usar la compatibilidad con el lenguaje LINQ en VB y C# para escribir consultas expresivas que recuperen objetos Dinner y RSVP de la base de datos. Así se minimiza la cantidad de código de datos que debemos escribir y podemos crear aplicaciones realmente limpias.

Adición de clases de LINQ to SQL a un proyecto

Comenzaremos haciendo clic con el botón derecho en la carpeta "Models" del proyecto y seleccionaremos el comando de menú Agregar-> Nuevo elemento:

Screenshot of the Models folder. New item is highlighted. Models is highlighted and selected.

Se abrirá el cuadro de diálogo "Agregar nuevo elemento". Filtraremos por la categoría "Datos" y seleccionaremos la plantilla "Clases de LINQ to SQL" dentro de ella:

Screenshot of the Add New Item dialog. Data is highlighted. L I N Q to S Q L Classes is selected and highlighted.

Asignaremos al elemento el nombre "NerdDinner" y haremos clic en el botón "Agregar". Visual Studio agregará un archivo NerdDinner.dbml en nuestro directorio \Models y, luego, abrirá el diseñador relacional de objetos de LINQ to SQL:

Screenshot of the Nerd Dinner dialog box in Visual Studio. The Nerd Dinner dot d b m l file is selected.

Creación de clases de modelo de datos con LINQ to SQL

LINQ to SQL nos permite crear rápidamente clases de modelo de datos a partir del esquema de base de datos existente. Para ello, abriremos la nueva base de datos NerdDinner en el Explorador de servidores y seleccionaremos las tablas que queremos modelar en ella:

Screenshot of Server Explorer. Tables is expanded. Dinners and R S V P are highlighted.

A continuación, podemos arrastrar las tablas a la superficie del diseñador de LINQ to SQL. Al hacer esto, LINQ to SQL creará automáticamente clases Dinner y RSVP mediante el esquema de las tablas (donde las propiedades de clase se asignan a las columnas de la tabla de base de datos):

Screenshot of the Nerd Dinner dialog box. The Dinner and R S V P classes are shown.

De forma predeterminada, el diseñador de LINQ to SQL "pluraliza" automáticamente los nombres de tabla y columna cuando crea clases basadas en un esquema de base de datos. Por ejemplo: la tabla "Dinners" de nuestro ejemplo anterior dio como resultado una clase "Dinner". Esta nomenclatura de clases ayuda a que nuestros modelos sean coherentes con las convenciones de nomenclatura de .NET, y, por lo general, me gusta que el diseñador corrija esto fácilmente (especialmente al agregar muchas tablas). Si no le gusta el nombre de una clase o propiedad que genera el diseñador, siempre puede invalidarlo y cambiarlo por el que prefiera. Para ello, puede editar el nombre de la entidad o propiedad en línea dentro del diseñador o modificarlo mediante la cuadrícula de propiedades.

De forma predeterminada, el diseñador de LINQ to SQL también inspecciona las relaciones de clave principal y clave externa de las tablas y, en función de ellas, crea automáticamente las "asociaciones de relaciones" predeterminadas entre las distintas clases de modelos que crea. Por ejemplo, cuando arrastramos las tablas Dinners y RSVP al diseñador de LINQ to SQL, se infirió una asociación de relación uno a varios entre las dos basándose en el hecho de que la tabla RSVP tenía una clave externa a la tabla Dinners (esto se indica mediante la flecha del diseñador):

Screenshot of the Dinner and R S V P tables. An arrow is highlighted and pointing from the Dinner properties tree and R S V P properties tree.

La asociación anterior hará que LINQ to SQL agregue una propiedad "Dinner" fuertemente tipada a la clase RSVP que los desarrolladores pueden usar para acceder a la cena asociada a un RSVP determinado. También hará que la clase Dinner tenga una propiedad de colección "RSVPs" que permita a los desarrolladores recuperar y actualizar objetos RSVP asociados a una cena determinada.

A continuación, puede ver un ejemplo de IntelliSense en Visual Studio cuando creamos un nuevo objeto RSVP y lo agregamos a una colección RSVPs de Dinner. Observe cómo LINQ to SQL agregó automáticamente una colección "RSVPs" en el objeto Dinner:

Screenshot of the intellisense within Visual Studio. R S V Ps is highlighted.

Al agregar el objeto RSVP a la colección RSVPs de Dinner, se indica a LINQ to SQL que asocie una relación de clave externa entre la fila Dinner y el RSVP de nuestra base de datos:

Screenshot of the R S V P object and Dinner's R S V P collection.

Si no le gusta cómo el diseñador ha modelado o designado una asociación de tabla, puede invalidarla. Simplemente haga clic en la flecha de asociación dentro del diseñador y acceda a sus propiedades a través de la cuadrícula de propiedades para cambiar su nombre, eliminarla o modificarla. Sin embargo, en el caso de nuestra aplicación NerdDinner, las reglas de asociación predeterminadas funcionan bien para las clases de modelo de datos que estamos compilando y podemos usar simplemente el comportamiento predeterminado.

NerdDinnerDataContext (clase)

Visual Studio creará automáticamente clases de .NET que representan los modelos y las relaciones de base de datos definidas mediante el diseñador de LINQ to SQL. También se genera una clase DataContext de LINQ to SQL para cada archivo del diseñador de LINQ to SQL agregado a la solución. Dado que llamamos al elemento de clase de LINQ to SQL "NerdDinner", la clase DataContext creada se denominará "NerdDinnerDataContext". Esta clase NerdDinnerDataContext es la forma principal de interactuar con la base de datos.

Nuestra clase NerdDinnerDataContext expone dos propiedades : "Dinners" y "RSVPs", que representan las dos tablas que modelamos dentro de la base de datos. Podemos usar C# para escribir consultas LINQ en esas propiedades con el fin de consultar y recuperar objetos Dinner y RSVP de la base de datos.

En el código siguiente se muestra cómo crear una instancia de un objeto NerdDinnerDataContext y realizar una consulta LINQ en ella para obtener una secuencia de cenas que se producen en el futuro. Visual Studio proporciona la característica IntelliSense completa al escribir la consulta LINQ, y los objetos devueltos por ella están fuertemente tipados y también admiten IntelliSense:

Screenshot of Visual Studio. Description is highlighted.

Además de permitirnos consultar objetos Dinner y RSVP, una clase NerdDinnerDataContext también realiza automáticamente un seguimiento de los cambios que realizamos posteriormente en los objetos Dinner y RSVP que recuperamos mediante ella. Podemos usar esta funcionalidad para guardar fácilmente los cambios en la base de datos, sin tener que escribir código de actualización de SQL explícito.

Por ejemplo, el código siguiente muestra cómo usar una consulta LINQ para recuperar un único objeto Dinner de la base de datos, actualizar dos de las propiedades Dinner y, luego, guardar los cambios en la base de datos:

NerdDinnerDataContext db = new NerdDinnerDataContext();

// Retrieve Dinner object that reprents row with DinnerID of 1
Dinner dinner = db.Dinners.Single(d => d.DinnerID == 1);

// Update two properties on Dinner 
dinner.Title = "Changed Title";
dinner.Description = "This dinner will be fun";

// Persist changes to database
db.SubmitChanges();

El objeto NerdDinnerDataContext del código anterior realizó un seguimiento automático de los cambios de propiedad realizados en el objeto Dinner que recuperamos de él. Cuando llamemos al método "SubmitChanges()", ejecutará una instrucción SQL "UPDATE" adecuada en la base de datos para conservar los valores actualizados.

Creación de una clase DinnerRepository

En el caso de aplicaciones pequeñas, a veces es adecuado que los controladores funcionen directamente en una clase DataContext de LINQ to SQL e insertar consultas LINQ dentro de los controladores. Sin embargo, a medida que las aplicaciones se hacen más grandes, este enfoque se vuelve complicado de mantener y probar. También puede llevarnos a duplicar las mismas consultas LINQ en varios lugares.

Un enfoque que puede hacer que las aplicaciones sean más fáciles de mantener y probar es usar un patrón de "repositorio". Una clase de repositorio ayuda a encapsular la lógica de consulta y persistencia de datos y abstrae los detalles de implementación de la persistencia de datos de la aplicación. Además de hacer que el código de la aplicación sea más limpio, el uso de un patrón de repositorio puede facilitar el cambio de las implementaciones de almacenamiento de datos en el futuro y puede ayudar a facilitar la prueba unitaria de una aplicación sin necesidad de una base de datos real.

En el caso de nuestra aplicación NerdDinner, definiremos una clase DinnerRepository con la firma siguiente:

public class DinnerRepository {

    // Query Methods
    public IQueryable<Dinner> FindAllDinners();
    public IQueryable<Dinner> FindUpcomingDinners();
    public Dinner             GetDinner(int id);

    // Insert/Delete
    public void Add(Dinner dinner);
    public void Delete(Dinner dinner);

    // Persistence
    public void Save();
}

Nota: Más adelante en este capítulo, extraeremos una interfaz IDinnerRepository de esta clase y habilitaremos la inserción de dependencias con ella en nuestros controladores. Sin embargo, para comenzar, vamos a empezar a trabajar simple y llanamente con la clase DinnerRepository.

Para implementar esta clase, haremos clic con el botón derecho en nuestra carpeta "Models" y elegiremos el comando de menú Agregar->Nuevo elemento. En el cuadro de diálogo "Agregar nuevo elemento", seleccionaremos la plantilla "Clase" y asignaremos al archivo el nombre "DinnerRepository.cs":

Screenshot of the Models folder. Add New Item is highlighted.

A continuación, podemos implementar nuestra clase DinnerRepository con el código siguiente:

public class DinnerRepository {
 
    private NerdDinnerDataContext db = new NerdDinnerDataContext();

    //
    // Query Methods

    public IQueryable<Dinner> FindAllDinners() {
        return db.Dinners;
    }

    public IQueryable<Dinner> FindUpcomingDinners() {
        return from dinner in db.Dinners
               where dinner.EventDate > DateTime.Now
               orderby dinner.EventDate
               select dinner;
    }

    public Dinner GetDinner(int id) {
        return db.Dinners.SingleOrDefault(d => d.DinnerID == id);
    }

    //
    // Insert/Delete Methods

    public void Add(Dinner dinner) {
        db.Dinners.InsertOnSubmit(dinner);
    }

    public void Delete(Dinner dinner) {
        db.RSVPs.DeleteAllOnSubmit(dinner.RSVPs);
        db.Dinners.DeleteOnSubmit(dinner);
    }

    //
    // Persistence 

    public void Save() {
        db.SubmitChanges();
    }
}

Recuperación, actualización, inserción y eliminación mediante la clase DinnerRepository

Ahora que hemos creado nuestra clase DinnerRepository, echemos un vistazo a algunos ejemplos de código que muestran las tareas comunes que podemos hacer con ella:

Ejemplos de consultas

El código siguiente recupera una sola cena con el valor DinnerID:

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);

El código siguiente recupera todas las próximas cenas y las recorre en bucle:

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve all upcoming Dinners
var upcomingDinners = dinnerRepository.FindUpcomingDinners();

// Loop over each upcoming Dinner and print out its Title
foreach (Dinner dinner in upcomingDinners) {
   Response.Write("Title" + dinner.Title);
}

Ejemplos de inserción y actualización

En el código siguiente se muestra cómo agregar dos cenas nuevas. Las adiciones o modificaciones al repositorio no se confirman en la base de datos hasta que se llame al método "Save()" en él. LINQ to SQL encapsula automáticamente todos los cambios en una transacción de base de datos, por lo que, o se producen todos los cambios o ninguno cuando se guarda nuestro repositorio:

DinnerRepository dinnerRepository = new DinnerRepository();

// Create First Dinner
Dinner newDinner1 = new Dinner();
newDinner1.Title = "Dinner with Scott";
newDinner1.HostedBy = "ScotGu";
newDinner1.ContactPhone = "425-703-8072";

// Create Second Dinner
Dinner newDinner2 = new Dinner();
newDinner2.Title = "Dinner with Bill";
newDinner2.HostedBy = "BillG";
newDinner2.ContactPhone = "425-555-5151";

// Add Dinners to Repository
dinnerRepository.Add(newDinner1);
dinnerRepository.Add(newDinner2);

// Persist Changes
dinnerRepository.Save();

El código siguiente recupera un objeto Dinner existente y modifica dos de sus propiedades. Los cambios se confirman de nuevo en la base de datos cuando se llama al método "Save()" en nuestro repositorio:

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);

// Update Dinner properties
dinner.Title = "Update Title";
dinner.HostedBy = "New Owner";

// Persist changes
dinnerRepository.Save();

El código siguiente recupera una cena y, a continuación, le agrega una confirmación de asistencia, o RSVP. Para ello, se usa la colección RSVPs del objeto Dinner que LINQ to SQL creó para nosotros (porque hay una relación de clave principal y clave externa entre los dos en la base de datos). Este cambio se conserva de nuevo en la base de datos como una nueva fila de tabla RSVP cuando se llama al método "Save()" en el repositorio:

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);

// Create a new RSVP object
RSVP myRSVP = new RSVP();
myRSVP.AttendeeName = "ScottGu";

// Add RSVP to Dinner's RSVP Collection
dinner.RSVPs.Add(myRSVP);

// Persist changes
dinnerRepository.Save();

Ejemplo de eliminación

El código siguiente recupera un objeto Dinner existente y, a continuación, lo marca para eliminarlo. Cuando se llame al método "Save()" en el repositorio, confirmará la eliminación en la base de datos:

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);

// Mark dinner to be deleted
dinnerRepository.Delete(dinner);

// Persist changes
dinnerRepository.Save();

Integración de la lógica de reglas de validación y de negocios con clases de modelos

La integración de la lógica de reglas de validación y de negocios es una parte clave de cualquier aplicación que funcione con datos.

Validación de esquemas

Cuando se definen clases de modelos mediante el diseñador de LINQ to SQL, los tipos de datos de las propiedades de las clases de modelos de datos corresponden a los tipos de datos de la tabla de base de datos. Por ejemplo: si la columna "EventDate" de la tabla Dinners es "datetime", la clase de modelo de datos creada por LINQ to SQL será de tipo "DateTime" (que es un tipo de datos integrado de .NET). Esto significa que recibirá errores de compilación si intenta asignarle un tipo entero o booleano desde el código, y se generará un error automáticamente si intenta convertir implícitamente un tipo de cadena no válido en tiempo de ejecución.

LINQ to SQL también controlará automáticamente los valores de SQL de escape al usar cadenas, lo que le ayuda a protegerse frente a ataques por inyección de código SQL al usarlo.

Lógica de reglas de validación y de negocios

La validación del esquema es útil como primer paso, pero rara vez es suficiente. En la mayoría de los escenarios del mundo real, hace falta poder especificar una lógica de validación más completa que pueda abarcar varias propiedades, ejecutar código y, a menudo, tener conocimiento del estado de un modelo (por ejemplo, si se está creando/actualizando/eliminando, o se encuentra dentro de un estado específico del dominio, como "archivado"). Existen diferentes patrones y marcos que se pueden usar para definir y aplicar reglas de validación a clases de modelos, y hay varios marcos basados en .NET que se pueden usar para ayudar con esto. Puede usar prácticamente cualquiera de ellos en aplicaciones ASP.NET MVC.

Para los fines de nuestra aplicación NerdDinner, usaremos un patrón relativamente sencillo en el que expondremos una propiedad IsValid y un método GetRuleViolations() en nuestro objeto de modelo Dinner. La propiedad IsValid devolverá true o false en función de si las reglas de validación y de negocios son válidas. El método GetRuleViolations() devolverá una lista de errores de las reglas.

Implementaremos IsValid y GetRuleViolations() para nuestro modelo Dinner agregando una "clase parcial" a nuestro proyecto. Las clases parciales se pueden usar para agregar métodos, propiedades o eventos a las clases mantenidas por un diseñador de VS (como la clase Dinner generada por el diseñador de LINQ to SQL) y ayudan a evitar que la herramienta se enrede con nuestro código. Para agregar una nueva clase parcial a nuestro proyecto, haga clic con el botón derecho en la carpeta \Models y, a continuación, seleccione el comando de menú "Agregar nuevo elemento". Podemos elegir entonces la plantilla "Clase" en el cuadro de diálogo "Agregar nuevo elemento" y asignarle el nombre Dinner.cs.

Screenshot of the Models folder. Add New Item is selected. Dinner dot c s is written in the Add New Item dialog box.

Al hacer clic en el botón "Agregar", se agregará un archivo Dinner.cs al proyecto y se abrirá en el IDE. Podemos implementar entonces un marco de cumplimiento básico de reglas o validación mediante el código siguiente:

public partial class Dinner {

    public bool IsValid {
        get { return (GetRuleViolations().Count() == 0); }
    }

    public IEnumerable<RuleViolation> GetRuleViolations() {
        yield break;
    }

    partial void OnValidate(ChangeAction action) {
        if (!IsValid)
            throw new ApplicationException("Rule violations prevent saving");
    }
}

public class RuleViolation {

    public string ErrorMessage { get; private set; }
    public string PropertyName { get; private set; }

    public RuleViolation(string errorMessage, string propertyName) {
        ErrorMessage = errorMessage;
        PropertyName = propertyName;
    }
}

Algunas notas sobre el código anterior:

  • La clase Dinner va precedida de la palabra clave "partial", lo que significa que el código contenido en ella se combinará con la clase generada o mantenida por el diseñador de LINQ to SQL y compilará en una sola clase.
  • La clase RuleViolation es una clase auxiliar que agregaremos al proyecto que nos permite proporcionar más detalles sobre la infracción de una regla.
  • El método Dinner.GetRuleViolations() hace que nuestras reglas de validación y de negocios se evalúen (las implementaremos en breve). A continuación, devuelve una secuencia de objetos RuleViolation que proporcionan más detalles sobre los posibles errores de la regla.
  • La propiedad Dinner.IsValid proporciona una práctica propiedad auxiliar que indica si el objeto Dinner tiene infracciones de reglas activas. Los desarrolladores pueden comprobar esto por adelantado usando el objeto Dinner en cualquier momento (y no se genera una excepción).
  • El método parcial Dinner.OnValidate() es un enlace que proporciona LINQ to SQL que nos permite recibir una notificación cada vez que el objeto Dinner está a punto de conservarse dentro de la base de datos. Nuestra implementación anterior de OnValidate() garantiza que la cena no tenga infracciones de reglas antes de guardarla. Si está en un estado no válido, se genera una excepción, que hará que LINQ to SQL anule la transacción.

Este enfoque proporciona un marco sencillo en el que se pueden integrar las reglas de validación y de negocios. Por ahora, vamos a agregar las siguientes reglas a nuestro método GetRuleViolations():

public IEnumerable<RuleViolation> GetRuleViolations() {

    if (String.IsNullOrEmpty(Title))
        yield return new RuleViolation("Title required","Title");

    if (String.IsNullOrEmpty(Description))
        yield return new RuleViolation("Description required","Description");

    if (String.IsNullOrEmpty(HostedBy))
        yield return new RuleViolation("HostedBy required", "HostedBy");

    if (String.IsNullOrEmpty(Address))
        yield return new RuleViolation("Address required", "Address");

    if (String.IsNullOrEmpty(Country))
        yield return new RuleViolation("Country required", "Country");

    if (String.IsNullOrEmpty(ContactPhone))
        yield return new RuleViolation("Phone# required", "ContactPhone");

    if (!PhoneValidator.IsValidNumber(ContactPhone, Country))
        yield return new RuleViolation("Phone# does not match country", "ContactPhone");

    yield break;
}

Usamos la característica "yield return" de C# para devolver una secuencia de cualquier infracción de reglas. Las seis primeras comprobaciones de reglas anteriores simplemente exigen que las propiedades de cadena de nuestro objeto Dinner no puedan ser nulas ni vacías. La última regla es un poco más interesante y llama al método auxiliar PhoneValidator.IsValidNumber() que podemos agregar a nuestro proyecto para comprobar que el formato de número de ContactPhone coincide con el país o región de la cena.

Podemos usar la compatibilidad con expresiones regulares de NET para implementar esta validación de teléfono. A continuación, se muestra una implementación sencilla de PhoneValidator que podemos agregar a nuestro proyecto y que nos permite agregar comprobaciones de patrones Regex específicas del país o región:

public class PhoneValidator {

    static IDictionary<string, Regex> countryRegex = new Dictionary<string, Regex>() {
           { "USA", new Regex("^[2-9]\\d{2}-\\d{3}-\\d{4}$")},
           { "UK", new Regex("(^1300\\d{6}$)|(^1800|1900|1902\\d{6}$)|(^0[2|3|7|8]{1}[0-9]{8}$)|(^13\\d{4}$)|(^04\\d{2,3}\\d{6}$)")},
           { "Netherlands", new Regex("(^\\+[0-9]{2}|^\\+[0-9]{2}\\(0\\)|^\\(\\+[0-9]{2}\\)\\(0\\)|^00[0-9]{2}|^0)([0-9]{9}$|[0-9\\-\\s]{10}$)")},
    };

    public static bool IsValidNumber(string phoneNumber, string country) {

        if (country != null && countryRegex.ContainsKey(country))
            return countryRegex[country].IsMatch(phoneNumber);
        else
            return false;
    }

    public static IEnumerable<string> Countries {
        get {
            return countryRegex.Keys;
        }
    }
}

Control de infracciones de la lógica de validación y negocios

Ahora que hemos agregado el código anterior de reglas de validación o de negocios, cada vez que intentemos crear o actualizar una cena, nuestras reglas de lógica de validación se evaluarán y aplicarán.

Los desarrolladores pueden escribir código como el siguiente para determinar por adelantado si un objeto Dinner es válido y recuperar una lista de todas las infracciones en él sin generar ninguna excepción:

Dinner dinner = dinnerRepository.GetDinner(5);

dinner.Country = "USA";
dinner.ContactPhone = "425-555-BOGUS";

if (!dinner.IsValid) {

    var errors = dinner.GetRuleViolations();
    
    // do something to fix the errors
}

Si intentamos guardar una cena en un estado no válido, se generará una excepción cuando llamemos al método Save() en DinnerRepository. El motivo es que LINQ to SQL llama automáticamente al método parcial Dinner.OnValidate() antes de guardar los cambios de Dinner y agregamos código a Dinner.OnValidate() para generar una excepción si existen infracciones de reglas en Dinner. Podemos detectar esta excepción y recuperar de forma reactiva una lista de infracciones para corregirlas:

Dinner dinner = dinnerRepository.GetDinner(5);

try {

    dinner.Country = "USA";
    dinner.ContactPhone = "425-555-BOGUS";

    dinnerRepository.Save();
}
catch {

    var errors = dinner.GetRuleViolations();

    // do something to fix errors
}

Dado que nuestras reglas de validación y de negocios se implementan dentro de nuestra capa de modelo y no dentro de la capa de interfaz de usuario, se aplicarán y usarán en todos los escenarios de nuestra aplicación. Más adelante podemos cambiar o agregar reglas de negocios y hacer que las respete todo el código que funcione con nuestros objetos Dinner.

Tener la flexibilidad de cambiar reglas de negocios en un solo lugar, sin que estos cambios se propaguen por toda la lógica de aplicación y de la interfaz de usuario, es señal de una aplicación bien escrita, y un beneficio que un marco de trabajo MVC ayuda a fomentar.

siguiente paso

Ahora tenemos un modelo que podemos usar para consultar y actualizar nuestra base de datos.

Seguidamente, agregaremos algunos controladores y vistas al proyecto que podemos usar para crear una experiencia de interfaz de usuario HTML en torno a él.