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.
di Microsoft
In questa sesta iterazione si aggiungono nuove funzionalità all'applicazione scrivendo prima unit test e scrivendo codice per gli unit test. In questa iterazione si aggiungono gruppi di contatti.
Compilazione di un'applicazione MVC ASP.NET Gestione contatti (VB)
In questa serie di esercitazioni viene creata un'intera applicazione Contact Management dall'inizio alla fine. L'applicazione Contact Manager consente di archiviare le informazioni di contatto, ovvero nomi, numeri di telefono e indirizzi di posta elettronica, per un elenco di persone.
L'applicazione viene compilata su più iterazioni. Con ogni iterazione, l'applicazione viene migliorata gradualmente. L'obiettivo di questo approccio a più iterazioni è consentire di comprendere il motivo di ogni modifica.
Iterazione n. 1: creare l'applicazione. Nella prima iterazione viene creato il Contact Manager nel modo più semplice possibile. Viene aggiunto il supporto per le operazioni di base del database: Create, Read, Update e Delete (CRUD).
Iterazione n. 2: rendere l'applicazione più bella. In questa iterazione viene migliorata l'aspetto dell'applicazione modificando la pagina master della visualizzazione MVC predefinita ASP.NET e il foglio di stile css.
Iterazione n. 3: aggiungere la convalida dei moduli. Nella terza iterazione viene aggiunta la convalida dei moduli di base. Microsoft impedisce agli utenti di inviare un modulo senza completare i campi modulo obbligatori. Vengono convalidati anche gli indirizzi di posta elettronica e i numeri di telefono.
Iterazione n. 4: rendere l'applicazione ad accoppiamento 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, si esegue il refactoring dell'applicazione per usare il modello repository e il modello di inserimento delle dipendenze.
Iterazione n. 5: creare unit test. Nella quinta iterazione l'applicazione risulta più semplice da gestire e modificare aggiungendo unit test. Si simulano le classi del modello di dati e si compilano unit test per i controller e la logica di convalida.
Iterazione n. 6: usare lo sviluppo basato su test. In questa sesta iterazione si aggiungono nuove funzionalità all'applicazione scrivendo prima unit test e scrivendo codice per gli unit test. In questa iterazione si aggiungono gruppi di contatti.
Iterazione n. 7: aggiungere la funzionalità Ajax. Nella settima iterazione viene migliorata 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 gli unit test sul posto, è possibile apportare qualsiasi modifica al codice e sapere immediatamente se sono state interrotte le funzionalità esistenti.
In questa iterazione vengono usati unit test per uno scopo completamente diverso. In questa iterazione si usano 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 durante la creazione di codice (Red/ Green/Refactoring):
- Scrivere uno unit test che ha esito negativo (rosso)
- Scrivere codice che supera lo unit test (Verde)
- Effettuare il refactoring del codice (refactoring)
Prima di tutto, si scrive lo unit test. Lo unit test deve esprimere l'intenzione di come si prevede che il codice si comporti. Quando si crea lo unit test per la prima volta, lo unit test dovrebbe non riuscire. Il test non riesce perché non è ancora stato scritto codice dell'applicazione che soddisfa il test.
Successivamente, si scrive codice sufficiente per il superamento dello unit test. L'obiettivo è scrivere il codice nel modo più pigrizio, più 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 dallo unit test.
Infine, dopo aver scritto codice sufficiente, è possibile tornare indietro e prendere in considerazione l'architettura complessiva dell'applicazione. In questo passaggio si riscrive (refactoring) 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 in questo passaggio senza paura perché il codice è coperto dagli unit test.
Esistono molti vantaggi derivanti dalla pratica dello sviluppo basato su test. Prima di tutto, lo sviluppo basato su test impone di concentrarsi sul codice che deve effettivamente essere scritto. Poiché ci si concentra costantemente sulla scrittura di codice sufficiente per superare un determinato test, non è possibile spostarsi nelle weeds e scrivere enormi quantità di codice che non verranno mai usate.
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. Di conseguenza, lo sviluppo basato su test può comportare API più pulite e 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, il test è in genere la prima cosa che esce dalla finestra. Quando si pratica lo sviluppo basato su test, invece, è più probabile che si sia più virtuosi nella scrittura di unit test perché lo sviluppo basato su test rende gli unit test fondamentali per il processo di compilazione di un'applicazione.
Nota
Per altre informazioni sullo sviluppo basato sui test, è consigliabile leggere il libro Michael Feathers Working Effectively with Legacy Code .To learn more about test-driven development, I recommend that you read Michael Feathers book Working Effectively with Legacy Code.
In questa iterazione viene aggiunta una nuova funzionalità all'applicazione Contact Manager. Viene aggiunto il supporto per i gruppi di contatti. È possibile usare i gruppi di contatti per organizzare i contatti in categorie come i gruppi Business e Friend.
Questa nuova funzionalità verrà aggiunta all'applicazione seguendo un processo di sviluppo basato su test. Si scriveranno prima gli unit test e si scriverà tutto il codice in questi test.
Che 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 uno unit test a meno che il test non possa essere eseguito più volte e più volte molto velocemente
Poiché lo sviluppo basato su test è basato sugli unit test, ci concentriamo inizialmente sulla scrittura di controller e logica di business. Si evita di toccare il database o le viste. Il database non verrà modificato o creato fino alla fine di questa esercitazione. Iniziamo con ciò che è possibile testare.
Creazione di storie utente
Quando si pratica lo sviluppo basato su test, si inizia sempre scrivendo un test. Questo genera immediatamente la domanda: Come si decide quale test scrivere per primo? Per rispondere a questa domanda, è necessario 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.
- Nella visualizzazione Indice viene visualizzato un elenco di gruppi di contatti.
- 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 tecnici dell'implementazione.
Durante il processo di compilazione 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 uno unit test per visualizzare l'elenco dei gruppi di contatti.
Elenco dei 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.Tests, scegliendo Aggiungi, Nuovo test e selezionando il modello unit test (vedere la figura 1). Assegnare al nuovo unit test il nome GroupControllerTest.vb e fare clic sul pulsante OK .
Figura 01: Aggiunta dello unit test GroupControllerTest (fare clic per visualizzare l'immagine a dimensione intera)
Il primo unit test è contenuto nell'elenco 1. Questo test verifica che il metodo Index() del controller Group restituisca un set di gruppi. Il test verifica che venga restituita una raccolta di gruppi nei dati di visualizzazione.
Elenco 1 - Controllers\GroupControllerTest.vb
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports System.Web.Mvc
<TestClass()> _
Public Class GroupControllerTest
<TestMethod()> _
Public Sub Index()
' Arrange
Dim controller = New GroupController()
' Act
Dim result = CType(controller.Index(), ViewResult)
' Assert
Assert.IsInstanceOfType(result.ViewData.Model, GetType(IEnumerable(Of Group)))
End Sub
End Class
Quando si digita per la prima volta il codice in Listato 1 in Visual Studio, si otterranno molte righe ondulate rosse. Le classi GroupController o Group non sono state create.
A questo punto, non è nemmeno possibile compilare l'applicazione in modo che non sia possibile eseguire il primo unit test. È buono. Questo conteggia come test non superato. Di conseguenza, è ora disponibile l'autorizzazione per iniziare a scrivere il codice dell'applicazione. È necessario scrivere codice sufficiente per eseguire il test.
La classe controller Group nell'elenco 2 contiene il minimo di codice necessario per superare lo unit test. L'azione Index() restituisce un elenco codificato in modo statico di Gruppi (la classe Group è definita nell'elenco 3).
Elenco 2 - Controllers\GroupController.vb
Public Class GroupController
Inherits System.Web.Mvc.Controller
Function Index() As ActionResult
Dim groups = new List(Of Group)
Return View(groups)
End Function
End Class
Elenco 3 - Models\Group.vb
Public Class Group
End Class
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: Operazione riuscita (Fare clic per visualizzare l'immagine a dimensione intera)
Creazione di gruppi di contatti
A questo punto è possibile passare alla seconda storia utente. Dobbiamo essere in grado di creare nuovi gruppi di contatti. Dobbiamo esprimere questa intenzione con un test.
Il test nell'elenco 4 verifica che la chiamata al metodo Create() con un nuovo gruppo aggiaggichi il gruppo all'elenco dei gruppi restituiti dal metodo Index(). In altre parole, se si crea un nuovo gruppo, si dovrebbe essere in grado di recuperare il nuovo gruppo dall'elenco di gruppi restituiti dal metodo Index().
Elenco 4 - Controllers\GroupControllerTest.vb
<TestMethod> _
Public Sub Create()
' Arrange
Dim controller = New GroupController()
' Act
Dim groupToCreate = New Group()
controller.Create(groupToCreate)
' Assert
Dim result = CType(controller.Index(), ViewResult)
Dim groups = CType(result.ViewData.Model, IEnumerable(Of Group))
CollectionAssert.Contains(groups.ToList(), groupToCreate)
End Sub
Il test nell'elenco 4 chiama il metodo Group controller Create() con un nuovo gruppo di contatti. Successivamente, il test verifica che la chiamata al metodo Group controller Index() restituisca 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 - Controllers\GroupController.vb
Public Class GroupController
Inherits Controller
Private _groups As IList(Of Group) = New List(Of Group)()
Public Function Index() As ActionResult
Return View(_groups)
End Function
Public Function Create(ByVal groupToCreate As Group) As ActionResult
_groups.Add(groupToCreate)
Return RedirectToAction("Index")
End Function
End Class
Il controller di gruppo nell'elenco 5 ha una nuova azione Create(). Questa azione aggiunge un oggetto Group a una raccolta di gruppi. Si noti che l'azione Index() è stata modificata per restituire il contenuto dell'insieme Groups.
Anche in questo caso è stata eseguita la quantità minima di lavoro necessaria per superare lo unit test. Dopo aver apportato queste modifiche al controller di gruppo, tutti gli unit test superano.
Aggiunta della convalida
Questo requisito non è stato dichiarato 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 restituisca un messaggio di errore di convalida nello stato del modello.
Elenco 6 - Controllers\GroupControllerTest.vb
<TestMethod> _
Public Sub CreateRequiredName()
' Arrange
Dim controller = New GroupController()
' Act
Dim groupToCreate As New Group()
groupToCreate.Name = String.Empty
Dim result = CType(controller.Create(groupToCreate), ViewResult)
' Assert
Dim [error] = result.ViewData.ModelState("Name").Errors(0)
Assert.AreEqual("Name is required.", [error].ErrorMessage)
End Sub
Per soddisfare questo test, è necessario aggiungere una proprietà Name alla classe Group (vedere Listato 7). Inoltre, è necessario aggiungere un piccolo bit di logica di convalida all'azione Create() del controller di gruppo (vedere Listato 8).
Elenco 7 - Models\Group.vb
Public Class Group
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
End Class
Elenco 8 - Controllers\GroupController.vb
Public Function Create(ByVal groupToCreate As Group) As ActionResult
' Validation logic
If groupToCreate.Name.Trim().Length = 0 Then
ModelState.AddModelError("Name", "Name is required.")
Return View("Create")
End If
' Database logic
_groups.Add(groupToCreate)
Return RedirectToAction("Index")
End Function
Si noti che l'azione Group controller Create() contiene ora sia la convalida che la logica del database. Attualmente, il database usato dal controller di gruppo è costituito da una raccolta in memoria.
Tempo di refactoring
Il terzo passaggio in Red/Green/Refactor è la parte refactoring. A questo punto, è necessario tornare indietro dal codice e valutare come è possibile effettuare il refactoring dell'applicazione per migliorarne 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 in cui si sceglie di migliorare la progettazione del codice. È disponibile una rete di sicurezza di unit test che impedisce l'interruzione delle funzionalità esistenti.
In questo momento, il nostro controller di gruppo è un casino dal punto di vista della buona progettazione del 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, è necessario separare queste problematiche in classi diverse.
La classe controller Group refactored è contenuta nell'elenco 9. Il controller è stato modificato per usare il livello del 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 fittizio.
Elenco 9 - Controllers\GroupController.vb
Public Class GroupController
Inherits Controller
Private _service As IContactManagerService
Public Sub New()
_service = New ContactManagerService(New ModelStateWrapper(Me.ModelState))
End Sub
Public Sub New(ByVal service As IContactManagerService)
_service = service
End Sub
Public Function Index() As ActionResult
Return View(_service.ListGroups())
End Function
Public Function Create(ByVal groupToCreate As Group) As ActionResult
If _service.CreateGroup(groupToCreate) Then
Return RedirectToAction("Index")
End If
Return View("Create")
End Function
End Class
Elenco 10 - Controllers\ContactManagerService.vb
Public Function ValidateGroup(ByVal groupToValidate As Group) As Boolean
If groupToValidate.Name.Trim().Length = 0 Then
_validationDictionary.AddError("Name", "Name is required.")
End If
Return _validationDictionary.IsValid
End Function
Public Function CreateGroup(ByVal groupToCreate As Group) As Boolean Implements IContactManagerService.CreateGroup
' Validation logic
If Not ValidateGroup(groupToCreate) Then
Return False
End If
' Database logic
Try
_repository.CreateGroup(groupToCreate)
Catch
Return False
End Try
Return True
End Function
Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerService.ListGroups
Return _repository.ListGroups()
End Function
Elenco 11 - Controllers\FakeContactManagerRepository.vb
Public Class FakeContactManagerRepository
Implements IContactManagerRepository
Private _groups As IList(Of Group) = New List(Of Group)()
#Region "IContactManagerRepository Members"
' Group methods
Public Function CreateGroup(ByVal groupToCreate As Group) As Group Implements IContactManagerRepository.CreateGroup
_groups.Add(groupToCreate)
Return groupToCreate
End Function
Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerRepository.ListGroups
Return _groups
End Function
' Contact methods
Public Function CreateContact(ByVal contactToCreate As Contact) As Contact Implements IContactManagerRepository.CreateContact
Throw New NotImplementedException()
End Function
Public Sub DeleteContact(ByVal contactToDelete As Contact) Implements IContactManagerRepository.DeleteContact
Throw New NotImplementedException()
End Sub
Public Function EditContact(ByVal contactToEdit As Contact) As Contact Implements IContactManagerRepository.EditContact
Throw New NotImplementedException()
End Function
Public Function GetContact(ByVal id As Integer) As Contact Implements IContactManagerRepository.GetContact
Throw New NotImplementedException()
End Function
Public Function ListContacts() As IEnumerable(Of Contact) Implements IContactManagerRepository.ListContacts
Throw New NotImplementedException()
End Function
#End Region
End Class
Per modificare l'interfaccia IContactManagerRepository è necessario usare 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 Function CreateGroup(groupToCreate As Group) As Group Implements IContactManagerRepository.CreateGroup
throw New NotImplementedException()
End Function
Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerRepository.ListGroups
throw New NotImplementedException()
End Function
Infine, queste modifiche alla progettazione dell'applicazione richiedono di apportare alcune modifiche agli unit test. È ora necessario usare FakeContactManagerRepository durante l'esecuzione degli unit test. La classe GroupControllerTest aggiornata è contenuta nell'elenco 12.
Elenco 12 - Controllers\GroupControllerTest.vb
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports System.Web.Mvc
<TestClass()> _
Public Class GroupControllerTest
Private _repository As IContactManagerRepository
Private _modelState As ModelStateDictionary
Private _service As IContactManagerService
<TestInitialize()> _
Public Sub Initialize()
_repository = New FakeContactManagerRepository()
_modelState = New ModelStateDictionary()
_service = New ContactManagerService(New ModelStateWrapper(_modelState), _repository)
End Sub
<TestMethod()> _
Public Sub Index()
' Arrange
Dim controller = New GroupController(_service)
' Act
Dim result = CType(controller.Index(), ViewResult)
' Assert
Assert.IsInstanceOfType(result.ViewData.Model, GetType(IEnumerable(Of Group)))
End Sub
<TestMethod()> _
Public Sub Create()
' Arrange
Dim controller = New GroupController(_service)
' Act
Dim groupToCreate = New Group()
groupToCreate.Name = "Business"
controller.Create(groupToCreate)
' Assert
Dim result = CType(controller.Index(), ViewResult)
Dim groups = CType(result.ViewData.Model, IEnumerable(Of Group))
CollectionAssert.Contains(groups.ToList(), groupToCreate)
End Sub
<TestMethod()> _
Public Sub CreateRequiredName()
' Arrange
Dim controller = New GroupController(_service)
' Act
Dim groupToCreate = New Group()
groupToCreate.Name = String.Empty
Dim result = CType(controller.Create(groupToCreate), ViewResult)
' Assert
Dim nameError = _modelState("Name").Errors(0)
Assert.AreEqual("Name is required.", nameError.ErrorMessage)
End Sub
End Class
Dopo aver apportato tutte queste modifiche, anche in questo caso, tutti gli unit test vengono superati. Abbiamo completato l'intero ciclo di red/green/refactoring. Sono state implementate le prime due storie utente. Sono ora disponibili unit test di supporto per i requisiti espressi nelle storie utente. L'implementazione del resto delle storie utente comporta la ripetizione dello stesso ciclo di red/green/refactoring.
Modifica del database
Purtroppo, anche se abbiamo soddisfatto tutti i requisiti espressi dagli unit test, il nostro lavoro non viene eseguito. È comunque necessario modificare il database.
È necessario creare una nuova tabella di database group. Seguire questa procedura:
- Nella finestra Esplora server fare clic con il pulsante destro del mouse sulla cartella Tabelle e scegliere l'opzione di menu Aggiungi nuova tabella.
- Immettere le due colonne descritte di seguito nella Designer Tabella.
- 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 scegliere l'opzione di menu Mostra dati tabella.
- Eliminare tutte le righe.
Successivamente, è necessario definire una relazione tra la tabella di database Groups e la tabella di database Contacts esistente. Seguire questa procedura:
- Fare doppio clic sulla tabella Contatti nella finestra Esplora server per aprire il 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)selezionare il valore Cascade (Cascade ) per Delete Rule (Regola di eliminazione).
- 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 dimensione intera)
Figura 04: Specifica delle relazioni tra tabelle (fare clic per visualizzare l'immagine a dimensione intera)
Aggiornamento del modello di dati
Successivamente, è 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 Models per aprire entity Designer.
- Fare clic con il pulsante destro del mouse sull'area di Designer e scegliere l'opzione di menu Aggiorna modello da Database.
- Nell'Aggiornamento guidato 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 scegliere l'opzione di menu Rinomina. Modificare il nome dell'entità Groups in Group (singolare).
- Fare clic con il pulsante destro del mouse sulla proprietà di navigazione Gruppi visualizzata nella parte inferiore dell'entità Contact. Modificare il nome della proprietà di navigazione Groups in Group (singolare).
Figura 05: Aggiornamento di un modello di Entity Framework dal database (fare clic per visualizzare l'immagine a dimensione intera)
Dopo aver completato questi passaggi, il modello di dati rappresenterà sia le tabelle Contatti che Gruppi. L'entità Designer deve visualizzare entrambe le entità (vedere la figura 6).
Figura 06: Entity Designer visualizzazione di Group and Contact (Fare clic per visualizzare l'immagine a dimensione intera)
Creazione delle classi di repository
Successivamente, è necessario implementare la classe del repository. Nel corso di questa iterazione sono stati aggiunti diversi nuovi metodi all'interfaccia IContactManagerRepository durante la scrittura del codice per soddisfare gli unit test. La versione finale dell'interfaccia IContactManagerRepository è contenuta nell'elenco 14.
Elenco 14 - Models\IContactManagerRepository.vb
Public Interface IContactManagerRepository
' Contact methods
Function CreateContact(ByVal groupId As Integer, ByVal contactToCreate As Contact) As Contact
Sub DeleteContact(ByVal contactToDelete As Contact)
Function EditContact(ByVal groupId As Integer, ByVal contactToEdit As Contact) As Contact
Function GetContact(ByVal id As Integer) As Contact
' Group methods
Function CreateGroup(ByVal groupToCreate As Group) As Group
Function ListGroups() As IEnumerable(Of Group)
Function GetGroup(ByVal groupId As Integer) As Group
Function GetFirstGroup() As Group
Sub DeleteGroup(ByVal groupToDelete As Group)
End Interface
Non sono stati effettivamente implementati metodi correlati all'uso dei gruppi di contatti nella classe EntityContactManagerRepository reale. Attualmente, la classe EntityContactManagerRepository dispone di metodi stub per ognuno dei metodi del gruppo di contatti elencati nell'interfaccia IContactManagerRepository. Ad esempio, il metodo ListGroups() è simile al seguente:
Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerRepository.ListGroups
throw New NotImplementedException()
End Function
I metodi stub consentono di compilare l'applicazione e di 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 - Models\EntityContactManagerRepository.vb
Public Class EntityContactManagerRepository
Implements IContactManagerRepository
Private _entities As New ContactManagerDBEntities()
' Contact methods
Public Function GetContact(ByVal id As Integer) As Contact Implements IContactManagerRepository.GetContact
Return (From c In _entities.ContactSet.Include("Group") _
Where c.Id = id _
Select c).FirstOrDefault()
End Function
Public Function CreateContact(ByVal groupId As Integer, ByVal contactToCreate As Contact) As Contact Implements IContactManagerRepository.CreateContact
' Associate group with contact
contactToCreate.Group = GetGroup(groupId)
' Save new contact
_entities.AddToContactSet(contactToCreate)
_entities.SaveChanges()
Return contactToCreate
End Function
Public Function EditContact(ByVal groupId As Integer, ByVal contactToEdit As Contact) As Contact Implements IContactManagerRepository.EditContact
' Get original contact
Dim originalContact = GetContact(contactToEdit.Id)
' Update with new group
originalContact.Group = GetGroup(groupId)
' Save changes
_entities.ApplyPropertyChanges(originalContact.EntityKey.EntitySetName, contactToEdit)
_entities.SaveChanges()
Return contactToEdit
End Function
Public Sub DeleteContact(ByVal contactToDelete As Contact) Implements IContactManagerRepository.DeleteContact
Dim originalContact = GetContact(contactToDelete.Id)
_entities.DeleteObject(originalContact)
_entities.SaveChanges()
End Sub
' Group methods
Public Function CreateGroup(ByVal groupToCreate As Group) As Group Implements IContactManagerRepository.CreateGroup
_entities.AddToGroupSet(groupToCreate)
_entities.SaveChanges()
Return groupToCreate
End Function
Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerRepository.ListGroups
Return _entities.GroupSet.ToList()
End Function
Public Function GetFirstGroup() As Group Implements IContactManagerRepository.GetFirstGroup
Return _entities.GroupSet.Include("Contacts").FirstOrDefault()
End Function
Public Function GetGroup(ByVal id As Integer) As Group Implements IContactManagerRepository.GetGroup
Return (From g In _entities.GroupSet.Include("Contacts") _
Where g.Id = id _
Select g).FirstOrDefault()
End Function
Public Sub DeleteGroup(ByVal groupToDelete As Group) Implements IContactManagerRepository.DeleteGroup
Dim originalGroup = GetGroup(groupToDelete.Id)
_entities.DeleteObject(originalGroup)
_entities.SaveChanges()
End Sub
End Class
Creazione delle visualizzazioni
ASP.NET'applicazione MVC quando si usa il motore di visualizzazione ASP.NET predefinito. Non si creano quindi visualizzazioni in risposta a uno unit test specifico. 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 a dimensione intera)
È necessario modificare le viste esistenti seguenti in modo che includano gruppi di contatti:
- Views\Home\Create.aspx
- Views\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 a dimensione intera)
Riepilogo
In questa iterazione sono state aggiunte nuove funzionalità all'applicazione Contact Manager seguendo una metodologia di progettazione delle applicazioni di sviluppo basata su test. Abbiamo iniziato creando un set di storie utente. È stato creato un set di unit test che corrisponde ai requisiti espressi dalle storie utente. Infine, è stato scritto codice sufficiente per soddisfare i requisiti espressi dagli unit test.
Al termine della scrittura di codice sufficiente per soddisfare i requisiti espressi dagli unit test, è stato aggiornato il database e le viste. È stata aggiunta una nuova tabella Groups al database e è stato aggiornato il modello di dati di Entity Framework. È stato anche creato e modificato un set di visualizzazioni.
Nell'iterazione successiva, ovvero l'iterazione finale, si riscrive l'applicazione per sfruttare i vantaggi di Ajax. Sfruttando Ajax, si migliorerà la velocità di risposta e le prestazioni dell'applicazione Contact Manager.