Everything but the kitchen async... (Updates to how Music Store controllers use data)

Specifically:
- Dispose contexts
- Use async wherever possible
- Stop using initializers (currently hard-coded to drop and recreate each run)
- Some general cleanup
- Stop using AttachDbFilename

Not included here:
- No major changes to app structure
- No major changes to data model
- No major changes to error handling, concurrency, etc.
This commit is contained in:
ajcvickers 2014-01-28 16:07:26 -08:00
parent 53268f3c9e
commit 6a9b6c16c2
15 changed files with 4198 additions and 1015 deletions

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Sake" version="0.2" />
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Sake" version="0.2" />
</packages>

View File

@ -9,6 +9,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MusicStore.k10", "src\Music
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MusicStore.net45", "src\MusicStore\MusicStore.net45.csproj", "{9C8A2D1F-D430-46DF-8F00-39E378EC8FB2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{44621553-AA7D-4893-8834-79582A7D8348}"
ProjectSection(SolutionItems) = preProject
.nuget\packages.config = .nuget\packages.config
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU

View File

@ -1,46 +1,53 @@
using Microsoft.AspNet.Identity;
using System.Configuration;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using MvcMusicStore.Models;
using Owin;
using System.Configuration;
using System.Threading.Tasks;
namespace MvcMusicStore
{
public partial class Startup
{
private const string RoleName = "Administrator";
public void ConfigureApp(IAppBuilder app)
{
System.Data.Entity.Database.SetInitializer(new MvcMusicStore.Models.SampleData());
using (var context = new MusicStoreEntities())
{
context.Database.Delete();
context.Database.Create();
new SampleData().Seed(context);
}
CreateAdminUser();
}
private async void CreateAdminUser()
{
string _username = ConfigurationManager.AppSettings["DefaultAdminUsername"];
string _password = ConfigurationManager.AppSettings["DefaultAdminPassword"];
string _role = "Administrator";
var username = ConfigurationManager.AppSettings["DefaultAdminUsername"];
var password = ConfigurationManager.AppSettings["DefaultAdminPassword"];
var context = new ApplicationDbContext();
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
var role = new IdentityRole(_role);
var result = await roleManager.RoleExistsAsync(_role);
if (result == false)
using (var context = new ApplicationDbContext())
{
await roleManager.CreateAsync(role);
}
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
var user = await userManager.FindByNameAsync(_username);
if (user == null)
{
user = new ApplicationUser { UserName = _username };
await userManager.CreateAsync(user, _password);
await userManager.AddToRoleAsync(user.Id, _role);
var role = new IdentityRole(RoleName);
var result = await roleManager.RoleExistsAsync(RoleName);
if (!result)
{
await roleManager.CreateAsync(role);
}
var user = await userManager.FindByNameAsync(username);
if (user == null)
{
user = new ApplicationUser { UserName = username };
await userManager.CreateAsync(user, password);
await userManager.AddToRoleAsync(user.Id, RoleName);
}
}
}
}

View File

@ -1,12 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using MvcMusicStore.Models;
@ -15,6 +14,18 @@ namespace MvcMusicStore.Controllers
[Authorize]
public class AccountController : Controller
{
public enum ManageMessageId
{
ChangePasswordSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
Error
}
private const string XsrfKey = "XsrfId";
private UserManager<ApplicationUser> _userManager;
public AccountController()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
@ -22,33 +33,35 @@ namespace MvcMusicStore.Controllers
public AccountController(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
_userManager = userManager;
}
public UserManager<ApplicationUser> UserManager { get; private set; }
private void MigrateShoppingCart(string UserName)
private IAuthenticationManager AuthenticationManager
{
var storeDb = new MusicStoreEntities();
// Associate shopping cart items with logged-in user
var cart = ShoppingCart.GetCart(storeDb, this.HttpContext);
cart.MigrateCart(UserName);
storeDb.SaveChanges();
Session[ShoppingCart.CartSessionKey] = UserName;
get { return HttpContext.GetOwinContext().Authentication; }
}
private async Task MigrateShoppingCart(string userName)
{
using (var storeContext = new MusicStoreEntities())
{
var cart = ShoppingCart.GetCart(storeContext, this);
await cart.MigrateCart(userName);
Session[ShoppingCart.CartSessionKey] = userName;
}
}
//
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
@ -57,23 +70,20 @@ namespace MvcMusicStore.Controllers
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
var user = await _userManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
ModelState.AddModelError("", "Invalid username or password.");
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
@ -81,7 +91,6 @@ namespace MvcMusicStore.Controllers
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
@ -90,85 +99,84 @@ namespace MvcMusicStore.Controllers
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.UserName };
var result = await UserManager.CreateAsync(user, model.Password);
var user = new ApplicationUser { UserName = model.UserName };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
await SignInAsync(user, false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// POST: /Account/Disassociate
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Disassociate(string loginProvider, string providerKey)
{
ManageMessageId? message = null;
IdentityResult result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey));
if (result.Succeeded)
{
message = ManageMessageId.RemoveLoginSuccess;
}
else
{
message = ManageMessageId.Error;
}
return RedirectToAction("Manage", new { Message = message });
var result = await _userManager.RemoveLoginAsync(
User.Identity.GetUserId(),
new UserLoginInfo(loginProvider, providerKey));
return RedirectToAction(
"Manage",
new { Message = result.Succeeded ? ManageMessageId.RemoveLoginSuccess : ManageMessageId.Error });
}
//
// GET: /Account/Manage
public ActionResult Manage(ManageMessageId? message)
public async Task<ActionResult> Manage(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: message == ManageMessageId.Error ? "An error has occurred."
: "";
ViewBag.HasLocalPassword = HasPassword();
message == ManageMessageId.ChangePasswordSuccess
? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess
? "Your password has been set."
: message == ManageMessageId.RemoveLoginSuccess
? "The external login was removed."
: message == ManageMessageId.Error
? "An error has occurred."
: "";
ViewBag.HasLocalPassword = await HasPasswordAsync();
ViewBag.ReturnUrl = Url.Action("Manage");
return View();
}
//
// POST: /Account/Manage
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Manage(ManageUserViewModel model)
{
bool hasPassword = HasPassword();
bool hasPassword = await HasPasswordAsync();
ViewBag.HasLocalPassword = hasPassword;
ViewBag.ReturnUrl = Url.Action("Manage");
if (hasPassword)
{
if (ModelState.IsValid)
{
IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
var result = await _userManager.ChangePasswordAsync(
User.Identity.GetUserId(),
model.OldPassword,
model.NewPassword);
if (result.Succeeded)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
}
else
{
AddErrors(result);
}
AddErrors(result);
}
}
else
{
// User does not have a password so remove any validation errors caused by a missing OldPassword field
ModelState state = ModelState["OldPassword"];
var state = ModelState["OldPassword"];
if (state != null)
{
state.Errors.Clear();
@ -176,34 +184,32 @@ namespace MvcMusicStore.Controllers
if (ModelState.IsValid)
{
IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
var result = await _userManager.AddPasswordAsync(
User.Identity.GetUserId(),
model.NewPassword);
if (result.Succeeded)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
}
else
{
AddErrors(result);
}
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
return new ChallengeResult(provider,
Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
//
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
@ -214,55 +220,56 @@ namespace MvcMusicStore.Controllers
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var user = await UserManager.FindAsync(loginInfo.Login);
var user = await _userManager.FindAsync(loginInfo.Login);
if (user != null)
{
await SignInAsync(user, isPersistent: false);
await SignInAsync(user, false);
return RedirectToLocal(returnUrl);
}
else
{
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { UserName = loginInfo.DefaultUserName });
}
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation",
new ExternalLoginConfirmationViewModel { UserName = loginInfo.DefaultUserName });
}
//
// POST: /Account/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId());
}
//
// GET: /Account/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
var loginInfo =
await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}
var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
var result = await _userManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
if (result.Succeeded)
{
return RedirectToAction("Manage");
}
return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}
//
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
public async Task<ActionResult> ExternalLoginConfirmation(
ExternalLoginConfirmationViewModel model,
string returnUrl)
{
if (User.Identity.IsAuthenticated)
{
@ -277,35 +284,39 @@ namespace MvcMusicStore.Controllers
{
return View("ExternalLoginFailure");
}
var user = new ApplicationUser() { UserName = model.UserName };
var result = await UserManager.CreateAsync(user);
var user = new ApplicationUser { UserName = model.UserName };
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await UserManager.AddLoginAsync(user.Id, info.Login);
result = await _userManager.AddLoginAsync(user.Id, info.Login);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
await SignInAsync(user, false);
return RedirectToLocal(returnUrl);
}
}
AddErrors(result);
}
ViewBag.ReturnUrl = returnUrl;
return View(model);
}
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
return RedirectToAction("Index", "Home");
}
//
// GET: /Account/ExternalLoginFailure
[AllowAnonymous]
public ActionResult ExternalLoginFailure()
@ -316,41 +327,34 @@ namespace MvcMusicStore.Controllers
[ChildActionOnly]
public ActionResult RemoveAccountList()
{
var linkedAccounts = UserManager.GetLogins(User.Identity.GetUserId());
var linkedAccounts = _userManager.GetLogins(User.Identity.GetUserId());
ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1;
return (ActionResult)PartialView("_RemoveAccountPartial", linkedAccounts);
return PartialView("_RemoveAccountPartial", linkedAccounts);
}
protected override void Dispose(bool disposing)
{
if (disposing && UserManager != null)
if (disposing && _userManager != null)
{
UserManager.Dispose();
UserManager = null;
_userManager.Dispose();
_userManager = null;
}
base.Dispose(disposing);
}
#region Helpers
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
// Migrate the user's shopping cart
MigrateShoppingCart(user.UserName);
var identity =
await _userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = isPersistent }, identity);
await MigrateShoppingCart(user.UserName);
}
private void AddErrors(IdentityResult result)
@ -363,61 +367,47 @@ namespace MvcMusicStore.Controllers
private bool HasPassword()
{
var user = UserManager.FindById(User.Identity.GetUserId());
if (user != null)
{
return user.PasswordHash != null;
}
return false;
var user = _userManager.FindById(User.Identity.GetUserId());
return user != null && user.PasswordHash != null;
}
public enum ManageMessageId
private async Task<bool> HasPasswordAsync()
{
ChangePasswordSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
Error
var user = await _userManager.FindByIdAsync(User.Identity.GetUserId());
return user != null && user.PasswordHash != null;
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
return Url.IsLocalUrl(returnUrl)
? (ActionResult)Redirect(returnUrl)
: RedirectToAction("Index", "Home");
}
private class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null)
{
}
private readonly string _loginProvider;
private readonly string _redirectUri;
private readonly string _userId;
public ChallengeResult(string provider, string redirectUri, string userId)
public ChallengeResult(string provider, string redirectUri, string userId = null)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
_loginProvider = provider;
_redirectUri = redirectUri;
_userId = userId;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
if (UserId != null)
var properties = new AuthenticationProperties { RedirectUri = _redirectUri };
if (_userId != null)
{
properties.Dictionary[XsrfKey] = UserId;
properties.Dictionary[XsrfKey] = _userId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, _loginProvider);
}
}
#endregion
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Data.Entity;
using System.Threading.Tasks;
using System.Web.Mvc;
using MvcMusicStore.Models;
@ -8,78 +9,56 @@ namespace MvcMusicStore.Controllers
[Authorize]
public class CheckoutController : Controller
{
MusicStoreEntities storeDB = new MusicStoreEntities();
const string PromoCode = "FREE";
private const string PromoCode = "FREE";
private readonly MusicStoreEntities _storeContext = new MusicStoreEntities();
//
// GET: /Checkout/
public ActionResult AddressAndPayment()
{
return View();
}
//
// POST: /Checkout/AddressAndPayment
[HttpPost]
public ActionResult AddressAndPayment(FormCollection values)
public async Task<ActionResult> AddressAndPayment(FormCollection values)
{
var order = new Order();
TryUpdateModel(order);
try
if (ModelState.IsValid
&& string.Equals(values["PromoCode"], PromoCode, StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(values["PromoCode"], PromoCode,
StringComparison.OrdinalIgnoreCase) == false)
{
return View(order);
}
else
{
order.Username = User.Identity.Name;
order.OrderDate = DateTime.Now;
order.Username = User.Identity.Name;
order.OrderDate = DateTime.Now;
//Add the Order
storeDB.Orders.Add(order);
_storeContext.Orders.Add(order);
//Process the order
var cart = ShoppingCart.GetCart(storeDB, this.HttpContext);
cart.CreateOrder(order);
await ShoppingCart.GetCart(_storeContext, this).CreateOrder(order);
// Save all changes
storeDB.SaveChanges();
return RedirectToAction("Complete",
new { id = order.OrderId });
}
await _storeContext.SaveChangesAsync();
return RedirectToAction("Complete", new { id = order.OrderId });
}
catch
{
//Invalid - redisplay with errors
return View(order);
}
return View(order);
}
//
// GET: /Checkout/Complete
public ActionResult Complete(int id)
public async Task<ActionResult> Complete(int id)
{
// Validate customer owns this order
bool isValid = storeDB.Orders.Any(
o => o.OrderId == id &&
o.Username == User.Identity.Name);
return await _storeContext.Orders.AnyAsync(o => o.OrderId == id && o.Username == User.Identity.Name)
? View(id)
: View("Error");
}
if (isValid)
protected override void Dispose(bool disposing)
{
if (disposing)
{
return View(id);
}
else
{
return View("Error");
_storeContext.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Threading.Tasks;
using System.Web.Mvc;
using MvcMusicStore.Models;
@ -8,28 +8,24 @@ namespace MvcMusicStore.Controllers
{
public class HomeController : Controller
{
private MusicStoreEntities storeDB = new MusicStoreEntities();
//
private readonly MusicStoreEntities _storeContext = new MusicStoreEntities();
// GET: /Home/
public ActionResult Index()
public async Task<ActionResult> Index()
{
// Get most popular albums
var albums = GetTopSellingAlbums(6);
return View(albums);
return View(await _storeContext.Albums
.OrderByDescending(a => a.OrderDetails.Count())
.Take(6)
.ToListAsync());
}
private List<Album> GetTopSellingAlbums(int count)
protected override void Dispose(bool disposing)
{
// Group the order details by album and return
// the albums with the highest count
return storeDB.Albums
.OrderByDescending(a => a.OrderDetails.Count())
.Take(count)
.ToList();
if (disposing)
{
_storeContext.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@ -1,4 +1,6 @@
using System.Linq;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Mvc;
using MvcMusicStore.Models;
using MvcMusicStore.ViewModels;
@ -7,75 +9,56 @@ namespace MvcMusicStore.Controllers
{
public class ShoppingCartController : Controller
{
MusicStoreEntities storeDB = new MusicStoreEntities();
private readonly MusicStoreEntities _storeContext = new MusicStoreEntities();
//
// GET: /ShoppingCart/
public ActionResult Index()
public async Task<ActionResult> Index()
{
var cart = ShoppingCart.GetCart(storeDB, this.HttpContext);
var cart = ShoppingCart.GetCart(_storeContext, this);
// Set up our ViewModel
var viewModel = new ShoppingCartViewModel
{
CartItems = cart.GetCartItems(),
CartTotal = cart.GetTotal()
CartItems = await cart.GetCartItems().ToListAsync(),
CartTotal = await cart.GetTotal()
};
// Return the view
return View(viewModel);
}
//
// GET: /ShoppingCart/AddToCart/5
public ActionResult AddToCart(int id)
public async Task<ActionResult> AddToCart(int id)
{
var cart = ShoppingCart.GetCart(_storeContext, this);
// Retrieve the album from the database
var addedAlbum = storeDB.Albums
.Single(album => album.AlbumId == id);
await cart.AddToCart(await _storeContext.Albums.SingleAsync(a => a.AlbumId == id));
// Add it to the shopping cart
var cart = ShoppingCart.GetCart(storeDB, this.HttpContext);
await _storeContext.SaveChangesAsync();
cart.AddToCart(addedAlbum);
storeDB.SaveChanges();
// Go back to the main store page for more shopping
return RedirectToAction("Index");
}
//
// AJAX: /ShoppingCart/RemoveFromCart/5
[HttpPost]
public ActionResult RemoveFromCart(int id)
public async Task<ActionResult> RemoveFromCart(int id)
{
// Retrieve the current user's shopping cart
var cart = ShoppingCart.GetCart(storeDB, this.HttpContext);
var cart = ShoppingCart.GetCart(_storeContext, this);
// Get the name of the album to display confirmation
string albumName = storeDB.Carts
.Single(item => item.RecordId == id).Album.Title;
var albumName = await _storeContext.Carts
.Where(i => i.RecordId == id)
.Select(i => i.Album.Title)
.SingleOrDefaultAsync();
// Remove from cart
int itemCount = cart.RemoveFromCart(id);
var itemCount = await cart.RemoveFromCart(id);
storeDB.SaveChanges();
await _storeContext.SaveChangesAsync();
string removed = (itemCount > 0) ? " 1 copy of " : string.Empty;
// Display the confirmation message
var removed = (itemCount > 0) ? " 1 copy of " : string.Empty;
var results = new ShoppingCartRemoveViewModel
{
Message = removed + albumName +
" has been removed from your shopping cart.",
CartTotal = cart.GetTotal(),
CartCount = cart.GetCount(),
Message = removed + albumName + " has been removed from your shopping cart.",
CartTotal = await cart.GetTotal(),
CartCount = await cart.GetCount(),
ItemCount = itemCount,
DeleteId = id
};
@ -86,16 +69,26 @@ namespace MvcMusicStore.Controllers
[ChildActionOnly]
public ActionResult CartSummary()
{
var cart = ShoppingCart.GetCart(storeDB, this.HttpContext);
var cart = ShoppingCart.GetCart(_storeContext, this);
var cartItems = cart.GetCartItems()
.Select(a => a.Album.Title)
.OrderBy(x => x);
.OrderBy(x => x)
.ToList();
ViewBag.CartCount = cartItems.Count();
ViewBag.CartSummary = string.Join("\n", cartItems.Distinct());
return PartialView("CartSummary");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_storeContext.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@ -1,57 +1,49 @@
using MvcMusicStore.Models;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Threading.Tasks;
using System.Web.Mvc;
using MvcMusicStore.Models;
namespace MvcMusicStore.Controllers
{
public class StoreController : Controller
{
MusicStoreEntities storeDB = new MusicStoreEntities();
//
private readonly MusicStoreEntities _storeContext = new MusicStoreEntities();
// GET: /Store/
public ActionResult Index()
public async Task<ActionResult> Index()
{
var genres = storeDB.Genres.ToList();
return View(genres);
return View(await _storeContext.Genres.ToListAsync());
}
//
// GET: /Store/Browse?genre=Disco
public ActionResult Browse(string genre)
public async Task<ActionResult> Browse(string genre)
{
// Retrieve Genre genre and its Associated associated Albums albums from database
var genreModel = storeDB.Genres.Include("Albums")
.Single(g => g.Name == genre);
return View(genreModel);
return View(await _storeContext.Genres.Include("Albums").SingleAsync(g => g.Name == genre));
}
public ActionResult Details(int id)
public async Task<ActionResult> Details(int id)
{
var album = storeDB.Albums.Find(id);
var album = await _storeContext.Albums.FindAsync(id);
return View(album);
return album != null ? View(album) : (ActionResult)HttpNotFound();
}
[ChildActionOnly]
public ActionResult GenreMenu()
{
var genres = storeDB.Genres
.OrderByDescending(
g => g.Albums.Sum(
a => a.OrderDetails.Sum(
od => od.Quantity)))
.Take(9)
.ToList();
return PartialView(
_storeContext.Genres.OrderByDescending(
g => g.Albums.Sum(a => a.OrderDetails.Sum(od => od.Quantity))).Take(9).ToList());
}
return PartialView(genres);
protected override void Dispose(bool disposing)
{
if (disposing)
{
_storeContext.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@ -1,9 +1,6 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Threading.Tasks;
using System.Web.Mvc;
using MvcMusicStore.Models;
@ -12,119 +9,132 @@ namespace MvcMusicStore.Controllers
[Authorize(Roles = "Administrator")]
public class StoreManagerController : Controller
{
private MusicStoreEntities db = new MusicStoreEntities();
private readonly MusicStoreEntities _storeContext = new MusicStoreEntities();
//
// GET: /StoreManager/
public ActionResult Index()
public async Task<ActionResult> Index()
{
var albums = db.Albums.Include(a => a.Genre).Include(a => a.Artist)
.OrderBy(a => a.Price);
return View(albums.ToList());
return View(await _storeContext.Albums
.Include(a => a.Genre)
.Include(a => a.Artist)
.OrderBy(a => a.Price).ToListAsync());
}
//
// GET: /StoreManager/Details/5
public ActionResult Details(int id = 0)
public async Task<ActionResult> Details(int id = 0)
{
Album album = db.Albums.Find(id);
var album = await _storeContext.Albums.FindAsync(id);
if (album == null)
{
return HttpNotFound();
}
return View(album);
}
//
// GET: /StoreManager/Create
public ActionResult Create()
public async Task<ActionResult> Create()
{
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name");
return View();
return await BuildView(null);
}
//
// POST: /StoreManager/Create
[HttpPost]
public ActionResult Create(Album album)
public async Task<ActionResult> Create(Album album)
{
if (ModelState.IsValid)
{
db.Albums.Add(album);
db.SaveChanges();
_storeContext.Albums.Add(album);
await _storeContext.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
return await BuildView(album);
}
//
// GET: /StoreManager/Edit/5
public ActionResult Edit(int id = 0)
public async Task<ActionResult> Edit(int id = 0)
{
Album album = db.Albums.Find(id);
var album = await _storeContext.Albums.FindAsync(id);
if (album == null)
{
return HttpNotFound();
}
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
return await BuildView(album);
}
//
// POST: /StoreManager/Edit/5
[HttpPost]
public ActionResult Edit(Album album)
public async Task<ActionResult> Edit(Album album)
{
if (ModelState.IsValid)
{
db.Entry(album).State = EntityState.Modified;
db.SaveChanges();
_storeContext.Entry(album).State = EntityState.Modified;
await _storeContext.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
return await BuildView(album);
}
//
// GET: /StoreManager/Delete/5
public ActionResult Delete(int id = 0)
public async Task<ActionResult> Delete(int id = 0)
{
Album album = db.Albums.Find(id);
var album = await _storeContext.Albums.FindAsync(id);
if (album == null)
{
return HttpNotFound();
}
return View(album);
}
//
// POST: /StoreManager/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
public async Task<ActionResult> DeleteConfirmed(int id)
{
Album album = db.Albums.Find(id);
db.Albums.Remove(album);
db.SaveChanges();
var album = await _storeContext.Albums.FindAsync(id);
if (album == null)
{
return HttpNotFound();
}
_storeContext.Albums.Remove(album);
await _storeContext.SaveChangesAsync();
return RedirectToAction("Index");
}
private async Task<ActionResult> BuildView(Album album)
{
ViewBag.GenreId = new SelectList(
await _storeContext.Genres.ToListAsync(),
"GenreId",
"Name",
album == null ? null : (object)album.GenreId);
ViewBag.ArtistId = new SelectList(
await _storeContext.Artists.ToListAsync(),
"ArtistId",
"Name",
album == null ? null : (object)album.ArtistId);
return View(album);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
if (disposing)
{
_storeContext.Dispose();
}
base.Dispose(disposing);
}
}

View File

@ -16,8 +16,6 @@ namespace MvcMusicStore
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
System.Data.Entity.Database.SetInitializer(new MvcMusicStore.Models.SampleData());
}
}
}

View File

@ -4,6 +4,11 @@ namespace MvcMusicStore.Models
{
public class MusicStoreEntities : DbContext
{
public MusicStoreEntities()
: base("name=MusicStoreEntities")
{
}
public DbSet<Album> Albums { get; set; }
public DbSet<Genre> Genres { get; set; }
public DbSet<Artist> Artists { get; set; }

View File

@ -8,6 +8,11 @@ namespace MvcMusicStore.Models
[Bind(Include = "FirstName,LastName,Address,City,State,PostalCode,Country,Phone,Email")]
public class Order
{
public Order()
{
OrderDetails = new List<OrderDetail>();
}
[ScaffoldColumn(false)]
public int OrderId { get; set; }

File diff suppressed because it is too large Load Diff

View File

@ -1,195 +1,159 @@
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
namespace MvcMusicStore.Models
{
public partial class ShoppingCart
public class ShoppingCart
{
MusicStoreEntities _db;
string ShoppingCartId { get; set; }
public ShoppingCart(MusicStoreEntities db)
{
_db = db;
}
public const string CartSessionKey = "CartId";
public static ShoppingCart GetCart(MusicStoreEntities db, HttpContextBase context)
private readonly MusicStoreEntities _storeContext;
private readonly string _cartId;
private ShoppingCart(MusicStoreEntities storeContext, string cartId)
{
var cart = new ShoppingCart(db);
cart.ShoppingCartId = cart.GetCartId(context);
return cart;
_storeContext = storeContext;
_cartId = cartId;
}
// Helper method to simplify shopping cart calls
public static ShoppingCart GetCart(MusicStoreEntities db, Controller controller)
public static ShoppingCart GetCart(MusicStoreEntities storeContext, Controller controller)
{
return GetCart(db, controller.HttpContext);
return new ShoppingCart(storeContext, GetCartId(controller.HttpContext));
}
public void AddToCart(Album album)
{
// Get the matching cart and album instances
var cartItem = _db.Carts.SingleOrDefault(
c => c.CartId == ShoppingCartId
&& c.AlbumId == album.AlbumId);
if (cartItem == null)
{
// Create a new cart item if no cart item exists
cartItem = new Cart
{
AlbumId = album.AlbumId,
CartId = ShoppingCartId,
Count = 1,
DateCreated = DateTime.Now
};
_db.Carts.Add(cartItem);
}
else
{
// If the item does exist in the cart, then add one to the quantity
cartItem.Count++;
}
}
public int RemoveFromCart(int id)
{
// Get the cart
var cartItem = _db.Carts.Single(
cart => cart.CartId == ShoppingCartId
&& cart.RecordId == id);
int itemCount = 0;
if (cartItem != null)
{
if (cartItem.Count > 1)
{
cartItem.Count--;
itemCount = cartItem.Count;
}
else
{
_db.Carts.Remove(cartItem);
}
}
return itemCount;
}
public void EmptyCart()
{
var cartItems = _db.Carts.Where(cart => cart.CartId == ShoppingCartId);
foreach (var cartItem in cartItems)
{
_db.Carts.Remove(cartItem);
}
}
public List<Cart> GetCartItems()
{
return _db.Carts.Where(cart => cart.CartId == ShoppingCartId).ToList();
}
public int GetCount()
{
// Get the count of each item in the cart and sum them up
int? count = (from cartItems in _db.Carts
where cartItems.CartId == ShoppingCartId
select (int?)cartItems.Count).Sum();
// Return 0 if all entries are null
return count ?? 0;
}
public decimal 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
decimal? total = (from cartItems in _db.Carts
where cartItems.CartId == ShoppingCartId
select (int?)cartItems.Count * cartItems.Album.Price).Sum();
return total ?? decimal.Zero;
}
public int CreateOrder(Order order)
{
decimal orderTotal = 0;
var cartItems = 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 orderDetail = new OrderDetail
{
AlbumId = item.AlbumId,
OrderId = order.OrderId,
UnitPrice = album.Price,
Quantity = item.Count,
};
// Set the order total of the shopping cart
orderTotal += (item.Count * item.Album.Price);
_db.OrderDetails.Add(orderDetail);
}
// Set the order's total to the orderTotal count
order.Total = orderTotal;
// Empty the shopping cart
EmptyCart();
// Return the OrderId as the confirmation number
return order.OrderId;
}
// We're using HttpContextBase to allow access to cookies.
public string GetCartId(HttpContextBase context)
private static string GetCartId(HttpContextBase context)
{
if (context.Session[CartSessionKey] == null)
{
if (!string.IsNullOrWhiteSpace(context.User.Identity.Name))
{
context.Session[CartSessionKey] = context.User.Identity.Name;
}
else
{
// Generate a new random GUID using System.Guid class
Guid tempCartId = Guid.NewGuid();
var username = context.User.Identity.Name;
// Send tempCartId back to client as a cookie
context.Session[CartSessionKey] = tempCartId.ToString();
}
context.Session[CartSessionKey] = !string.IsNullOrWhiteSpace(username)
? username
: Guid.NewGuid().ToString();
}
return context.Session[CartSessionKey].ToString();
}
// When a user has logged in, migrate their shopping cart to
// be associated with their username
public void MigrateCart(string userName)
public async Task AddToCart(Album album)
{
var shoppingCart = _db.Carts.Where(c => c.CartId == ShoppingCartId);
var cartItem = await GetCartItem(album.AlbumId);
foreach (Cart item in shoppingCart)
if (cartItem == null)
{
cartItem = new Cart
{
AlbumId = album.AlbumId,
CartId = _cartId,
Count = 1,
DateCreated = DateTime.Now
};
_storeContext.Carts.Add(cartItem);
}
else
{
cartItem.Count++;
}
}
public async Task<int> RemoveFromCart(int id)
{
var cartItem = await GetCartItem(id);
if (cartItem != null)
{
if (cartItem.Count > 1)
{
return --cartItem.Count;
}
_storeContext.Carts.Remove(cartItem);
}
return 0;
}
private Task<Cart> GetCartItem(int albumId)
{
return _storeContext.Carts.SingleOrDefaultAsync(
c => c.CartId == _cartId && c.AlbumId == albumId);
}
public IQueryable<Cart> GetCartItems()
{
return _storeContext.Carts.Where(c => c.CartId == _cartId);
}
public Task<int> GetCount()
{
return _storeContext.Carts
.Where(c => c.CartId == _cartId)
.Select(c => c.Count)
.SumAsync();
}
public Task<decimal> GetTotal()
{
return _storeContext.Carts
.Where(c => c.CartId == _cartId)
.Select(c => c.Count * c.Album.Price)
.SumAsync();
}
public async Task<int> CreateOrder(Order order)
{
decimal orderTotal = 0;
var cartItems = await _storeContext.Carts
.Where(c => c.CartId == _cartId)
.Include(c => c.Album)
.ToListAsync();
foreach (var item in cartItems)
{
order.OrderDetails.Add(new OrderDetail
{
AlbumId = item.AlbumId,
OrderId = order.OrderId,
UnitPrice = item.Album.Price,
Quantity = item.Count,
});
orderTotal += item.Count * item.Album.Price;
}
order.Total = orderTotal;
await EmptyCart();
return order.OrderId;
}
private async Task EmptyCart()
{
foreach (var cartItem in await _storeContext.Carts.Where(
c => c.CartId == _cartId).ToListAsync())
{
_storeContext.Carts.Remove(cartItem);
}
}
public async Task MigrateCart(string userName)
{
var carts = await _storeContext.Carts.Where(c => c.CartId == _cartId).ToListAsync();
foreach (var item in carts)
{
item.CartId = userName;
}
await _storeContext.SaveChangesAsync();
}
}
}

View File

@ -9,8 +9,8 @@
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --></configSections>
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-MvcMusicStore-20131025034205.mdf;Initial Catalog=aspnet-MvcMusicStore-20131025034205;Integrated Security=True" providerName="System.Data.SqlClient" />
<add name="MusicStoreEntities" connectionString="Data Source=(LocalDB)\v11.0; AttachDbFilename=|DataDirectory|\MvcMusicStore.mdf; Integrated Security=True" providerName="System.Data.SqlClient" />
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MvcMusicStore-20131025034205;Integrated Security=True" providerName="System.Data.SqlClient" />
<add name="MusicStoreEntities" connectionString="Data Source=(LocalDB)\v11.0;Initial Catalog=MusicStore;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
<appSettings>
<add key="DefaultAdminUsername" value="Administrator" />