Updating MusicStore for better unit-testability

This commit is contained in:
Youngjune Hong 2015-02-03 13:55:23 -08:00
parent daaeca8681
commit 2e2ca59aa5
11 changed files with 145 additions and 124 deletions

View File

@ -11,15 +11,11 @@ namespace MusicStore.Controllers
[Authorize]
public class AccountController : Controller
{
public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
{
UserManager = userManager;
SignInManager = signInManager;
}
[FromServices]
public UserManager<ApplicationUser> UserManager { get; set; }
public UserManager<ApplicationUser> UserManager { get; private set; }
public SignInManager<ApplicationUser> SignInManager { get; private set; }
[FromServices]
public SignInManager<ApplicationUser> SignInManager { get; set; }
[AllowAnonymous]
//Bug: https://github.com/aspnet/WebFx/issues/339

View File

@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
@ -17,26 +18,42 @@ namespace MusicStore.Areas.Admin.Controllers
[Microsoft.AspNet.Mvc.Authorize("ManageStore")]
public class StoreManagerController : Controller
{
private readonly MusicStoreContext _dbContext;
private readonly IMemoryCache _cache;
private IConnectionManager _connectionManager;
private IHubContext _announcementHub;
public StoreManagerController(
MusicStoreContext dbContext,
IConnectionManager connectionManager,
IMemoryCache memoryCache)
[FromServices]
public MusicStoreContext DbContext
{
_dbContext = dbContext;
_announcementHub = connectionManager.GetHubContext<AnnouncementHub>();
_cache = memoryCache;
get;
set;
}
[FromServices]
public IMemoryCache Cache
{
get;
set;
}
[FromServices]
public IConnectionManager ConnectionManager
{
get
{
return _connectionManager;
}
set
{
_connectionManager = value;
_announcementHub = _connectionManager.GetHubContext<AnnouncementHub>();
}
}
//
// GET: /StoreManager/
public async Task<IActionResult> Index()
{
var albums = await _dbContext.Albums
var albums = await DbContext.Albums
.Include(a => a.Genre)
.Include(a => a.Artist)
.ToListAsync();
@ -46,18 +63,17 @@ namespace MusicStore.Areas.Admin.Controllers
//
// GET: /StoreManager/Details/5
public async Task<IActionResult> Details(int id)
{
var cacheKey = GetCacheKey(id);
var album = await _cache.GetOrSet(cacheKey, async context =>
var album = await Cache.GetOrSet(cacheKey, async context =>
{
//Remove it from cache if not retrieved in last 10 minutes.
context.SetSlidingExpiration(TimeSpan.FromMinutes(10));
//If this returns null how do we prevent the cache to store this.
return await _dbContext.Albums
return await DbContext.Albums
.Where(a => a.AlbumId == id)
.Include(a => a.Artist)
.Include(a => a.Genre)
@ -66,7 +82,7 @@ namespace MusicStore.Areas.Admin.Controllers
if (album == null)
{
_cache.Remove(cacheKey);
Cache.Remove(cacheKey);
return View(album);
}
@ -77,20 +93,20 @@ namespace MusicStore.Areas.Admin.Controllers
// GET: /StoreManager/Create
public IActionResult Create()
{
ViewBag.GenreId = new SelectList(_dbContext.Genres, "GenreId", "Name");
ViewBag.ArtistId = new SelectList(_dbContext.Artists, "ArtistId", "Name");
ViewBag.GenreId = new SelectList(DbContext.Genres, "GenreId", "Name");
ViewBag.ArtistId = new SelectList(DbContext.Artists, "ArtistId", "Name");
return View();
}
// POST: /StoreManager/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Album album)
public async Task<IActionResult> Create(Album album, CancellationToken requestAborted)
{
if (ModelState.IsValid)
{
_dbContext.Albums.Add(album);
await _dbContext.SaveChangesAsync(Context.RequestAborted);
DbContext.Albums.Add(album);
await DbContext.SaveChangesAsync(requestAborted);
var albumData = new AlbumData
{
@ -99,12 +115,12 @@ namespace MusicStore.Areas.Admin.Controllers
};
_announcementHub.Clients.All.announcement(albumData);
_cache.Remove("latestAlbum");
Cache.Remove("latestAlbum");
return RedirectToAction("Index");
}
ViewBag.GenreId = new SelectList(_dbContext.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(_dbContext.Artists, "ArtistId", "Name", album.ArtistId);
ViewBag.GenreId = new SelectList(DbContext.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(DbContext.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
@ -112,7 +128,7 @@ namespace MusicStore.Areas.Admin.Controllers
// GET: /StoreManager/Edit/5
public async Task<IActionResult> Edit(int id)
{
var album = await _dbContext.Albums.
var album = await DbContext.Albums.
Where(a => a.AlbumId == id).
FirstOrDefaultAsync();
@ -121,8 +137,8 @@ namespace MusicStore.Areas.Admin.Controllers
return View(album);
}
ViewBag.GenreId = new SelectList(_dbContext.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(_dbContext.Artists, "ArtistId", "Name", album.ArtistId);
ViewBag.GenreId = new SelectList(DbContext.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(DbContext.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
@ -130,19 +146,19 @@ namespace MusicStore.Areas.Admin.Controllers
// POST: /StoreManager/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(Album album)
public async Task<IActionResult> Edit(Album album, CancellationToken requestAborted)
{
if (ModelState.IsValid)
{
_dbContext.Update(album);
await _dbContext.SaveChangesAsync(Context.RequestAborted);
DbContext.Update(album);
await DbContext.SaveChangesAsync(requestAborted);
//Invalidate the cache entry as it is modified
_cache.Remove(GetCacheKey(album.AlbumId));
Cache.Remove(GetCacheKey(album.AlbumId));
return RedirectToAction("Index");
}
ViewBag.GenreId = new SelectList(_dbContext.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(_dbContext.Artists, "ArtistId", "Name", album.ArtistId);
ViewBag.GenreId = new SelectList(DbContext.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(DbContext.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
@ -150,23 +166,23 @@ namespace MusicStore.Areas.Admin.Controllers
// GET: /StoreManager/RemoveAlbum/5
public async Task<IActionResult> RemoveAlbum(int id)
{
var album = await _dbContext.Albums.Where(a => a.AlbumId == id).FirstOrDefaultAsync();
var album = await DbContext.Albums.Where(a => a.AlbumId == id).FirstOrDefaultAsync();
return View(album);
}
//
// POST: /StoreManager/RemoveAlbum/5
[HttpPost, ActionName("RemoveAlbum")]
public async Task<IActionResult> RemoveAlbumConfirmed(int id)
public async Task<IActionResult> RemoveAlbumConfirmed(int id, CancellationToken requestAborted)
{
var album = await _dbContext.Albums.Where(a => a.AlbumId == id).FirstOrDefaultAsync();
var album = await DbContext.Albums.Where(a => a.AlbumId == id).FirstOrDefaultAsync();
if (album != null)
{
_dbContext.Albums.Remove(album);
await _dbContext.SaveChangesAsync(Context.RequestAborted);
DbContext.Albums.Remove(album);
await DbContext.SaveChangesAsync(requestAborted);
//Remove the cache entry as it is removed
_cache.Remove(GetCacheKey(id));
Cache.Remove(GetCacheKey(id));
}
return RedirectToAction("Index");
@ -184,7 +200,7 @@ namespace MusicStore.Areas.Admin.Controllers
[HttpGet]
public async Task<IActionResult> GetAlbumIdFromName(string albumName)
{
var album = await _dbContext.Albums.Where(a => a.Title == albumName).FirstOrDefaultAsync();
var album = await DbContext.Albums.Where(a => a.Title == albumName).FirstOrDefaultAsync();
if (album == null)
{

View File

@ -10,18 +10,23 @@ namespace MusicStore.Components
[ViewComponent(Name = "Announcement")]
public class AnnouncementComponent : ViewComponent
{
private readonly MusicStoreContext _dbContext;
private readonly IMemoryCache _cache;
public AnnouncementComponent(MusicStoreContext dbContext, IMemoryCache memoryCache)
[Activate]
public MusicStoreContext DbContext
{
_dbContext = dbContext;
_cache = memoryCache;
get;
set;
}
[Activate]
public IMemoryCache Cache
{
get;
set;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var latestAlbum = await _cache.GetOrSet("latestAlbum", async context =>
var latestAlbum = await Cache.GetOrSet("latestAlbum", async context =>
{
context.SetAbsoluteExpiration(TimeSpan.FromMinutes(10));
return await GetLatestAlbum();
@ -32,7 +37,7 @@ namespace MusicStore.Components
private Task<Album> GetLatestAlbum()
{
var latestAlbum = _dbContext.Albums
var latestAlbum = DbContext.Albums
.OrderByDescending(a => a.Created)
.Where(a => (a.Created - DateTime.UtcNow).TotalDays <= 2)
.FirstOrDefaultAsync();

View File

@ -8,11 +8,11 @@ namespace MusicStore.Components
[ViewComponent(Name = "CartSummary")]
public class CartSummaryComponent : ViewComponent
{
private readonly MusicStoreContext _dbContext;
public CartSummaryComponent(MusicStoreContext dbContext)
[Activate]
public MusicStoreContext DbContext
{
_dbContext = dbContext;
get;
set;
}
public async Task<IViewComponentResult> InvokeAsync()
@ -27,7 +27,7 @@ namespace MusicStore.Components
private async Task<IOrderedEnumerable<string>> GetCartItems()
{
var cart = ShoppingCart.GetCart(_dbContext, Context);
var cart = ShoppingCart.GetCart(DbContext, Context);
return (await cart.GetCartItems())
.Select(a => a.Album.Title)

View File

@ -9,11 +9,11 @@ namespace MusicStore.Components
[ViewComponent(Name = "GenreMenu")]
public class GenreMenuComponent : ViewComponent
{
private readonly MusicStoreContext _dbContext;
public GenreMenuComponent(MusicStoreContext dbContext)
[Activate]
public MusicStoreContext DbContext
{
_dbContext = dbContext;
get;
set;
}
public async Task<IViewComponentResult> InvokeAsync()
@ -34,7 +34,7 @@ namespace MusicStore.Components
//.Take(9)
//.ToList();
return await _dbContext.Genres.Take(9).ToListAsync();
return await DbContext.Genres.Take(9).ToListAsync();
}
}
}

View File

@ -14,15 +14,12 @@ namespace MusicStore.Controllers
[Authorize]
public class AccountController : Controller
{
public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
{
UserManager = userManager;
SignInManager = signInManager;
}
public UserManager<ApplicationUser> UserManager { get; private set; }
[FromServices]
public UserManager<ApplicationUser> UserManager { get; set; }
public SignInManager<ApplicationUser> SignInManager { get; private set; }
[FromServices]
public SignInManager<ApplicationUser> SignInManager { get; set; }
//
// GET: /Account/Login

View File

@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using MusicStore.Models;
@ -11,16 +12,16 @@ namespace MusicStore.Controllers
public class CheckoutController : Controller
{
private const string PromoCode = "FREE";
private readonly MusicStoreContext _dbContext;
public CheckoutController(MusicStoreContext dbContext)
[FromServices]
public MusicStoreContext DbContext
{
_dbContext = dbContext;
}
get;
set;
}
//
// GET: /Checkout/
public IActionResult AddressAndPayment()
{
return View();
@ -31,7 +32,7 @@ namespace MusicStore.Controllers
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddressAndPayment(Order order)
public async Task<IActionResult> AddressAndPayment(Order order, CancellationToken requestAborted)
{
var formCollection = await Context.Request.ReadFormAsync();
@ -48,14 +49,14 @@ namespace MusicStore.Controllers
order.OrderDate = DateTime.Now;
//Add the Order
_dbContext.Orders.Add(order);
DbContext.Orders.Add(order);
//Process the order
var cart = ShoppingCart.GetCart(_dbContext, Context);
var cart = ShoppingCart.GetCart(DbContext, Context);
await cart.CreateOrder(order);
// Save all changes
await _dbContext.SaveChangesAsync(Context.RequestAborted);
await DbContext.SaveChangesAsync(requestAborted);
return RedirectToAction("Complete", new { id = order.OrderId });
}
@ -73,7 +74,7 @@ namespace MusicStore.Controllers
public async Task<IActionResult> Complete(int id)
{
// Validate customer owns this order
bool isValid = await _dbContext.Orders.AnyAsync(
bool isValid = await DbContext.Orders.AnyAsync(
o => o.OrderId == id &&
o.Username == Context.User.Identity.GetUserName());

View File

@ -10,13 +10,19 @@ namespace MusicStore.Controllers
{
public class HomeController : Controller
{
private readonly MusicStoreContext _dbContext;
private readonly IMemoryCache _cache;
public HomeController(MusicStoreContext dbContext, IMemoryCache memoryCache)
[FromServices]
public MusicStoreContext DbContext
{
_dbContext = dbContext;
_cache = memoryCache;
get;
set;
}
[FromServices]
public IMemoryCache Cache
{
get;
set;
}
//
@ -24,7 +30,7 @@ namespace MusicStore.Controllers
public async Task<IActionResult> Index()
{
// Get most popular albums
var albums = await _cache.GetOrSet("topselling", async context =>
var albums = await Cache.GetOrSet("topselling", async context =>
{
//Refresh it every 10 minutes. Let this be the last item to be removed by cache if cache GC kicks in.
context.SetAbsoluteExpiration(TimeSpan.FromMinutes(10));
@ -48,7 +54,7 @@ namespace MusicStore.Controllers
// the albums with the highest count
// TODO [EF] We don't query related data as yet, so the OrderByDescending isn't doing anything
return await _dbContext.Albums
return await DbContext.Albums
.OrderByDescending(a => a.OrderDetails.Count())
.Take(count)
.ToListAsync();

View File

@ -13,15 +13,12 @@ namespace MusicStore.Controllers
[Authorize]
public class ManageController : Controller
{
public ManageController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
{
UserManager = userManager;
SignInManager = signInManager;
}
[FromServices]
public UserManager<ApplicationUser> UserManager { get; set; }
public UserManager<ApplicationUser> UserManager { get; private set; }
public SignInManager<ApplicationUser> SignInManager { get; private set; }
[FromServices]
public SignInManager<ApplicationUser> SignInManager { get; set; }
//
// GET: /Manage/Index

View File

@ -1,4 +1,5 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.Framework.DependencyInjection;
@ -9,19 +10,18 @@ namespace MusicStore.Controllers
{
public class ShoppingCartController : Controller
{
private readonly MusicStoreContext _dbContext;
public ShoppingCartController(MusicStoreContext dbContext)
[FromServices]
public MusicStoreContext DbContext
{
_dbContext = dbContext;
get;
set;
}
//
// GET: /ShoppingCart/
public async Task<IActionResult> Index()
{
var cart = ShoppingCart.GetCart(_dbContext, Context);
var cart = ShoppingCart.GetCart(DbContext, Context);
// Set up our ViewModel
var viewModel = new ShoppingCartViewModel
@ -37,18 +37,18 @@ namespace MusicStore.Controllers
//
// GET: /ShoppingCart/AddToCart/5
public async Task<IActionResult> AddToCart(int id)
public async Task<IActionResult> AddToCart(int id, CancellationToken requestAborted)
{
// Retrieve the album from the database
var addedAlbum = _dbContext.Albums
var addedAlbum = DbContext.Albums
.Single(album => album.AlbumId == id);
// Add it to the shopping cart
var cart = ShoppingCart.GetCart(_dbContext, Context);
var cart = ShoppingCart.GetCart(DbContext, Context);
cart.AddToCart(addedAlbum);
await _dbContext.SaveChangesAsync(Context.RequestAborted);
await DbContext.SaveChangesAsync(requestAborted);
// Go back to the main store page for more shopping
return RedirectToAction("Index");
@ -57,7 +57,7 @@ namespace MusicStore.Controllers
//
// AJAX: /ShoppingCart/RemoveFromCart/5
[HttpPost]
public async Task<IActionResult> RemoveFromCart(int id)
public async Task<IActionResult> RemoveFromCart(int id, CancellationToken requestAborted)
{
var formParameters = await Context.Request.ReadFormAsync();
var requestVerification = formParameters["RequestVerificationToken"];
@ -79,10 +79,10 @@ namespace MusicStore.Controllers
antiForgery.Validate(Context, new AntiForgeryTokenSet(formToken, cookieToken));
// Retrieve the current user's shopping cart
var cart = ShoppingCart.GetCart(_dbContext, Context);
var cart = ShoppingCart.GetCart(DbContext, Context);
// Get the name of the album to display confirmation
var cartItem = await _dbContext.CartItems
var cartItem = await DbContext.CartItems
.Where(item => item.CartItemId == id)
.Include(c => c.Album)
.SingleOrDefaultAsync();
@ -90,7 +90,7 @@ namespace MusicStore.Controllers
// Remove from cart
int itemCount = cart.RemoveFromCart(id);
await _dbContext.SaveChangesAsync(Context.RequestAborted);
await DbContext.SaveChangesAsync(requestAborted);
string removed = (itemCount > 0) ? " 1 copy of " : string.Empty;

View File

@ -9,32 +9,35 @@ namespace MusicStore.Controllers
{
public class StoreController : Controller
{
private readonly MusicStoreContext _dbContext;
private readonly IMemoryCache _cache;
public StoreController(MusicStoreContext context, IMemoryCache memoryCache)
[FromServices]
public MusicStoreContext DbContext
{
_dbContext = context;
_cache = memoryCache;
get;
set;
}
[FromServices]
public IMemoryCache Cache
{
get;
set;
}
//
// GET: /Store/
public async Task<IActionResult> Index()
{
var genres = await _dbContext.Genres.ToListAsync();
var genres = await DbContext.Genres.ToListAsync();
return View(genres);
}
//
// GET: /Store/Browse?genre=Disco
public async Task<IActionResult> Browse(string genre)
{
// Retrieve Genre genre and its Associated associated Albums albums from database
var genreModel = await _dbContext.Genres
var genreModel = await DbContext.Genres
.Include(g => g.Albums)
.Where(g => g.Name == genre)
.FirstOrDefaultAsync();
@ -44,12 +47,12 @@ namespace MusicStore.Controllers
public async Task<IActionResult> Details(int id)
{
var album = await _cache.GetOrSet(string.Format("album_{0}", id), async context =>
var album = await Cache.GetOrSet(string.Format("album_{0}", id), async context =>
{
//Remove it from cache if not retrieved in last 10 minutes
context.SetSlidingExpiration(TimeSpan.FromMinutes(10));
return await _dbContext.Albums
return await DbContext.Albums
.Where(a => a.AlbumId == id)
.Include(a => a.Artist)
.Include(a => a.Genre)