diff --git a/src/MusicStore/Areas/Admin/Controllers/StoreManagerController.cs b/src/MusicStore/Areas/Admin/Controllers/StoreManagerController.cs index e49d345a5e..f2271dfd51 100644 --- a/src/MusicStore/Areas/Admin/Controllers/StoreManagerController.cs +++ b/src/MusicStore/Areas/Admin/Controllers/StoreManagerController.cs @@ -1,15 +1,15 @@ -using Microsoft.AspNet.Mvc; +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Infrastructure; using Microsoft.Data.Entity; -using MusicStore.Models; -using System.Linq; -using MusicStore.Hubs; -using MusicStore.ViewModels; using Microsoft.Framework.Cache.Memory; -using System; -using System.Threading.Tasks; +using MusicStore.Hubs; +using MusicStore.Models; +using MusicStore.ViewModels; namespace MusicStore.Areas.Admin.Controllers { @@ -17,45 +17,29 @@ namespace MusicStore.Areas.Admin.Controllers [Microsoft.AspNet.Mvc.Authorize("ManageStore", "Allowed")] public class StoreManagerController : Controller { - private readonly MusicStoreContext db; - private IHubContext annoucementHub; - private readonly IMemoryCache cache; + private readonly MusicStoreContext _dbContext; + private readonly IMemoryCache _cache; + private IHubContext _announcementHub; - public StoreManagerController(MusicStoreContext context, IConnectionManager connectionManager, IMemoryCache memoryCache) + public StoreManagerController( + MusicStoreContext dbContext, + IConnectionManager connectionManager, + IMemoryCache memoryCache) { - db = context; - annoucementHub = connectionManager.GetHubContext(); - cache = memoryCache; + _dbContext = dbContext; + _announcementHub = connectionManager.GetHubContext(); + _cache = memoryCache; } // // GET: /StoreManager/ - public IActionResult Index() + public async Task Index() { - // TODO [EF] Swap to native support for loading related data when available - var albums = from album in db.Albums - join genre in db.Genres on album.GenreId equals genre.GenreId - join artist in db.Artists on album.ArtistId equals artist.ArtistId - select new Album() - { - ArtistId = album.ArtistId, - AlbumArtUrl = album.AlbumArtUrl, - AlbumId = album.AlbumId, - GenreId = album.GenreId, - Price = album.Price, - Title = album.Title, - Artist = new Artist() - { - ArtistId = album.ArtistId, - Name = artist.Name - }, - Genre = new Genre() - { - GenreId = album.GenreId, - Name = genre.Name - } - }; + var albums = await _dbContext.Albums + .Include(a => a.Genre) + .Include(a => a.Artist) + .ToListAsync(); return View(albums); } @@ -63,26 +47,29 @@ namespace MusicStore.Areas.Admin.Controllers // // GET: /StoreManager/Details/5 - public IActionResult Details(int id) + public async Task Details(int id) { - string cacheId = string.Format("album_{0}", id); - var album = cache.GetOrSet(cacheId, context => + var cacheKey = GetCacheKey(id); + + var album = await _cache.GetOrSet(GetCacheKey(id), async context => { - //Remove it from cache if not retrieved in last 10 minutes + //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 db.Albums.Where(a => a.AlbumId == id).FirstOrDefault(); + + //If this returns null how do we prevent the cache to store this. + return await _dbContext.Albums + .Where(a => a.AlbumId == id) + .Include(a => a.Artist) + .Include(a => a.Genre) + .FirstOrDefaultAsync(); }); if (album == null) { - cache.Remove(cacheId); + _cache.Remove(cacheKey); return View(album); } - // TODO [EF] We don't query related data as yet. We have to populate this until we do automatically. - album.Genre = db.Genres.Single(g => g.GenreId == album.GenreId); - album.Artist = db.Artists.Single(a => a.ArtistId == album.ArtistId); return View(album); } @@ -90,8 +77,8 @@ namespace MusicStore.Areas.Admin.Controllers // GET: /StoreManager/Create public IActionResult Create() { - ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name"); - ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name"); + ViewBag.GenreId = new SelectList(_dbContext.Genres, "GenreId", "Name"); + ViewBag.ArtistId = new SelectList(_dbContext.Artists, "ArtistId", "Name"); return View(); } @@ -102,31 +89,40 @@ namespace MusicStore.Areas.Admin.Controllers { if (ModelState.IsValid) { - await db.Albums.AddAsync(album, Context.RequestAborted); - await db.SaveChangesAsync(Context.RequestAborted); - annoucementHub.Clients.All.announcement(new AlbumData() { Title = album.Title, Url = Url.Action("Details", "Store", new { id = album.AlbumId }) }); - cache.Remove("latestAlbum"); + await _dbContext.Albums.AddAsync(album, Context.RequestAborted); + await _dbContext.SaveChangesAsync(Context.RequestAborted); + + var albumData = new AlbumData + { + Title = album.Title, + Url = Url.Action("Details", "Store", new { id = album.AlbumId }) + }; + + _announcementHub.Clients.All.announcement(albumData); + _cache.Remove("latestAlbum"); return RedirectToAction("Index"); } - ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId); - ViewBag.ArtistId = new SelectList(db.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); } // // GET: /StoreManager/Edit/5 - public IActionResult Edit(int id) + public async Task Edit(int id) { - Album album = db.Albums.Where(a => a.AlbumId == id).FirstOrDefault(); + var album = await _dbContext.Albums. + Where(a => a.AlbumId == id). + FirstOrDefaultAsync(); if (album == null) { return View(album); } - ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId); - ViewBag.ArtistId = new SelectList(db.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); } @@ -138,15 +134,15 @@ namespace MusicStore.Areas.Admin.Controllers { if (ModelState.IsValid) { - db.Entry(album).SetState(EntityState.Modified); - await db.SaveChangesAsync(Context.RequestAborted); + _dbContext.Entry(album).SetState(EntityState.Modified); + await _dbContext.SaveChangesAsync(Context.RequestAborted); //Invalidate the cache entry as it is modified - cache.Remove(string.Format("album_{0}", album.AlbumId)); + _cache.Remove(GetCacheKey(album.AlbumId)); return RedirectToAction("Index"); } - ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId); - ViewBag.ArtistId = new SelectList(db.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); } @@ -154,7 +150,7 @@ namespace MusicStore.Areas.Admin.Controllers // GET: /StoreManager/RemoveAlbum/5 public IActionResult RemoveAlbum(int id) { - Album album = db.Albums.Where(a => a.AlbumId == id).FirstOrDefault(); + var album = _dbContext.Albums.Where(a => a.AlbumId == id).FirstOrDefault(); return View(album); } @@ -163,19 +159,24 @@ namespace MusicStore.Areas.Admin.Controllers [HttpPost, ActionName("RemoveAlbum")] public async Task RemoveAlbumConfirmed(int id) { - Album album = db.Albums.Where(a => a.AlbumId == id).FirstOrDefault(); + var album = _dbContext.Albums.Where(a => a.AlbumId == id).FirstOrDefault(); if (album != null) { - db.Albums.Remove(album); - await db.SaveChangesAsync(Context.RequestAborted); + _dbContext.Albums.Remove(album); + await _dbContext.SaveChangesAsync(Context.RequestAborted); //Remove the cache entry as it is removed - cache.Remove(string.Format("album_{0}", id)); + _cache.Remove(GetCacheKey(id)); } return RedirectToAction("Index"); } + private static string GetCacheKey(int id) + { + return string.Format("album_{0}", id); + } + #if TESTING // // GET: /StoreManager/GetAlbumIdFromName @@ -183,7 +184,7 @@ namespace MusicStore.Areas.Admin.Controllers [HttpGet] public IActionResult GetAlbumIdFromName(string albumName) { - var album = db.Albums.Where(a => a.Title == albumName).FirstOrDefault(); + var album = _dbContext.Albums.Where(a => a.Title == albumName).FirstOrDefault(); if (album == null) { diff --git a/src/MusicStore/Components/AnnouncementComponent.cs b/src/MusicStore/Components/AnnouncementComponent.cs index 8fa4a0b2d9..ad96b62802 100644 --- a/src/MusicStore/Components/AnnouncementComponent.cs +++ b/src/MusicStore/Components/AnnouncementComponent.cs @@ -2,26 +2,26 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Mvc; -using MusicStore.Models; using Microsoft.Framework.Cache.Memory; +using MusicStore.Models; namespace MusicStore.Components { [ViewComponent(Name = "Announcement")] public class AnnouncementComponent : ViewComponent { - private readonly MusicStoreContext db; - private readonly IMemoryCache cache; + private readonly MusicStoreContext _dbContext; + private readonly IMemoryCache _cache; - public AnnouncementComponent(MusicStoreContext context, IMemoryCache memoryCache) + public AnnouncementComponent(MusicStoreContext dbContext, IMemoryCache memoryCache) { - db = context; - cache = memoryCache; + _dbContext = dbContext; + _cache = memoryCache; } public async Task 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,15 +32,12 @@ namespace MusicStore.Components private Task GetLatestAlbum() { - var latestAlbum = db.Albums.OrderByDescending(a => a.Created).FirstOrDefault(); - if ((latestAlbum.Created - DateTime.UtcNow).TotalDays <= 2) - { - return Task.FromResult(latestAlbum); - } - else - { - return Task.FromResult(null); - } + var latestAlbum = _dbContext.Albums + .OrderByDescending(a => a.Created) + .Where(a => (a.Created - DateTime.UtcNow).TotalDays <= 2) + .FirstOrDefaultAsync(); + + return latestAlbum; } } } \ No newline at end of file diff --git a/src/MusicStore/Components/CartSummaryComponent.cs b/src/MusicStore/Components/CartSummaryComponent.cs index cff3a3a342..b83ae752d4 100644 --- a/src/MusicStore/Components/CartSummaryComponent.cs +++ b/src/MusicStore/Components/CartSummaryComponent.cs @@ -1,18 +1,18 @@ -using Microsoft.AspNet.Mvc; -using MusicStore.Models; -using System.Linq; +using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using MusicStore.Models; namespace MusicStore.Components { [ViewComponent(Name = "CartSummary")] public class CartSummaryComponent : ViewComponent { - private readonly MusicStoreContext db; + private readonly MusicStoreContext _dbContext; - public CartSummaryComponent(MusicStoreContext context) + public CartSummaryComponent(MusicStoreContext dbContext) { - db = context; + _dbContext = dbContext; } public async Task InvokeAsync() @@ -25,15 +25,13 @@ namespace MusicStore.Components return View(); } - private Task> GetCartItems() + private async Task> GetCartItems() { - var cart = ShoppingCart.GetCart(db, Context); + var cart = ShoppingCart.GetCart(_dbContext, Context); - var cartItems = cart.GetCartItems() + return (await cart.GetCartItems()) .Select(a => a.Album.Title) .OrderBy(x => x); - - return Task.FromResult(cartItems); } } } \ No newline at end of file diff --git a/src/MusicStore/Components/GenreMenuComponent.cs b/src/MusicStore/Components/GenreMenuComponent.cs index 7a8aaa2a2f..f1ecf0b8f6 100644 --- a/src/MusicStore/Components/GenreMenuComponent.cs +++ b/src/MusicStore/Components/GenreMenuComponent.cs @@ -1,20 +1,19 @@ -using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Mvc; using MusicStore.Models; -using System.Collections.Generic; namespace MusicStore.Components { [ViewComponent(Name = "GenreMenu")] public class GenreMenuComponent : ViewComponent { - private readonly MusicStoreContext db; + private readonly MusicStoreContext _dbContext; - public GenreMenuComponent(MusicStoreContext context) + public GenreMenuComponent(MusicStoreContext dbContext) { - db = context; + _dbContext = dbContext; } public async Task InvokeAsync() @@ -24,10 +23,10 @@ namespace MusicStore.Components return View(genres); } - private Task> GetGenres() + private async Task> GetGenres() { // TODO [EF] We don't query related data as yet, so the OrderByDescending isn't doing anything - //var genres = db.Genres + //var genres = _dbContext.Genres //.OrderByDescending( // g => g.Albums.Sum( // a => a.OrderDetails.Sum( @@ -35,8 +34,7 @@ namespace MusicStore.Components //.Take(9) //.ToList(); - var genres = db.Genres.ToList(); - return Task.FromResult(genres); + return await _dbContext.Genres.Take(9).ToListAsync(); } } } \ No newline at end of file diff --git a/src/MusicStore/Controllers/AccountController.cs b/src/MusicStore/Controllers/AccountController.cs index 663577a61c..007b40b4c4 100644 --- a/src/MusicStore/Controllers/AccountController.cs +++ b/src/MusicStore/Controllers/AccountController.cs @@ -1,11 +1,11 @@ using System.Linq; +using System.Security.Claims; using System.Security.Principal; using System.Threading.Tasks; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.Rendering; using MusicStore.Models; -using System.Security.Claims; namespace MusicStore.Controllers { @@ -338,8 +338,7 @@ namespace MusicStore.Controllers [AllowAnonymous] public async Task ExternalLoginCallback(string returnUrl = null) { - //https://github.com/aspnet/Identity/issues/216 - var loginInfo = await SignInManager.GetExternalLoginInfoAsync(); + var loginInfo = await SignInManager.GetExternalLoginInfoAsync(cancellationToken: Context.RequestAborted); if (loginInfo == null) { return RedirectToAction("Login"); @@ -381,8 +380,7 @@ namespace MusicStore.Controllers if (ModelState.IsValid) { // Get the information about the user from the external login provider - //https://github.com/aspnet/Identity/issues/216 - var info = await SignInManager.GetExternalLoginInfoAsync(); + var info = await SignInManager.GetExternalLoginInfoAsync(cancellationToken: Context.RequestAborted); if (info == null) { return View("ExternalLoginFailure"); diff --git a/src/MusicStore/Controllers/CheckoutController.cs b/src/MusicStore/Controllers/CheckoutController.cs index 6d84dcb4b1..f8d4e1f3f3 100644 --- a/src/MusicStore/Controllers/CheckoutController.cs +++ b/src/MusicStore/Controllers/CheckoutController.cs @@ -1,24 +1,23 @@ -using Microsoft.AspNet.Mvc; -using MusicStore.Models; -using System; +using System; using System.Linq; using System.Security.Principal; using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using MusicStore.Models; namespace MusicStore.Controllers { [Authorize] public class CheckoutController : Controller { - private readonly MusicStoreContext db; - - public CheckoutController(MusicStoreContext context) + private const string PromoCode = "FREE"; + private readonly MusicStoreContext _dbContext; + + public CheckoutController(MusicStoreContext dbContext) { - db = context; + _dbContext = dbContext; } - const string PromoCode = "FREE"; - // // GET: /Checkout/ @@ -49,14 +48,14 @@ namespace MusicStore.Controllers order.OrderDate = DateTime.Now; //Add the Order - await db.Orders.AddAsync(order, Context.RequestAborted); + await _dbContext.Orders.AddAsync(order, Context.RequestAborted); //Process the order - var cart = ShoppingCart.GetCart(db, Context); - cart.CreateOrder(order); + var cart = ShoppingCart.GetCart(_dbContext, Context); + await cart.CreateOrder(order); // Save all changes - await db.SaveChangesAsync(Context.RequestAborted); + await _dbContext.SaveChangesAsync(Context.RequestAborted); return RedirectToAction("Complete", new { id = order.OrderId }); @@ -75,7 +74,7 @@ namespace MusicStore.Controllers public IActionResult Complete(int id) { // Validate customer owns this order - bool isValid = db.Orders.Any( + bool isValid = _dbContext.Orders.Any( o => o.OrderId == id && o.Username == Context.User.Identity.GetUserName()); diff --git a/src/MusicStore/Controllers/HomeController.cs b/src/MusicStore/Controllers/HomeController.cs index 37c7d4367f..ee63cac9e1 100644 --- a/src/MusicStore/Controllers/HomeController.cs +++ b/src/MusicStore/Controllers/HomeController.cs @@ -1,34 +1,35 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.Framework.Cache.Memory; +using System.Threading.Tasks; using Microsoft.AspNet.Mvc; +using Microsoft.Framework.Cache.Memory; using MusicStore.Models; namespace MusicStore.Controllers { public class HomeController : Controller { - private readonly MusicStoreContext db; - private readonly IMemoryCache cache; + private readonly MusicStoreContext _dbContext; + private readonly IMemoryCache _cache; - public HomeController(MusicStoreContext context, IMemoryCache memoryCache) + public HomeController(MusicStoreContext dbContext, IMemoryCache memoryCache) { - db = context; - cache = memoryCache; + _dbContext = dbContext; + _cache = memoryCache; } // // GET: /Home/ - public IActionResult Index() + public async Task Index() { // Get most popular albums - var albums = cache.GetOrSet("topselling", 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)); context.SetPriority(CachePreservationPriority.High); - return GetTopSellingAlbums(6); + return await GetTopSellingAlbums(6); }); return View(albums); @@ -41,16 +42,16 @@ namespace MusicStore.Controllers return View("~/Views/Shared/Error.cshtml"); } - private List GetTopSellingAlbums(int count) + private async Task> GetTopSellingAlbums(int count) { // Group the order details by album and return // the albums with the highest count // TODO [EF] We don't query related data as yet, so the OrderByDescending isn't doing anything - return db.Albums + return await _dbContext.Albums .OrderByDescending(a => a.OrderDetails.Count()) .Take(count) - .ToList(); + .ToListAsync(); } } } \ No newline at end of file diff --git a/src/MusicStore/Controllers/ManageController.cs b/src/MusicStore/Controllers/ManageController.cs index c91fa92e19..2b3e914398 100644 --- a/src/MusicStore/Controllers/ManageController.cs +++ b/src/MusicStore/Controllers/ManageController.cs @@ -1,10 +1,9 @@ -using System.Threading.Tasks; +using System.Linq; using System.Security.Principal; +using System.Threading.Tasks; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Mvc; using MusicStore.Models; -using System.Linq; -using Microsoft.AspNet.Http; namespace MusicStore.Controllers { @@ -89,8 +88,7 @@ namespace MusicStore.Controllers } var user = await GetCurrentUserAsync(); // Generate the token and send it - //https://github.com/aspnet/Identity/issues/217 - var code = await UserManager.GenerateChangePhoneNumberTokenAsync(user, model.Number); + var code = await UserManager.GenerateChangePhoneNumberTokenAsync(user, model.Number, cancellationToken: Context.RequestAborted); var message = new IdentityMessage { Destination = model.Number, @@ -139,8 +137,7 @@ namespace MusicStore.Controllers // This code allows you exercise the flow without actually sending codes // For production use please register a SMS provider in IdentityConfig and generate a code here. #if DEMO - //https://github.com/aspnet/Identity/issues/217 - ViewBag.Code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber); + ViewBag.Code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber, cancellationToken: Context.RequestAborted); #endif return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber }); } @@ -318,16 +315,17 @@ namespace MusicStore.Controllers public async Task LinkLoginCallback() { var user = await GetCurrentUserAsync(); - if(user == null) + if (user == null) { return View("Error"); } - //https://github.com/aspnet/Identity/issues/216 - var loginInfo = await SignInManager.GetExternalLoginInfoAsync(User.Identity.GetUserId()); + + var loginInfo = await SignInManager.GetExternalLoginInfoAsync(User.Identity.GetUserId(), cancellationToken: Context.RequestAborted); if (loginInfo == null) { return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error }); } + var result = await UserManager.AddLoginAsync(user, loginInfo); var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error; return RedirectToAction("ManageLogins", new { Message = message }); @@ -343,17 +341,6 @@ namespace MusicStore.Controllers } } - //TODO: No caller - do we need this? - private async Task HasPhoneNumber() - { - var user = await UserManager.FindByIdAsync(User.Identity.GetUserId(), cancellationToken: Context.RequestAborted); - if (user != null) - { - return user.PhoneNumber != null; - } - return false; - } - public enum ManageMessageId { AddPhoneSuccess, @@ -370,18 +357,7 @@ namespace MusicStore.Controllers { return await UserManager.FindByIdAsync(Context.User.Identity.GetUserId(), cancellationToken: Context.RequestAborted); } - - private IActionResult RedirectToLocal(string returnUrl) - { - if (Url.IsLocalUrl(returnUrl)) - { - return Redirect(returnUrl); - } - else - { - return RedirectToAction("Index", "Home"); - } - } + #endregion } } \ No newline at end of file diff --git a/src/MusicStore/Controllers/ShoppingCartController.cs b/src/MusicStore/Controllers/ShoppingCartController.cs index 828cf10469..7c6f8b9aa5 100644 --- a/src/MusicStore/Controllers/ShoppingCartController.cs +++ b/src/MusicStore/Controllers/ShoppingCartController.cs @@ -1,33 +1,33 @@ -using Microsoft.AspNet.Mvc; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.Framework.DependencyInjection; using MusicStore.Models; using MusicStore.ViewModels; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Framework.DependencyInjection; namespace MusicStore.Controllers { public class ShoppingCartController : Controller { - private readonly MusicStoreContext db; + private readonly MusicStoreContext _dbContext; - public ShoppingCartController(MusicStoreContext context) + public ShoppingCartController(MusicStoreContext dbContext) { - db = context; + _dbContext = dbContext; } // // GET: /ShoppingCart/ - public IActionResult Index() + public async Task Index() { - var cart = ShoppingCart.GetCart(db, Context); + var cart = ShoppingCart.GetCart(_dbContext, Context); // Set up our ViewModel var viewModel = new ShoppingCartViewModel { - CartItems = cart.GetCartItems(), - CartTotal = cart.GetTotal() + CartItems = await cart.GetCartItems(), + CartTotal = await cart.GetTotal() }; // Return the view @@ -40,15 +40,15 @@ namespace MusicStore.Controllers public async Task AddToCart(int id) { // Retrieve the album from the database - var addedAlbum = db.Albums + var addedAlbum = _dbContext.Albums .Single(album => album.AlbumId == id); // Add it to the shopping cart - var cart = ShoppingCart.GetCart(db, Context); + var cart = ShoppingCart.GetCart(_dbContext, Context); cart.AddToCart(addedAlbum); - await db.SaveChangesAsync(Context.RequestAborted); + await _dbContext.SaveChangesAsync(Context.RequestAborted); // Go back to the main store page for more shopping return RedirectToAction("Index"); @@ -79,17 +79,18 @@ namespace MusicStore.Controllers antiForgery.Validate(Context, new AntiForgeryTokenSet(formToken, cookieToken)); // Retrieve the current user's shopping cart - var cart = ShoppingCart.GetCart(db, Context); + var cart = ShoppingCart.GetCart(_dbContext, Context); // Get the name of the album to display confirmation - // TODO [EF] Turn into one query once query of related data is enabled - int albumId = db.CartItems.Single(item => item.CartItemId == id).AlbumId; - string albumName = db.Albums.Single(a => a.AlbumId == albumId).Title; + var cartItem = await _dbContext.CartItems + .Where(item => item.CartItemId == id) + .Include(c => c.Album) + .SingleOrDefaultAsync(); // Remove from cart int itemCount = cart.RemoveFromCart(id); - await db.SaveChangesAsync(Context.RequestAborted); + await _dbContext.SaveChangesAsync(Context.RequestAborted); string removed = (itemCount > 0) ? " 1 copy of " : string.Empty; @@ -97,10 +98,10 @@ namespace MusicStore.Controllers var results = new ShoppingCartRemoveViewModel { - Message = removed + albumName + + Message = removed + cartItem.Album.Title + " has been removed from your shopping cart.", - CartTotal = cart.GetTotal(), - CartCount = cart.GetCount(), + CartTotal = await cart.GetTotal(), + CartCount = await cart.GetCount(), ItemCount = itemCount, DeleteId = id }; diff --git a/src/MusicStore/Controllers/StoreController.cs b/src/MusicStore/Controllers/StoreController.cs index 6da044bd15..d2e7f00aa3 100644 --- a/src/MusicStore/Controllers/StoreController.cs +++ b/src/MusicStore/Controllers/StoreController.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using Microsoft.AspNet.Mvc; using Microsoft.Framework.Cache.Memory; using MusicStore.Models; @@ -8,21 +9,21 @@ namespace MusicStore.Controllers { public class StoreController : Controller { - private readonly MusicStoreContext db; - private readonly IMemoryCache cache; + private readonly MusicStoreContext _dbContext; + private readonly IMemoryCache _cache; public StoreController(MusicStoreContext context, IMemoryCache memoryCache) { - db = context; - cache = memoryCache; + _dbContext = context; + _cache = memoryCache; } // // GET: /Store/ - public IActionResult Index() + public async Task Index() { - var genres = db.Genres.ToList(); + var genres = await _dbContext.Genres.ToListAsync(); return View(genres); } @@ -30,22 +31,29 @@ namespace MusicStore.Controllers // // GET: /Store/Browse?genre=Disco - public IActionResult Browse(string genre) + public async Task Browse(string genre) { // Retrieve Genre genre and its Associated associated Albums albums from database - var genreModel = db.Genres.Include(g => g.Albums).Where(g => g.Name == genre).FirstOrDefault(); + var genreModel = await _dbContext.Genres + .Include(g => g.Albums) + .Where(g => g.Name == genre) + .FirstOrDefaultAsync(); + return View(genreModel); } - public IActionResult Details(int id) + public async Task Details(int id) { - var album = cache.GetOrSet(string.Format("album_{0}", id), 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)); - var albumData = db.Albums.Where(a => a.AlbumId == id).Include(a => a.Artist).Include(a => a.Genre).ToList().FirstOrDefault(); - return albumData; + return await _dbContext.Albums + .Where(a => a.AlbumId == id) + .Include(a => a.Artist) + .Include(a => a.Genre) + .FirstOrDefaultAsync(); }); return View(album); diff --git a/src/MusicStore/IdentityProviders.cs b/src/MusicStore/IdentityProviders.cs index c1c48eac57..7282f7a346 100644 --- a/src/MusicStore/IdentityProviders.cs +++ b/src/MusicStore/IdentityProviders.cs @@ -1,7 +1,6 @@ -using Microsoft.AspNet.Identity; -using MusicStore.Models; -using System.Threading.Tasks; using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNet.Identity; namespace MusicStore { diff --git a/src/MusicStore/Mocks/Common/CustomStateDataFormat.cs b/src/MusicStore/Mocks/Common/CustomStateDataFormat.cs index b91c141c7f..e0f6ea586e 100644 --- a/src/MusicStore/Mocks/Common/CustomStateDataFormat.cs +++ b/src/MusicStore/Mocks/Common/CustomStateDataFormat.cs @@ -6,17 +6,17 @@ namespace MusicStore.Mocks.Common { public class CustomStateDataFormat : ISecureDataFormat { - private static string lastSavedAuthenticationProperties; + private static string _lastSavedAuthenticationProperties; public string Protect(AuthenticationProperties data) { - lastSavedAuthenticationProperties = Serialize(data); + _lastSavedAuthenticationProperties = Serialize(data); return "ValidStateData"; } public AuthenticationProperties Unprotect(string state) { - return state == "ValidStateData" ? DeSerialize(lastSavedAuthenticationProperties) : null; + return state == "ValidStateData" ? DeSerialize(_lastSavedAuthenticationProperties) : null; } private string Serialize(AuthenticationProperties data) diff --git a/src/MusicStore/Mocks/Facebook/FacebookMockBackChannelHttpHandler.cs b/src/MusicStore/Mocks/Facebook/FacebookMockBackChannelHttpHandler.cs index 447e7837fd..43787a14f5 100644 --- a/src/MusicStore/Mocks/Facebook/FacebookMockBackChannelHttpHandler.cs +++ b/src/MusicStore/Mocks/Facebook/FacebookMockBackChannelHttpHandler.cs @@ -1,8 +1,8 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MusicStore.Mocks.Common; using Microsoft.AspNet.WebUtilities; +using MusicStore.Mocks.Common; namespace MusicStore.Mocks.Facebook { diff --git a/src/MusicStore/Mocks/Facebook/FacebookNotifications.cs b/src/MusicStore/Mocks/Facebook/FacebookNotifications.cs index ce6acec454..f2158ac349 100644 --- a/src/MusicStore/Mocks/Facebook/FacebookNotifications.cs +++ b/src/MusicStore/Mocks/Facebook/FacebookNotifications.cs @@ -1,11 +1,11 @@ -using Microsoft.AspNet.Identity; -using Microsoft.AspNet.Security.Facebook; -using Microsoft.AspNet.Security.OAuth; -using MusicStore.Mocks.Common; -using System; +using System; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Security.Facebook; +using Microsoft.AspNet.Security.OAuth; +using MusicStore.Mocks.Common; namespace MusicStore.Mocks.Facebook { diff --git a/src/MusicStore/Mocks/Google/GoogleMockBackChannelHttpHandler.cs b/src/MusicStore/Mocks/Google/GoogleMockBackChannelHttpHandler.cs index c436f6c8af..6b330f0491 100644 --- a/src/MusicStore/Mocks/Google/GoogleMockBackChannelHttpHandler.cs +++ b/src/MusicStore/Mocks/Google/GoogleMockBackChannelHttpHandler.cs @@ -1,7 +1,7 @@ using System.Net.Http; +using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Text; using Microsoft.AspNet.WebUtilities; namespace MusicStore.Mocks.Google diff --git a/src/MusicStore/Mocks/Google/GoogleNotifications.cs b/src/MusicStore/Mocks/Google/GoogleNotifications.cs index 9277fc7876..348885e7af 100644 --- a/src/MusicStore/Mocks/Google/GoogleNotifications.cs +++ b/src/MusicStore/Mocks/Google/GoogleNotifications.cs @@ -1,11 +1,11 @@ -using Microsoft.AspNet.Identity; -using Microsoft.AspNet.Security.Google; -using Microsoft.AspNet.Security.OAuth; -using MusicStore.Mocks.Common; -using System; +using System; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Security.Google; +using Microsoft.AspNet.Security.OAuth; +using MusicStore.Mocks.Common; namespace MusicStore.Mocks.Google { diff --git a/src/MusicStore/Mocks/MicrosoftAccount/MicrosoftAccountMockBackChannelHandler.cs b/src/MusicStore/Mocks/MicrosoftAccount/MicrosoftAccountMockBackChannelHandler.cs index a1b1e0718a..0de1e470a9 100644 --- a/src/MusicStore/Mocks/MicrosoftAccount/MicrosoftAccountMockBackChannelHandler.cs +++ b/src/MusicStore/Mocks/MicrosoftAccount/MicrosoftAccountMockBackChannelHandler.cs @@ -1,7 +1,7 @@ using System.Net.Http; +using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Text; using Microsoft.AspNet.WebUtilities; namespace MusicStore.Mocks.MicrosoftAccount diff --git a/src/MusicStore/Mocks/MicrosoftAccount/MicrosoftAccountNotifications.cs b/src/MusicStore/Mocks/MicrosoftAccount/MicrosoftAccountNotifications.cs index 49d4c19779..d6178b97e9 100644 --- a/src/MusicStore/Mocks/MicrosoftAccount/MicrosoftAccountNotifications.cs +++ b/src/MusicStore/Mocks/MicrosoftAccount/MicrosoftAccountNotifications.cs @@ -1,11 +1,11 @@ -using Microsoft.AspNet.Identity; -using Microsoft.AspNet.Security.MicrosoftAccount; -using Microsoft.AspNet.Security.OAuth; -using MusicStore.Mocks.Common; -using System; +using System; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Security.MicrosoftAccount; +using Microsoft.AspNet.Security.OAuth; +using MusicStore.Mocks.Common; namespace MusicStore.Mocks.MicrosoftAccount { diff --git a/src/MusicStore/Mocks/Twitter/CustomTwitterStateDataFormat.cs b/src/MusicStore/Mocks/Twitter/CustomTwitterStateDataFormat.cs index 58fe11967c..9d0ef8faed 100644 --- a/src/MusicStore/Mocks/Twitter/CustomTwitterStateDataFormat.cs +++ b/src/MusicStore/Mocks/Twitter/CustomTwitterStateDataFormat.cs @@ -9,18 +9,18 @@ namespace MusicStore.Mocks.Twitter /// public class CustomTwitterStateDataFormat : ISecureDataFormat { - private static string lastSavedRequestToken; + private static string _lastSavedRequestToken; public string Protect(RequestToken data) { data.Token = "valid_oauth_token"; - lastSavedRequestToken = Serialize(data); + _lastSavedRequestToken = Serialize(data); return "valid_oauth_token"; } public RequestToken Unprotect(string state) { - return state == "valid_oauth_token" ? DeSerialize(lastSavedRequestToken) : null; + return state == "valid_oauth_token" ? DeSerialize(_lastSavedRequestToken) : null; } private string Serialize(RequestToken data) diff --git a/src/MusicStore/Mocks/Twitter/TwitterMockBackChannelHttpHandler.cs b/src/MusicStore/Mocks/Twitter/TwitterMockBackChannelHttpHandler.cs index 3fba8c684b..a6ab9a9fef 100644 --- a/src/MusicStore/Mocks/Twitter/TwitterMockBackChannelHttpHandler.cs +++ b/src/MusicStore/Mocks/Twitter/TwitterMockBackChannelHttpHandler.cs @@ -1,10 +1,10 @@ using System; +using System.Collections.Generic; +using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.WebUtilities; -using System.Collections.Generic; -using System.Net; namespace MusicStore.Mocks.Twitter { @@ -13,7 +13,7 @@ namespace MusicStore.Mocks.Twitter /// public class TwitterMockBackChannelHttpHandler : HttpMessageHandler { - private static bool RequestTokenEndpointInvoked = false; + private static bool _requestTokenEndpointInvoked = false; protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { @@ -25,7 +25,7 @@ namespace MusicStore.Mocks.Twitter var formData = FormHelpers.ParseForm(await request.Content.ReadAsStringAsync()); if (formData["oauth_verifier"] == "valid_oauth_verifier") { - if (RequestTokenEndpointInvoked) + if (_requestTokenEndpointInvoked) { var response_Form_data = new List>() { @@ -53,7 +53,7 @@ namespace MusicStore.Mocks.Twitter new KeyValuePair("oauth_token_secret", "valid_oauth_token_secret") }; - RequestTokenEndpointInvoked = true; + _requestTokenEndpointInvoked = true; response.Content = new FormUrlEncodedContent(response_Form_data); } diff --git a/src/MusicStore/Mocks/Twitter/TwitterNotifications.cs b/src/MusicStore/Mocks/Twitter/TwitterNotifications.cs index 5cf3b2c21b..7f88927b89 100644 --- a/src/MusicStore/Mocks/Twitter/TwitterNotifications.cs +++ b/src/MusicStore/Mocks/Twitter/TwitterNotifications.cs @@ -1,9 +1,9 @@ -using Microsoft.AspNet.Identity; -using Microsoft.AspNet.Security.Twitter; -using MusicStore.Mocks.Common; -using System.Linq; +using System.Linq; using System.Security.Claims; using System.Threading.Tasks; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Security.Twitter; +using MusicStore.Mocks.Common; namespace MusicStore.Mocks.Twitter { diff --git a/src/MusicStore/Models/AccountViewModels.cs b/src/MusicStore/Models/AccountViewModels.cs index 248b510f0f..38a9735292 100644 --- a/src/MusicStore/Models/AccountViewModels.cs +++ b/src/MusicStore/Models/AccountViewModels.cs @@ -1,6 +1,6 @@ -using Microsoft.AspNet.Mvc.Rendering; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Microsoft.AspNet.Mvc.Rendering; namespace MusicStore.Models { diff --git a/src/MusicStore/Models/Album.cs b/src/MusicStore/Models/Album.cs index bcd151feda..bd0aafd6d3 100644 --- a/src/MusicStore/Models/Album.cs +++ b/src/MusicStore/Models/Album.cs @@ -1,7 +1,7 @@ -using Microsoft.AspNet.Mvc.ModelBinding; -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Microsoft.AspNet.Mvc.ModelBinding; namespace MusicStore.Models { diff --git a/src/MusicStore/Models/ManageViewModels.cs b/src/MusicStore/Models/ManageViewModels.cs index e115fda993..426d8a47e1 100644 --- a/src/MusicStore/Models/ManageViewModels.cs +++ b/src/MusicStore/Models/ManageViewModels.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using Microsoft.AspNet.Http.Security; using Microsoft.AspNet.Identity; diff --git a/src/MusicStore/Models/Order.cs b/src/MusicStore/Models/Order.cs index 12aa2fb8e7..cd2e91dedf 100644 --- a/src/MusicStore/Models/Order.cs +++ b/src/MusicStore/Models/Order.cs @@ -1,6 +1,6 @@ -using Microsoft.AspNet.Mvc.ModelBinding; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Microsoft.AspNet.Mvc.ModelBinding; namespace MusicStore.Models { diff --git a/src/MusicStore/Models/SampleData.cs b/src/MusicStore/Models/SampleData.cs index 9adee1c328..a7d43f1f1c 100644 --- a/src/MusicStore/Models/SampleData.cs +++ b/src/MusicStore/Models/SampleData.cs @@ -6,8 +6,8 @@ using System.Threading.Tasks; using Microsoft.AspNet.Identity; using Microsoft.Data.Entity; using Microsoft.Data.Entity.SqlServer; -using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.ConfigurationModel; +using Microsoft.Framework.DependencyInjection; namespace MusicStore.Models { diff --git a/src/MusicStore/Models/ShoppingCart.cs b/src/MusicStore/Models/ShoppingCart.cs index 9b52445615..9ce5c776c9 100644 --- a/src/MusicStore/Models/ShoppingCart.cs +++ b/src/MusicStore/Models/ShoppingCart.cs @@ -1,18 +1,19 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Microsoft.AspNet.Http; namespace MusicStore.Models { - public partial class ShoppingCart + public class ShoppingCart { - MusicStoreContext _db; - string ShoppingCartId { get; set; } + private readonly MusicStoreContext _dbContext; + private string ShoppingCartId { get; set; } - public ShoppingCart(MusicStoreContext db) + public ShoppingCart(MusicStoreContext dbContext) { - _db = db; + _dbContext = dbContext; } public static ShoppingCart GetCart(MusicStoreContext db, HttpContext context) @@ -25,7 +26,7 @@ namespace MusicStore.Models public void AddToCart(Album album) { // Get the matching cart and album instances - var cartItem = _db.CartItems.SingleOrDefault( + var cartItem = _dbContext.CartItems.SingleOrDefault( c => c.CartId == ShoppingCartId && c.AlbumId == album.AlbumId); @@ -40,7 +41,7 @@ namespace MusicStore.Models DateCreated = DateTime.Now }; - _db.CartItems.Add(cartItem); + _dbContext.CartItems.Add(cartItem); } else { @@ -52,7 +53,7 @@ namespace MusicStore.Models public int RemoveFromCart(int id) { // Get the cart - var cartItem = _db.CartItems.Single( + var cartItem = _dbContext.CartItems.Single( cart => cart.CartId == ShoppingCartId && cart.CartItemId == id); @@ -67,7 +68,7 @@ namespace MusicStore.Models } else { - _db.CartItems.Remove(cartItem); + _dbContext.CartItems.Remove(cartItem); } } @@ -76,71 +77,50 @@ namespace MusicStore.Models public void EmptyCart() { - var cartItems = _db.CartItems.Where(cart => cart.CartId == ShoppingCartId).ToArray(); - _db.CartItems.Remove(cartItems); + var cartItems = _dbContext.CartItems.Where(cart => cart.CartId == ShoppingCartId).ToArray(); + _dbContext.CartItems.Remove(cartItems); } - public List GetCartItems() + public async Task> GetCartItems() { - var cartItems = _db.CartItems.Where(cart => cart.CartId == ShoppingCartId).ToList(); - //TODO: Auto population of the related album data not available until EF feature is lighted up. - foreach (var cartItem in cartItems) - { - cartItem.Album = _db.Albums.Single(a => a.AlbumId == cartItem.AlbumId); - } - - return cartItems; + return await _dbContext.CartItems. + Where(cart => cart.CartId == ShoppingCartId). + Include(c => c.Album). + ToListAsync(); } - public int GetCount() + public async Task GetCount() { - int sum = 0; - //https://github.com/aspnet/EntityFramework/issues/557 // Get the count of each item in the cart and sum them up - var cartItemCounts = (from cartItems in _db.CartItems - where cartItems.CartId == ShoppingCartId - select (int?)cartItems.Count); - - cartItemCounts.ForEachAsync(carItemCount => - { - if (carItemCount.HasValue) - { - sum += carItemCount.Value; - } - }); - - // Return 0 if all entries are null - return sum; + return await (from cartItem in _dbContext.CartItems + where cartItem.CartId == ShoppingCartId + select cartItem.Count).SumAsync(); } - public decimal GetTotal() + public async Task GetTotal() { // Multiply album price by count of that album to get // the current price for each of those albums in the cart // sum all album price totals to get the cart total - // TODO Collapse to a single query once EF supports querying related data - decimal total = 0; - foreach (var item in _db.CartItems.Where(c => c.CartId == ShoppingCartId)) - { - var album = _db.Albums.Single(a => a.AlbumId == item.AlbumId); - total += item.Count * album.Price; - } - - return total; + // TODO: Use nav prop traversal instead of joins (EF #https://github.com/aspnet/EntityFramework/issues/325) + return await (from cartItem in _dbContext.CartItems + join album in _dbContext.Albums on cartItem.AlbumId equals album.AlbumId + where cartItem.CartId == ShoppingCartId + select cartItem.Count * album.Price).SumAsync(); } - public int CreateOrder(Order order) + public async Task CreateOrder(Order order) { decimal orderTotal = 0; - var cartItems = GetCartItems(); + var cartItems = await GetCartItems(); // Iterate over the items in the cart, adding the order details for each foreach (var item in cartItems) { //var album = _db.Albums.Find(item.AlbumId); - var album = _db.Albums.Single(a => a.AlbumId == item.AlbumId); + var album = _dbContext.Albums.Single(a => a.AlbumId == item.AlbumId); var orderDetail = new OrderDetail { @@ -153,7 +133,7 @@ namespace MusicStore.Models // Set the order total of the shopping cart orderTotal += (item.Count * album.Price); - _db.OrderDetails.Add(orderDetail); + _dbContext.OrderDetails.Add(orderDetail); } // Set the order's total to the orderTotal count diff --git a/src/MusicStore/Program.cs b/src/MusicStore/Program.cs index 8609c15924..23812e98e1 100644 --- a/src/MusicStore/Program.cs +++ b/src/MusicStore/Program.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNet.Hosting; using Microsoft.Framework.ConfigurationModel; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection.Fallback; -using Microsoft.Framework.Runtime; namespace MusicStore { diff --git a/src/MusicStore/Startup.cs b/src/MusicStore/Startup.cs index c3d500f00a..d9f7edd3c0 100644 --- a/src/MusicStore/Startup.cs +++ b/src/MusicStore/Startup.cs @@ -3,10 +3,10 @@ using Microsoft.AspNet.Builder; using Microsoft.AspNet.Diagnostics; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Routing; +using Microsoft.Framework.Cache.Memory; using Microsoft.Framework.ConfigurationModel; using Microsoft.Framework.DependencyInjection; using MusicStore.Models; -using Microsoft.Framework.Cache.Memory; namespace MusicStore { diff --git a/test/E2ETests/Extensions.cs b/test/E2ETests/Extensions.cs index 4338731d9d..8bac4b6585 100644 --- a/test/E2ETests/Extensions.cs +++ b/test/E2ETests/Extensions.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace System.Net +namespace System.Net { public static class Extensions { diff --git a/test/E2ETests/Helpers.cs b/test/E2ETests/Helpers.cs index ff9ff5d0c9..20e92278b7 100644 --- a/test/E2ETests/Helpers.cs +++ b/test/E2ETests/Helpers.cs @@ -43,7 +43,6 @@ namespace E2ETests return true; } - //if (serverType == ServerType.IISNativeModule && // Environment.GetEnvironmentVariable("IIS_NATIVE_MODULE_SETUP") != "true") //{