ASP.NET MVC TIPS




MODEL binding

public class Album {
public int AlbumId { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public Artist Artist { get; set; }
}
public class Artist {
    public int ArtistId { get; set; }
    public string Name { get; set; }
    public string Country { get; set; }
public string TwitterHandler { get; set; }
}
public class AlbumController {
private IAlbumRepository albumRepo = new AlbumRepository(new DBContext());
public ActionResult Edit(Album album)
{
albumRepo.UpdateAlbum(album); //update in database
albumRepo.Save();
return View(album);

}
}

1. Form submission
<form action="/Album/Edit" method="post">
<input type="text" name="Title" value="AM" />
<input type="text" name="Price" value="12" />

<input type="text" name="Artist.Name" value="Arctic Monkeys" />
<input type="text" name="Artist.Country" value="Australia" />

<input type="submit" value="Submit" />
</form>

2. AJAX post
 $.ajax({
type: 'POST',
url: 'Album/Edit',
data: { Title : 'AM',
Price : '12',
Artist : { Name : 'Arctic Monkeys', Country : 'Australia' }
},
success: function() {},
dataType: 'json'
});

public class AlbumController
{
private IAlbumRepository albumRepo = new AlbumRepository(new DBContext());

public ActionResult Edit(Album album)
{
//album.Title = "AM"
//album.Price = "12"
//album.Artist.Name = "Arctic Monkeys";
//album.Artist.Country = "Australia";

albumRepo.UpdateAlbum(album); //update into database using EF
albumRepo.Save();
return View(album);
}
}
!Over binding
<form action="/Album/Edit" method="post">
<input type="text" name="Title" value="AM" />
...
<input type="text" name="TwitterHandler" value="IAmJackyBlack"/>
<input type="submit" value="Submit" />
</form>

Fix 1 - bind only required fields to the action

public ActionResult Edit([Bind(Include="Title,Price")] Album album)
{
...
}

Fix 2 - don't bind to domain model directly, use view model

public ActionResult Edit(AlbumModel albumModel)
{
var album = new Album
{
AlbumId = albumModel.AlbumId,
Title = albumModel.Title,
Price = albumModel.Price,
...
};
albumRepo.UpdateAlbum(album); //update in database

return View(album);
}




Never trust user input!




controllers

User: Admin - can edit all albums




"Aerosmith - Big Ones"

AlbumId = 2

User: AC/DC Manager - can edit albums of AC/DC




"AC/DC - Let There Be Rock"


AlbumId = 1





public ActionResult Edit(AlbumModel albumModel)
{
var album = albumRepo.GetAlbum(albumModel.AlbumId);

album.Price = albumModel.Price;

albumRepo.UpdateAlbum(album);
albumRepo.Save();

return View(album);
}

!AC/DC Manager can edit all albums by entering the id in the url

 http://localhost/StoreManager/Edit/2

Fix 1: Deny access to edit page if album doesn't belong to user

Fix 2: Check if album can be edited by logged in user


[HttpPost]public ActionResult Edit(AlbumModel albumModel)
{
var album = albumRepo.GetAlbum(albumModel.AlbumId);

if (SessionHelper.User.CanEditAlbum(album))
{
album.Price = albumModel.Price;

albumRepo.UpdateAlbum(album);
albumRepo.Save();

return View(album);
}
else
return View("NotAllowed");
}




Never trust user input!




entity framework

public class Album
{
public int AlbumId { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public Artist Artist { get; set; }
}


private MusicStoreDB db = new MusicStoreDB();

var albums = db.Albums.ToList();

foreach (var album in albums) {
Console.WriteLine(album.Artist.Name);
}

!N+1 issue

private MusicStoreDB db = new MusicStoreDB();

var albums = db.Albums.ToList();

SELECT AlbumId, Title, Price, ...
FROM dbo.Albums

 
foreach (var album in albums) {
Console.WriteLine(album.Artist.Name);
}

SELECT Name, Country, ...
From Artist
WHERE ArtistId = {0}






N + 1 database queries instead of 1 query

Fix 1: Use eager loading vs lazy loading

private MusicStoreDB db = new MusicStoreDB();

var albums = db.Albums.Include(a => a.Artist).ToList();

foreach (var album in albums) {
Console.WriteLine(album.Artist.Name);
}

Fix 2: Request only required fields


a. Extended syntax

private MusicStoreDB db = new MusicStoreDB();

var albums =
(
from album in db.Albums
select new AlbumModel
{
AlbumId = album.AlbumId,
Title = album.Title,
...
ArtistName = album.Artist.Name
}
);

foreach (var album in albums) {
Console.WriteLine(album.ArtistName);
}

b. Compact syntax

private MusicStoreDB db = new MusicStoreDB();

var albums = db.Albums.Select
(
album => new AlbumModel
{
AlbumId = album.AlbumId,
Title = album.Title,
...
ArtistName = album.Artist.Name
}
);
);

foreach (var album in albums) {
Console.WriteLine(album.ArtistName);
}



Check DB queries generated by entity framework



MVC Music Store



Walkthrough 
  • Web site
  • PDF

ASP.NET MVC TIPS

By Szilard David