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
SQL Profiler (AnjLab Profiler for SQL Express Server)
- Glimpse - http://getglimpse.com/
- MVC MiniProfiler - http://miniprofiler.com/
MVC Music Store
Walkthrough
-
Web site
ASP.NET MVC TIPS
By Szilard David
ASP.NET MVC TIPS
- 678