BTI420 Lab 5

A web app that will gather and display content about recorded/streamed music.

.

BTI420 Lab 5 – Due on Tue Feb 17

Due date:

Tuesday, February 17, 2015, at 7:30am ET for Section B

Wednesday, February 18, 2015, at 5:00pm ET for Section A

Grade value: 3% of your final course grade

.

Objective(s)

Work with associated objects.

Work with data annotations.

.

Introduction to the problem that you will solve

We need a web app that will gather and display content about recorded/streamed music.

The domain model includes Artists, Albums, and Songs.

Your app will display each collection on a ‘list’ view.

In addition, your app will enable the user to ‘create’ new items in each collection (on a ‘create’ view).

Read through this assignment fully before starting work on it. That way you will know what’s coming.

.

Use cases

The app will support the following six (6) use cases:

  • Add an artist
  • Add an album – must be associated with an artist
  • Add a song – must be associated with an album
  • Display list of objects – all three entity types – with appropriate associated data

.

Your professor recommends that you use scaffolded views, to reduce the amount of work. Most of the views will require a small amount of changes to meet the use case. For example, the ‘add an album’ must display an HTML <input> or <select> element to enable the user to choose the album’s artist. In addition, the ‘list’ and ‘details’ views will need to render data from associated objects.

.

Get started with Lab 5

Create a new project, named “Lab5”. No authentication.

During class, we will begin the following sequence of tasks (which you will continue on your own):

Write the model classes

Add data annotations to those classes

Use the scaffolding feature to create a throw-away controller, which will initialize the database-hosted persistent store components

Add a store initializer to the project (to configure the genres)

Add AutoMapper to the project

Add a Manager class to the project

Write the view model classes, for list, add, and base functions

Add data annotations to the view model classes

Add the controllers

Implement the ‘add new’ Artist workflow, which includes writing the ‘add form’ view model class, the view, and the controller and manager methods

Implement the ‘get all’ Artist workflow, which includes a view, and the controller and manager methods

Implement the ‘add new’ Album workflow, which includes writing the ‘add form’ view model class, the view, and the controller and manager methods

Implement the ‘get all’ Album workflow, which includes a view, and the controller and manager methods

Implement the ‘add new’ Song workflow, which includes writing the ‘add form’ view model class, the view, and the controller and manager methods

Implement the ‘get all’ Song workflow, which includes a view, and the controller and manager methods

.

Write the model classes

There are three entities – artists, albums, and songs. Each one has an Id property, and the following:

Artist properties (assume strings unless otherwise noted; you can add more):

  • Name
  • Birth name
  • Birth or start date – date
  • Start decade – int (e.g. 1990)
  • Genre

Album properties:

  • Name
  • Release date – date
  • Genre
  • Length (in minutes) – double
  • Producer
  • Album cover URL

Song properties:

  • Name
  • Composer
  • Genre
  • Release date as a single – date (nullable; see discussion below)
  • Track number – int

The Song class has a “Release date as a single” property. Its type should be a nullable DateTime:


public DateTime? ReleaseDateAsSingle { get; set; }

This technique enables us to declare but not store a date. For a Song object, it may not have a value for ReleaseDateAsSingle, because the song may never be released as a single. However, this property will need special handling later, because you must compare it to null before attempting to use its value.

In addition to the three classes above, create a Genre class, with Id and Name properties. We will use the data in its collection as a lookup table. All three entities will have a string property named Genre. However, for this app, we do not want to create an association from (for example) the Artist class and the Genre class. We’ll use the Genre data collection as a somewhat static resource.

Remember to add a default constructor to initialize collections and non-null dates.

Here’s a class diagram. Click to open full-size in its own tab/window:

lab5-design-model-classes-v2

 

.

Add data annotations to those classes

Recently, you learned about data annotations.

Add suitable data annotations to these design model classes. Use your discretion – it is not necessary to add data annotations to every property.

A ‘to-one’ navigation property must have the [Required] attribute. Follow the other guidance in the notes and reference material.

.

Use the scaffolding feature to create a throw-away controller, which will initialize the database-hosted persistent store components

As you recently learned, create a throw-away controller, which will 1) add the Entity Framework classes to your project, 2) write a connection string, and 3) create a ‘data context’ class.

After the scaffolder finishes its task, you can delete the throw-away controller (and its views, if it created them). Then, make sure you add the remaining entity collection properties to the data context class source code.

Or, manually code these components yourself.

.

Add a store initializer to the project (to configure the genres)

At the bottom of a “Models” folder source code file (either DesignModelClasses.cs or DataContext.cs), add a store initializer class, as you recently learned.

Add several Genre objects. Use your knowledge, or iTunes, or another resource to create at least eight (8) genre objects. Genre names could include Pop, Country, Classical, Hip-Hop, and so on.

Remember to configure the MvcApplication class’ Application_Start() method to start/run the store initializer.

.

Add AutoMapper to the project

Add AutoMapper to the project.

.

Add a Manager class to the project

Add a Manager class to the project. Add the necessary ‘using…’ statements (for the Models namespace, and the AutoMapper namespace).

Remember to add a class-level private field (variable) that references a new instance of the data context class.

Looking ahead, you will need a number of methods to support get-all, get-one, and add-new tasks:

  • AllGenres
  • AddArtist
  • AllArtists
  • AllArtistsList (for the ‘ArtistList’ view model class, with only Id and Name properties)
  • AddAlbum
  • AllAlbums
  • AllAlbumsList (ditto above)
  • AddSong
  • AllSongs
  • AllSongsList (ditto above)

.

Remember, Manager methods parameter or return types include only one of these:

  • empty parameter list, or null return type
  • a value type (see “Value Types” in this document)
  • a view model object
  • a view model collection

.

Write the view model classes, for list, add, and base functions

For each entity (Artist, Album, Song), write the view model classes for list, add, and base functions.

Note:

For each ‘add‘ class, simply copy most of the properties from the entity’s design model class.

The Id property is not needed – do not copy it over.

Navigation properties for collections are not needed – do not copy them over.

If you have a ‘to-one’ navigation property, change its type to int, and add an “Id” suffix to its name. For example:

Design model class property: public Artist Artist { get; set; }

View model class property: public int ArtistId { get; set; }

.

Later, you will also write ‘add form’ view model classes.

.

Add data annotations to the view model classes

Add suitable data annotations to these view model classes. Use your discretion – it is not necessary to add data annotations to every property. However, plan to use five (5) different kinds of annotations in your classes, where and when appropriate:

  • Required
  • StringLength
  • Display
  • DataType
  • Range

.

Add the controllers

Add controllers for Artists, Albums, and Songs.

Use the “MVC 5 Controller with read/write actions” template.

In each controller, you can remove the ‘edit’ and ‘delete’ method pairs, because we will not need them for this app.

Also, ensure that you add a private field (variable) that references and initializes a manager object.

.

Implement the ‘add new’ Artist workflow, which includes writing the ‘add form’ view model class, the view, and the controller and manager methods

Write an ‘add form’ view model class for the ‘add new’ artist task. Why? The HTML Form needs a <select> or <input> element to enable the user to choose a genre. We need to send the data as a SelectList object.

The best way is to copy-paste the existing ‘ArtistAdd’ view model class, to a new view model class named ‘ArtistAddForm’.

Then, change the type of the Genre property to SelectList.

.

Add the view

Add a view to support ‘add new’ artist, using scaffolding, as suggested by the following:

add-view-create-artist

.

Edit the titles etc. of the view, to customize it to this app’s needs. (Unsure about what this means? Well, look at the form. What does “ArtistAddForm” really mean to end users who aren’t software developers?)

Add the HTML <select> or <input> element that will render the list of genres. You can use whatever you want (dropdown, listbox, radio button group), as long as it allows only a single item to be selected.)

See this document for rendering and styling suggestions. 

In your view code, pre-select one of the genres.

Set the focus to the artist name textbox.

.

Manager method to fetch the genres

We want this data as a simple string collection. Then, we can bind it to the collection argument of the SelectList constructor in the controller Create() (HTTP GET) method.

The genre objects have an Id and a Name property. You need only the Name strings.

Oh yes, you could do it the normal way, by enumerating through the collection…


var genres = new List<string>();
foreach (var item in ds.Genres)
{
    genres.Add(item.Name);
}
return genres;

.

However, you should consider using the Select() extension method, will enables you to create a new data structure. Here, we will use only the strings. Notice that we use only one line of code:


return ds.Genres.OrderBy(g => g.Name).Select(g => g.Name).ToList();

.

AutoMapper maps

In the MvcApplication class’ Application_Start() method, add statements that configure AutoMapper maps. You will need these now:

  • From Model ‘artist’, to View model ‘artist base’
  • From Model ‘artist’, to View model ‘artist list’
  • From View model ‘artist add’, to Model ‘artist

.

Manager method to add a new Artist object

Write a method that accepts an ‘artist add’ object, adds it to the persistent store, then returns an ‘artist base’ object (or null if not successful).

.

Controller method to display the form

As you know, the Create() method – which responds to an HTTP GET request – is used to prepare the view to display an HTML Form. We create an ‘add form’ object, and configure it with reasonable initial values.

Here’s a possible approach for this ‘add new’ artist task:

artist-controller-create-method-add-form

.

Here’s a sample form:

artist-create-get

.

Controller method to handle the browser’s HTTP POST request

Write a standard Create(ArtistAdd newItem) method to handle the browser’s HTTP POST request.

The controller template has a method implementation; replace it with the now-standard ‘if model-state-is-valid’ approach.

The RedirectToAction() target can simply be the Index view for now. You could change it later if you wish.

.

Implement the ‘get all’ Artist workflow, which includes a view, and the controller and manager methods

Add a view to support ‘get all’ artists, using scaffolding, as suggested by the following:

add-view-artist-list

.

Edit the titles etc. of the view, to customize it to this app’s needs.

.

Manager method to fetch all artists

Write a manager method to fetch and return all artists. It would be a good idea to sort the results by artist name.

.

Controller method to return the view

Edit the code to ensure that the method’s return statement passes the ‘artist base’ collection to the view.

.

At this point, you now have done enough to add and display artist objects. Here’s a sample ‘list’ view:

artist-list

.

Implement the ‘add new’ Album workflow, which includes writing the ‘add form’ view model class, the view, and the controller and manager methods

These tasks will be similar to the set of tasks you did above for the artist entity. Here are a few items to be aware of:

.

AutoMapper maps

Add more AutoMapper maps to support this workflow:

  • From Model ‘album’, to View model ‘album base’
  • From Model ‘album’, to View model ‘album list’
  • From View model ‘album add’, to Model ‘album’

.

Manager method to return an ‘artist list’ collection

Write a method that returns a collection of ‘artist list’ objects. The collection must be sorted by name.

.

Album add form needs two SelectList objects

When adding a new album, the artist must be configured. Therefore the ‘album add form’ will need a SelectList of artists (using the ‘artist list’ view model class).

On the view, render the ‘artist list’ collection in a suitable fashion. We suggest a listbox, no taller than about a dozen items. (Other single-select user interface objects may be annoying to use with large collections.)

It also needs a SelectList of genres.

Here is a possible approach:

album-controller-create-method-add-form

.

Here’s an example form:

album-create-get

.

Manager method to add a new Album object

The manager method that adds a new album must validate the artist identifier before attempting to add the album.

.

Implement the ‘get all’ Album workflow, which includes a view, and the controller and manager methods

This will be similar to the set of tasks you did above for the artist entity.

You may get a result that looks similar to this (click to view full size in a new tab/window):

album-list-original

.

That’s pretty ugly. ArtistId? Zero? Your manager method probably did not use the Include() method. And what about those URLs? Ugly.

Would you like yours to look like this?

album-list-better

.

Only three small changes would be needed to get this done:

Manager method to ‘get all’ albums:

Use the Include() method to get the associated artist object.

View model class, ‘album base’:

Add another property to the ‘album base’ class. If you want the artist’s name, add an “ArtistName” property (which is a string type). You could also add a [Display…] data annotation to make a nice title.

View source code:

Change the column titles in the table header row, to match the new columns.

In the data row, replace ArtistId with ArtistName. Then, replace the AlbumCoverUrl content with an <img> tag, using the AlbumCoverUrl string data.

.

Implement the ‘add new’ Song workflow, which includes writing the ‘add form’ view model class, the view, and the controller and manager methods

These tasks will be similar to the set of tasks you did above for the artist and album entities. Here are a few items to be aware of:

.

AutoMapper maps

Add more AutoMapper maps to support this workflow:

  • From Model ‘song’, to View model ‘song base’
  • From Model ‘song’, to View model ‘song list’
  • From View model ‘song add’, to Model ‘song’

.

Manager method to return an ‘album list’ collection

Write a method that returns a collection of ‘album list’ objects. The collection must be sorted by name.

.

Song add form needs two SelectList objects

When adding a new song, the album must be configured. Therefore the ‘song add form’ will need a SelectList of albums (using the ‘album list’ view model class).

On the view, render the ‘album list’ collection in a suitable fashion. We suggest a listbox, no taller than about a dozen items. (Other single-select user interface objects may be annoying to use with large collections.)

It also needs a SelectList of genres.

Here’s a sample form:

song-create-get-original

.

song-create-get-betterWould you like a better “Album” listbox?

Maybe showing the artist?

Only three small changes would be needed to get this done:

Manager method to ‘get album list’:

Use the Include() method to get the associated artist object.

View model class, ‘album list’:

Add more properties to the ‘album list’ class. Here, we created a composite of the album name, and the artist name.

album-view-model-better

Songs controller, Create() method:

When creating the AlbumId SelectList, the dataTextField will be the new composite property name (“AlbumAndArtist” above).

.

Manager method to add a new Song object

The manager method that adds a new song must validate the album identifier before attempting to add the song.

.

Implement the ‘get all’ Song workflow, which includes a view, and the controller and manager methods

This will be similar to the set of tasks you did above for the album entity.

Here’s a sample list:

song-list-original

.

Optional – would you like your list to look a bit better? With names for the artist and album?

song-list-better

.

Only three small changes would be needed to get this done:

Manager method to ‘get all’ songs:

Use the Include() method to get the associated album and artist objects:


var fetchedObjects = ds.Songs.Include("Album.Artist").OrderBy(a => a.Name);

View model class, ‘song base’:

Add more properties to the ‘song base’ class. Use the composite name technique you recently learned:

  • AlbumName – gets the Name property of the Album object
  • AlbumArtistName – gets the Name property of the Artist object in the Album object (wow!)

View source code:

Change the AlbumId column title to AlbumArtistName. Add another column for AlbumName.

In the data row, replace AlbumId with AlbumArtistName. Add another column for AlbumName.

.

Test your work, by adding objects

Before you submit your work, add…

  • at least three Artist objects
  • for one (or more) of the Artists, add at least three Album objects
  • for one (or more) of the Albums, add at least three Song objects

.

Looking for real-world data? You can use any of these services:

.

Submit your work on My.Seneca/Blackboard

The instructions are similar to those from Lab 1. Here’s a brief version:

1. Make a copy of your work, and remove the packages, bin, and obj folders.

2. Create a compressed (zip) version of your solution’s folder.

3. Login to My.Seneca/Blackboard

4. Navigate to the BTI420 Assignments area, and use the link to upload/submit your work

.

.

.

.

.

.

.

.

.

.

.

.

.

  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: