Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
da Microsoft
In questa sesta iterazione si aggiungono nuove funzionalità all'applicazione scrivendo prima unit test e scrivendo codice rispetto agli unit test. In questa iterazione si aggiungono gruppi di contatti.
Creazione di un'applicazione di gestione contatti ASP.NET MVC (C#)
In questa serie di esercitazioni viene creata un'intera applicazione Di gestione contatti dall'inizio alla fine. L'applicazione Contact Manager consente di archiviare informazioni di contatto, nomi, numeri di telefono e indirizzi di posta elettronica, per un elenco di persone.
Viene creata l'applicazione su più iterazioni. Con ogni iterazione, si migliora gradualmente l'applicazione. L'obiettivo di questo approccio a più iterazioni è quello di consentire di comprendere il motivo di ogni modifica.
Iterazione #1: creare l'applicazione. Nella prima iterazione, creiamo Contact Manager nel modo più semplice possibile. È possibile aggiungere il supporto per le operazioni di base del database: creare, leggere, aggiornare ed eliminare (CRUD).
Iterazione #2: rendere l'applicazione più bella. In questa iterazione si migliora l'aspetto dell'applicazione modificando la pagina master della visualizzazione MVC predefinita ASP.NET e il foglio di stile a catena.
Iterazione #3 - Aggiungere la convalida del modulo. Nella terza iterazione si aggiunge la convalida dei moduli di base. Impedisciamo agli utenti di inviare un modulo senza completare i campi del modulo obbligatori. Si convalidano anche gli indirizzi di posta elettronica e i numeri di telefono.
Iterazione #4: rendere l'applicazione in modo libero. In questa quarta iterazione si sfruttano diversi modelli di progettazione software per semplificare la gestione e la modifica dell'applicazione Contact Manager. Ad esempio, l'applicazione viene refactoring per usare il modello repository e il modello di inserimento delle dipendenze.
Iterazione #5 - Creare unit test. Nella quinta iterazione, l'applicazione semplifica la gestione e la modifica dell'applicazione aggiungendo unit test. È possibile simulare le classi del modello di dati e compilare unit test per i controller e la logica di convalida.
Iterazione #6 : usare lo sviluppo basato su test. In questa sesta iterazione si aggiungono nuove funzionalità all'applicazione scrivendo prima unit test e scrivendo codice rispetto agli unit test. In questa iterazione si aggiungono gruppi di contatti.
Iterazione 7 - Aggiungere funzionalità Ajax. Nella settima iterazione si migliora la velocità di risposta e le prestazioni dell'applicazione aggiungendo il supporto per Ajax.
Iterazione
Nell'iterazione precedente dell'applicazione Contact Manager sono stati creati unit test per fornire una rete di sicurezza per il codice. La motivazione per la creazione degli unit test è stata quella di rendere il codice più resiliente alla modifica. Con unit test sul posto, è possibile apportare tutte le modifiche apportate al codice e sapere immediatamente se è stata interrotta la funzionalità esistente.
In questa iterazione vengono usati unit test per uno scopo completamente diverso. In questa iterazione vengono usati unit test come parte di una filosofia di progettazione dell'applicazione denominata sviluppo basato su test. Quando si pratica lo sviluppo basato su test, si scrivono prima i test e quindi si scrive codice sui test.
Più precisamente, quando si pratica lo sviluppo basato su test, sono necessari tre passaggi per la creazione di codice (Red/ Green/Refactor):
- Scrivere un unit test che ha esito negativo (Rosso)
- Scrivere codice che supera l'unit test (Verde)
- Eseguire il refactoring del codice (Refactor)
Prima di tutto, si scrive l'unit test. L'unit test deve esprimere l'intenzione di come si prevede che il codice si comporti. Quando si crea prima di tutto l'unit test, l'unit test deve avere esito negativo. Il test deve non riuscire perché non è ancora stato scritto alcun codice dell'applicazione che soddisfa il test.
Successivamente, si scrive un codice sufficiente per il passaggio dell'unit test. L'obiettivo è scrivere il codice nel modo più pigre, sloppiest e più veloce possibile. Non è consigliabile perdere tempo pensando all'architettura dell'applicazione. È invece consigliabile concentrarsi sulla scrittura della quantità minima di codice necessaria per soddisfare l'intenzione espressa dall'unit test.
Infine, dopo aver scritto un codice sufficiente, è possibile tornare indietro e prendere in considerazione l'architettura complessiva dell'applicazione. In questo passaggio si riscrive (refactor) il codice sfruttando i modelli di progettazione software, ad esempio il modello di repository, in modo che il codice sia più gestibile. È possibile riscrivere il codice senza paura in questo passaggio perché il codice è coperto dagli unit test.
Ci sono molti vantaggi che derivano dalla pratica dello sviluppo guidato dai test. Prima di tutto, lo sviluppo basato su test impone di concentrarsi sul codice che effettivamente deve essere scritto. Poiché si è costantemente concentrati sulla scrittura di codice sufficiente per passare un determinato test, non si è impedito di vagare nelle weeds e scrivere grandi quantità di codice che non si userà mai.
In secondo luogo, una metodologia di progettazione "test first" impone di scrivere codice dal punto di vista del modo in cui verrà usato il codice. In altre parole, quando si pratica lo sviluppo basato su test, si scrivono costantemente i test dal punto di vista dell'utente. Pertanto, lo sviluppo basato su test può comportare API più pulite e più comprensibili.
Infine, lo sviluppo basato su test forza la scrittura di unit test come parte del normale processo di scrittura di un'applicazione. Quando una scadenza del progetto si avvicina, i test sono in genere la prima cosa che va fuori dalla finestra. Quando si pratica lo sviluppo basato su test, d'altra parte, è più probabile che si sia più virtuosi per scrivere unit test perché lo sviluppo basato su test rende unit test centrali al processo di compilazione di un'applicazione.
Nota
Per altre informazioni sullo sviluppo basato sui test, ti consigliamo di leggere il libro Michael Feathers Working Effectively with Legacy Code.
In questa iterazione si aggiunge una nuova funzionalità all'applicazione Contact Manager. È stato aggiunto il supporto per i gruppi di contatti. È possibile usare i gruppi di contatti per organizzare i contatti in categorie, ad esempio gruppi Business e Friend.
Questa nuova funzionalità verrà aggiunta all'applicazione seguendo un processo di sviluppo guidato dal test. Verranno scritti prima gli unit test e verrà scritto tutto il codice rispetto a questi test.
Cosa viene testato
Come illustrato nell'iterazione precedente, in genere non si scrivono unit test per la logica di accesso ai dati o la logica di visualizzazione. Non si scrivono unit test per la logica di accesso ai dati perché l'accesso a un database è un'operazione relativamente lenta. Non si scrivono unit test per la logica di visualizzazione perché l'accesso a una visualizzazione richiede l'esecuzione di un server Web che è un'operazione relativamente lenta. Non è consigliabile scrivere un unit test a meno che il test non possa essere eseguito più e più velocemente
Poiché lo sviluppo basato su test è basato su unit test, si concentra inizialmente sulla scrittura di controller e logica di business. È possibile evitare di toccare il database o le visualizzazioni. Il database non verrà modificato o creato fino alla fine di questa esercitazione. Iniziamo con ciò che può essere testato.
Creazione di storie utente
Quando si pratica lo sviluppo basato su test, si inizia sempre scrivendo un test. Ciò genera immediatamente la domanda: come si decide quale test scrivere prima? Per rispondere a questa domanda, è consigliabile scrivere un set di storie utente.
Una storia utente è una descrizione molto breve (in genere una frase) di un requisito software. Deve essere una descrizione non tecnica di un requisito scritto dal punto di vista dell'utente.
Ecco il set di storie utente che descrivono le funzionalità richieste dalla nuova funzionalità del gruppo di contatti:
- L'utente può visualizzare un elenco di gruppi di contatti.
- L'utente può creare un nuovo gruppo di contatti.
- L'utente può eliminare un gruppo di contatti esistente.
- L'utente può selezionare un gruppo di contatti durante la creazione di un nuovo contatto.
- L'utente può selezionare un gruppo di contatti durante la modifica di un contatto esistente.
- Viene visualizzato un elenco di gruppi di contatti nella visualizzazione Indice.
- Quando un utente fa clic su un gruppo di contatti, viene visualizzato un elenco di contatti corrispondenti.
Si noti che questo elenco di storie utente è completamente comprensibile da un cliente. Non vi è alcuna menzione dei dettagli dell'implementazione tecnica.
Durante il processo di creazione dell'applicazione, il set di storie utente può diventare più raffinato. È possibile suddividere una storia utente in più storie (requisiti). Ad esempio, è possibile decidere che la creazione di un nuovo gruppo di contatti deve comportare la convalida. L'invio di un gruppo di contatti senza un nome deve restituire un errore di convalida.
Dopo aver creato un elenco di storie utente, è possibile scrivere il primo unit test. Si inizierà creando un unit test per visualizzare l'elenco dei gruppi di contatti.
Elencare i gruppi di contatti
La prima storia utente è che un utente deve essere in grado di visualizzare un elenco di gruppi di contatti. Dobbiamo esprimere questa storia con un test.
Creare un nuovo unit test facendo clic con il pulsante destro del mouse sulla cartella Controller nel progetto ContactManager.Test, selezionando Aggiungi, Nuovo test e selezionando il modello unit test (vedere la figura 1). Assegnare al nuovo unit test GroupControllerTest.cs e fare clic sul pulsante OK .
Figura 01: Aggiunta dell'unit test GroupControllerTest(Fare clic per visualizzare l'immagine a dimensioni complete)
Il primo unit test è contenuto nell'elenco 1. Questo test verifica che il metodo Index() del controller Group restituisce un set di Gruppi. Il test verifica che una raccolta di gruppi venga restituita nei dati della visualizzazione.
Elenco 1 - Controller\GroupControllerTest.cs
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Web.Mvc;
using ContactManager.Models;
namespace ContactManager.Tests.Controllers
{
[TestClass]
public class GroupControllerTest
{
[TestMethod]
public void Index()
{
// Arrange
var controller = new GroupController();
// Act
var result = (ViewResult)controller.Index();
// Assert
Assert.IsInstanceOfType(result.ViewData.Model, typeof(IEnumerable));
}
}
}
Quando si digita prima il codice nell'elenco 1 in Visual Studio, si otterranno molte righe rosse. Non sono state create le classi GroupController o Group.
A questo punto, non è anche possibile compilare l'applicazione in modo da non poter eseguire il primo unit test. È buono. Ciò conta come test non riuscito. Pertanto, è ora disponibile l'autorizzazione per iniziare a scrivere il codice dell'applicazione. È necessario scrivere codice sufficiente per eseguire il test.
La classe controller group in List 2 contiene il minimo di codice necessario per superare l'unit test. L'azione Index() restituisce un elenco codificato statico di Gruppi (la classe Group è definita in List 3).
Elenco 2 - Controller\GroupController.cs
using System.Collections.Generic;
using System.Web.Mvc;
using ContactManager.Models;
namespace ContactManager.Controllers
{
public class GroupController : Controller
{
public ActionResult Index()
{
var groups = new List();
return View(groups);
}
}
}
Elenco 3 - Modelli\Group.cs
namespace ContactManager.Models
{
public class Group
{
}
}
Dopo aver aggiunto le classi GroupController e Group al progetto, il primo unit test viene completato correttamente (vedere la figura 2). Abbiamo svolto il lavoro minimo necessario per superare il test. È il momento di festeggiare.
Figura 02: Successo! (Fare clic per visualizzare l'immagine full-size)
Creazione di gruppi di contatti
Ora possiamo passare alla seconda storia utente. Dobbiamo essere in grado di creare nuovi gruppi di contatti. Dobbiamo esprimere questa intenzione con un test.
Il test in List 4 verifica che la chiamata al metodo Create() con un nuovo gruppo aggiunge il gruppo all'elenco di Gruppi restituiti dal metodo Index(). In altre parole, se creo un nuovo gruppo, dovrei essere in grado di recuperare il nuovo gruppo dall'elenco di Gruppi restituiti dal metodo Index().
Elenco 4 - Controller\GroupControllerTest.cs
[TestMethod]
public void Create()
{
// Arrange
var controller = new GroupController();
// Act
var groupToCreate = new Group();
controller.Create(groupToCreate);
// Assert
var result = (ViewResult)controller.Index();
var groups = (IEnumerable<Group>)result.ViewData.Model;
CollectionAssert.Contains(groups.ToList(), groupToCreate);
}
Il test nell'elenco 4 chiama il metodo Create() del controller di gruppo con un nuovo gruppo di contatti. Successivamente, il test verifica che la chiamata al metodo Index() del controller di gruppo restituisce il nuovo gruppo nei dati di visualizzazione.
Il controller di gruppo modificato nell'elenco 5 contiene il minimo di modifiche necessarie per superare il nuovo test.
Elenco 5 - Controller\GroupController.cs
using System.Collections.Generic;
using System.Web.Mvc;
using ContactManager.Models;
using System.Collections;
namespace ContactManager.Controllers
{
public class GroupController : Controller
{
private IList<Group> _groups = new List<Group>();
public ActionResult Index()
{
return View(_groups);
}
public ActionResult Create(Group groupToCreate)
{
_groups.Add(groupToCreate);
return RedirectToAction("Index");
}
}
}
Il controller di gruppo nell'elenco 5 ha una nuova azione Create(). Questa azione aggiunge un gruppo a una raccolta di gruppi. Si noti che l'azione Index() è stata modificata per restituire il contenuto della raccolta di Gruppi.
Ancora una volta, è stata eseguita la quantità minima minima di lavoro necessaria per superare l'unit test. Dopo aver apportato queste modifiche al controller di gruppo, tutte le unità test superano.
Aggiunta della convalida
Questo requisito non è stato indicato in modo esplicito nella storia utente. Tuttavia, è ragionevole richiedere che un gruppo abbia un nome. In caso contrario, l'organizzazione dei contatti in Gruppi non sarebbe molto utile.
L'elenco 6 contiene un nuovo test che esprime questa intenzione. Questo test verifica che il tentativo di creare un gruppo senza fornire un nome genera un messaggio di errore di convalida nello stato del modello.
Elenco 6 - Controller\GroupControllerTest.cs
[TestMethod]
public void CreateRequiredName()
{
// Arrange
var controller = new GroupController();
// Act
var groupToCreate = new Group();
groupToCreate.Name = String.Empty;
var result = (ViewResult)controller.Create(groupToCreate);
// Assert
var error = result.ViewData.ModelState["Name"].Errors[0];
Assert.AreEqual("Name is required.", error.ErrorMessage);
}
Per soddisfare questo test, è necessario aggiungere una proprietà Name alla classe Group (vedere Elenco 7). Inoltre, è necessario aggiungere un piccolo bit di logica di convalida all'azione Create() del controller di gruppo (vedere Elenco 8).
Elenco 7 - Modelli\Group.cs
namespace ContactManager.Models
{
public class Group
{
public string Name { get; set; }
}
}
Elenco 8 - Controller\GroupController.cs
public ActionResult Create(Group groupToCreate)
{
// Validation logic
if (groupToCreate.Name.Trim().Length == 0)
{
ModelState.AddModelError("Name", "Name is required.");
return View("Create");
}
// Database logic
_groups.Add(groupToCreate);
return RedirectToAction("Index");
}
Si noti che l'azione Create() del controller di gruppo contiene ora sia la convalida che la logica del database. Attualmente, il database usato dal controller di gruppo è costituito da niente di più di una raccolta in memoria.
Tempo di refactoring
Il terzo passaggio in Red/Green/Refactor è la parte Refactor. A questo punto, è necessario tornare dal codice e considerare come eseguire il refactoring dell'applicazione per migliorare la progettazione. La fase di refactoring è la fase in cui pensiamo duramente al modo migliore di implementare principi e modelli di progettazione software.
È possibile modificare il codice in qualsiasi modo che si sceglie di migliorare la progettazione del codice. Abbiamo una rete di sicurezza di unit test che impediscono di interrompere le funzionalità esistenti.
A questo punto, il nostro controller di gruppo è un disordine dal punto di vista della buona progettazione software. Il controller di gruppo contiene un disordine di convalida e codice di accesso ai dati. Per evitare di violare il principio di responsabilità singola, dobbiamo separare queste preoccupazioni in classi diverse.
La classe controller group refactored è contenuta nell'elenco 9. Il controller è stato modificato per usare il livello di servizio ContactManager. Si tratta dello stesso livello di servizio usato con il controller di contatto.
L'elenco 10 contiene i nuovi metodi aggiunti al livello di servizio ContactManager per supportare la convalida, l'elenco e la creazione di gruppi. L'interfaccia IContactManagerService è stata aggiornata per includere i nuovi metodi.
L'elenco 11 contiene una nuova classe FakeContactManagerRepository che implementa l'interfaccia IContactManagerRepository. A differenza della classe EntityContactManagerRepository che implementa anche l'interfaccia IContactManagerRepository, la nuova classe FakeContactManagerRepository non comunica con il database. La classe FakeContactManagerRepository usa una raccolta in memoria come proxy per il database. Questa classe verrà usata negli unit test come livello di repository falso.
Elenco 9 - Controller\GroupController.cs
using System.Web.Mvc;
using ContactManager.Models;
namespace ContactManager.Controllers
{
public class GroupController : Controller
{
private IContactManagerService _service;
public GroupController()
{
_service = new ContactManagerService(new ModelStateWrapper(this.ModelState));
}
public GroupController(IContactManagerService service)
{
_service = service;
}
public ActionResult Index()
{
return View(_service.ListGroups());
}
public ActionResult Create(Group groupToCreate)
{
if (_service.CreateGroup(groupToCreate))
return RedirectToAction("Index");
return View("Create");
}
}
}
Elenco 10 - Controller\ContactManagerService.cs
public bool ValidateGroup(Group groupToValidate)
{
if (groupToValidate.Name.Trim().Length == 0)
_validationDictionary.AddError("Name", "Name is required.");
return _validationDictionary.IsValid;
}
public bool CreateGroup(Group groupToCreate)
{
// Validation logic
if (!ValidateGroup(groupToCreate))
return false;
// Database logic
try
{
_repository.CreateGroup(groupToCreate);
}
catch
{
return false;
}
return true;
}
public IEnumerable<Group> ListGroups()
{
return _repository.ListGroups();
}
Elenco 11 - Controller\FakeContactManagerRepository.cs
using System;
using System.Collections.Generic;
using ContactManager.Models;
namespace ContactManager.Tests.Models
{
public class FakeContactManagerRepository : IContactManagerRepository
{
private IList<Group> _groups = new List<Group>();
#region IContactManagerRepository Members
// Group methods
public Group CreateGroup(Group groupToCreate)
{
_groups.Add(groupToCreate);
return groupToCreate;
}
public IEnumerable<Group> ListGroups()
{
return _groups;
}
// Contact methods
public Contact CreateContact(Contact contactToCreate)
{
throw new NotImplementedException();
}
public void DeleteContact(Contact contactToDelete)
{
throw new NotImplementedException();
}
public Contact EditContact(Contact contactToEdit)
{
throw new NotImplementedException();
}
public Contact GetContact(int id)
{
throw new NotImplementedException();
}
public IEnumerable<Contact> ListContacts()
{
throw new NotImplementedException();
}
#endregion
}
}
La modifica dell'interfaccia IContactManagerRepository richiede l'uso per implementare i metodi CreateGroup() e ListGroups() nella classe EntityContactManagerRepository. Il modo più veloce e più veloce per eseguire questa operazione consiste nell'aggiungere metodi stub simili al seguente:
public Group CreateGroup(Group groupToCreate)
{
throw new NotImplementedException();
}
public IEnumerable<Group> ListGroups()
{
throw new NotImplementedException();
}
Infine, queste modifiche alla progettazione dell'applicazione richiedono di apportare alcune modifiche ai unit test. È ora necessario usare FakeContactManagerRepository durante l'esecuzione degli unit test. La classe GroupControllerTest aggiornata è contenuta nell'elenco 12.
Elenco 12 - Controller\GroupControllerTest.cs
using System.Collections.Generic;
using System.Web.Mvc;
using ContactManager.Controllers;
using ContactManager.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections;
using System.Linq;
using System;
using ContactManager.Tests.Models;
namespace ContactManager.Tests.Controllers
{
[TestClass]
public class GroupControllerTest
{
private IContactManagerRepository _repository;
private ModelStateDictionary _modelState;
private IContactManagerService _service;
[TestInitialize]
public void Initialize()
{
_repository = new FakeContactManagerRepository();
_modelState = new ModelStateDictionary();
_service = new ContactManagerService(new ModelStateWrapper(_modelState), _repository);
}
[TestMethod]
public void Index()
{
// Arrange
var controller = new GroupController(_service);
// Act
var result = (ViewResult)controller.Index();
// Assert
Assert.IsInstanceOfType(result.ViewData.Model, typeof(IEnumerable));
}
[TestMethod]
public void Create()
{
// Arrange
var controller = new GroupController(_service);
// Act
var groupToCreate = new Group();
groupToCreate.Name = "Business";
controller.Create(groupToCreate);
// Assert
var result = (ViewResult)controller.Index();
var groups = (IEnumerable)result.ViewData.Model;
CollectionAssert.Contains(groups.ToList(), groupToCreate);
}
[TestMethod]
public void CreateRequiredName()
{
// Arrange
var controller = new GroupController(_service);
// Act
var groupToCreate = new Group();
groupToCreate.Name = String.Empty;
var result = (ViewResult)controller.Create(groupToCreate);
// Assert
var error = _modelState["Name"].Errors[0];
Assert.AreEqual("Name is required.", error.ErrorMessage);
}
}
}
Dopo aver apportato tutte queste modifiche, ancora una volta tutte le unità test superano. È stato completato l'intero ciclo di Red/Green/Refactor. Sono state implementate le prime due storie utente. Sono ora disponibili unit test per i requisiti espressi nelle storie utente. L'implementazione del resto delle storie utente comporta la ripetizione dello stesso ciclo di Red/Green/Refactor.
Modifica del database
Purtroppo, anche se abbiamo soddisfatto tutti i requisiti espressi dai nostri unit test, il nostro lavoro non viene fatto. È comunque necessario modificare il database.
È necessario creare una nuova tabella di database del gruppo. Seguire questa procedura:
- Nella finestra Esplora server fare clic con il pulsante destro del mouse sulla cartella Tabelle e selezionare l'opzione di menu Aggiungi nuova tabella.
- Immettere le due colonne descritte di seguito nella tabella Designer.
- Contrassegnare la colonna ID come chiave primaria e colonna Identity.
- Salvare la nuova tabella con il nome Gruppi facendo clic sull'icona del floppy.
| Nome colonna | Tipo di dati | Consenti valori NULL |
|---|---|---|
| ID | INT | Falso |
| Nome | nvarchar(50) | Falso |
Successivamente, è necessario eliminare tutti i dati dalla tabella Contatti (in caso contrario, non sarà possibile creare una relazione tra le tabelle Contatti e Gruppi). Seguire questa procedura:
- Fare clic con il pulsante destro del mouse sulla tabella Contatti e selezionare l'opzione di menu Mostra dati tabella.
- Eliminare tutte le righe.
È quindi necessario definire una relazione tra la tabella di database Gruppi e la tabella di database Contatti esistente. Seguire questa procedura:
- Fare doppio clic sulla tabella Contatti nella finestra Esplora server per aprire l'Designer Tabella.
- Aggiungere una nuova colonna integer alla tabella Contacts denominata GroupId.
- Fare clic sul pulsante Relazione per aprire la finestra di dialogo Relazioni chiave esterna (vedere la figura 3).
- Fare clic sul pulsante Aggiungi.
- Fare clic sul pulsante con i puntini di sospensione visualizzato accanto al pulsante Specifica tabella e colonne.
- Nella finestra di dialogo Tabelle e colonne selezionare Gruppi come tabella chiave primaria e ID come colonna chiave primaria. Selezionare Contatti come tabella chiave esterna e GroupId come colonna chiave esterna (vedere la figura 4). Fare clic sul pulsante OK.
- In INSERT e UPDATE Specification (Specifica INSERT e UPDATE) selezionare il valore Cascade for Delete Rule (Elimina regola).
- Fare clic sul pulsante Chiudi per chiudere la finestra di dialogo Relazioni chiave esterna.
- Fare clic sul pulsante Salva per salvare le modifiche apportate alla tabella Contatti.
Figura 03: Creazione di una relazione di tabella di database (Fare clic per visualizzare l'immagine a dimensioni complete)
Figura 04: Specifica delle relazioni di tabella(Fare clic per visualizzare l'immagine full-size)
Aggiornamento del modello di dati
È quindi necessario aggiornare il modello di dati per rappresentare la nuova tabella di database. Seguire questa procedura:
- Fare doppio clic sul file ContactManagerModel.edmx nella cartella Modelli per aprire l'Designer entità.
- Fare clic con il pulsante destro del mouse sull'area di Designer e selezionare l'opzione di menu Aggiorna modello da Database.
- Nella Procedura guidata aggiornamento selezionare la tabella Gruppi e fare clic sul pulsante Fine (vedere La figura 5).
- Fare clic con il pulsante destro del mouse sull'entità Gruppi e selezionare l'opzione di menu Rinomina. Modificare il nome dell'entità Gruppi in Group (singolare).
- Fare clic con il pulsante destro del mouse sulla proprietà di spostamento Gruppi visualizzata nella parte inferiore dell'entità Contact. Modificare il nome della proprietà di spostamento Gruppi in Group (singolare).
Figura 05: Aggiornamento di un modello Entity Framework dal database(Fare clic per visualizzare l'immagine a dimensioni complete)
Al termine di questi passaggi, il modello di dati rappresenta sia le tabelle Contatti che Gruppi. L'entità Designer deve visualizzare entrambe le entità (vedere la figura 6).
Figura 06: Entità Designer visualizzazione gruppo e contatto(Fare clic per visualizzare l'immagine a dimensioni complete)
Creazione delle classi di repository
Successivamente, è necessario implementare la classe repository. Nel corso di questa iterazione, sono stati aggiunti diversi nuovi metodi all'interfaccia IContactManagerRepository durante la scrittura del codice per soddisfare i unit test. La versione finale dell'interfaccia IContactManagerRepository è contenuta nell'elenco 14.
Elenco 14 - Modelli\IContactManagerRepository.cs
using System.Collections.Generic;
namespace ContactManager.Models
{
public interface IContactManagerRepository
{
// Contact methods
Contact CreateContact(int groupId, Contact contactToCreate);
void DeleteContact(Contact contactToDelete);
Contact EditContact(int groupId, Contact contactToEdit);
Contact GetContact(int id);
// Group methods
Group CreateGroup(Group groupToCreate);
IEnumerable<Group> ListGroups();
Group GetGroup(int groupId);
Group GetFirstGroup();
void DeleteGroup(Group groupToDelete);
}
}
Non sono stati effettivamente implementati metodi correlati all'uso dei gruppi di contatti. Attualmente, la classe EntityContactManagerRepository include metodi stub per ognuno dei metodi del gruppo di contatti elencati nell'interfaccia IContactManagerRepository. Ad esempio, il metodo ListGroups() è attualmente simile al seguente:
public IEnumerable<Group> ListGroups()
{
throw new NotImplementedException();
}
I metodi stub ci hanno abilitato per compilare l'applicazione e superare gli unit test. Tuttavia, è ora il momento di implementare effettivamente questi metodi. La versione finale della classe EntityContactManagerRepository è contenuta nell'elenco 13.
Elenco 13 - Modelli\EntityContactManagerRepository.cs
using System.Collections.Generic;
using System.Linq;
using System;
namespace ContactManager.Models
{
public class EntityContactManagerRepository : ContactManager.Models.IContactManagerRepository
{
private ContactManagerDBEntities _entities = new ContactManagerDBEntities();
// Contact methods
public Contact GetContact(int id)
{
return (from c in _entities.ContactSet.Include("Group")
where c.Id == id
select c).FirstOrDefault();
}
public Contact CreateContact(int groupId, Contact contactToCreate)
{
// Associate group with contact
contactToCreate.Group = GetGroup(groupId);
// Save new contact
_entities.AddToContactSet(contactToCreate);
_entities.SaveChanges();
return contactToCreate;
}
public Contact EditContact(int groupId, Contact contactToEdit)
{
// Get original contact
var originalContact = GetContact(contactToEdit.Id);
// Update with new group
originalContact.Group = GetGroup(groupId);
// Save changes
_entities.ApplyPropertyChanges(originalContact.EntityKey.EntitySetName, contactToEdit);
_entities.SaveChanges();
return contactToEdit;
}
public void DeleteContact(Contact contactToDelete)
{
var originalContact = GetContact(contactToDelete.Id);
_entities.DeleteObject(originalContact);
_entities.SaveChanges();
}
public Group CreateGroup(Group groupToCreate)
{
_entities.AddToGroupSet(groupToCreate);
_entities.SaveChanges();
return groupToCreate;
}
// Group Methods
public IEnumerable<Group> ListGroups()
{
return _entities.GroupSet.ToList();
}
public Group GetFirstGroup()
{
return _entities.GroupSet.Include("Contacts").FirstOrDefault();
}
public Group GetGroup(int id)
{
return (from g in _entities.GroupSet.Include("Contacts")
where g.Id == id
select g).FirstOrDefault();
}
public void DeleteGroup(Group groupToDelete)
{
var originalGroup = GetGroup(groupToDelete.Id);
_entities.DeleteObject(originalGroup);
_entities.SaveChanges();
}
}
}
Creazione delle visualizzazioni
ASP.NET'applicazione MVC quando si usa il motore di visualizzazione ASP.NET predefinito. Quindi, non si creano visualizzazioni in risposta a un determinato unit test. Tuttavia, poiché un'applicazione sarebbe inutile senza visualizzazioni, non è possibile completare questa iterazione senza creare e modificare le visualizzazioni contenute nell'applicazione Contact Manager.
È necessario creare le nuove visualizzazioni seguenti per la gestione dei gruppi di contatti (vedere la figura 7):
- Views\Group\Index.aspx - Visualizza l'elenco dei gruppi di contatti
- Views\Group\Delete.aspx - Visualizza il modulo di conferma per l'eliminazione di un gruppo di contatti
Figura 07: Visualizzazione indice gruppo(Fare clic per visualizzare l'immagine full-size)
È necessario modificare le visualizzazioni esistenti seguenti in modo che includano gruppi di contatti:
- Views\Home\Create.aspx
- Visualizzazioni\Home\Edit.aspx
- Views\Home\Index.aspx
È possibile visualizzare le visualizzazioni modificate esaminando l'applicazione Visual Studio che accompagna questa esercitazione. Ad esempio, la figura 8 illustra la visualizzazione Indice contatto.
Figura 08: Visualizzazione indice contatto(Fare clic per visualizzare l'immagine full-size)
Riepilogo
In questa iterazione è stata aggiunta una nuova funzionalità all'applicazione Contact Manager seguendo una metodologia di progettazione dell'applicazione di sviluppo basata su test. Abbiamo iniziato creando un set di storie utente. È stato creato un set di unit test che corrispondono ai requisiti espressi dalle storie utente. Infine, è stato scritto un codice sufficiente per soddisfare i requisiti espressi dagli unit test.
Dopo aver scritto codice sufficiente per soddisfare i requisiti espressi dagli unit test, è stato aggiornato il database e le visualizzazioni. È stata aggiunta una nuova tabella Gruppi al database e è stata aggiornata il modello di dati Entity Framework. È stato creato e modificato anche un set di visualizzazioni.
Nell'iterazione successiva, ovvero l'iterazione finale, riscriviamo l'applicazione per sfruttare Ajax. Sfruttando Ajax, si migliorerà la velocità di risposta e le prestazioni dell'applicazione Contact Manager.