代写 html database Lab 08a- Creating a Shopping Basket

Lab 08a- Creating a Shopping Basket
Contents
Adding a Basket Line Entity 1

These instructions cover
• How to allow users to add products to their shopping basket.
• We are going to treat a shopping basket as a list of “basket lines” with a basket ID, a product, a quantity, and the time when the entry was added to the basket.
The features of the basket will be as follows:
• The basket will allow logged in and anonymous users to add items to their basket and store these baskets in the database.
• For demonstration purposes, we are going to use the session to store a key that will represent the current user. The default session timeout is set to 20 minutes for ASP.NET web applications.
• If the user is anonymous, we will generate a GUID to represent the user and if the user is logged in, we will use the username to store basket entries.
• The site will convert the GUID into a userID if a user logs in or registers after adding items to the basket.
• If a logged in user logs out, the site will no longer display the items in the basket since anyone could then see them or edit the basket.
• If a user has previously added items to their basket, the site will display them when they log in.
• The site will not empty a logged in user’s basket unless they choose to do so themselves.
You can read the book chapter 8 for more clarity.
Adding a Basket Line Entity

• Create a class BasketLine.
The best way to think of this class is that it represents a physical line on the screen in a basket. Add a new class named BasketLine to the models’ folder of the project as follows:

public class BasketLine
{
public int ID { get; set; }
public string BasketID { get; set; }
public int ProductID { get; set; }
[Range(0, 50, ErrorMessage = “Please enter a quantity between 0 and 50”)]
public int Quantity { get; set; }
public DateTime DateCreated { get; set; }
public virtual Product Product { get; set; }
}
Add the required directives.
• Modify the DAL\StoreContext.cs file to add the BasketLines Property.
public class StoreContext:DbContext
{
public DbSet Products { get; set; }
public DbSet Categories { get; set; }
public DbSet ProductImages { get; set; }
public DbSet ProductImageMappings { get; set; }
public DbSet BasketLines { get; set; }
}
• Now create a Code First Migration in order to generate the BasketLines Table.
• Run the command
add-migration AddBasketLine -Configuration StoreConfiguration
It should generate a new migration file containing statements to create and drop a BasketLines database table.
• Now create the BasketLine tables table by running the following command:
update-database -Configuration StoreConfiguration.
• Check your Server Explorer- your database. It should have the newly created table.
• Now that we have a BasketLine class, we are going to create a Basket class which will add main logic behind this class:
• Get a basket
• Set a session key for a basket
• Add a quantity of a product to a basket
• Update the quantity of one or more products in a basket
• Empty a basket
• Calculate the total cost of a basket
• Get the items in a basket
• Get the overall number of products in a basket
• Migrate the session key for a basket to a username when a user logs in
• In your Models folder, add a new class, Basket and add the following code to the class:
Now this code implements all the logic that is mentioned above. The normal CRUD(Create, Read, Update, Delete) Scaffolding cannot write such logic, therefore, programmers have to think and write it according to their project needs.
using OnlineStore.DAL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace OnlineStore.Models
{
public class Basket
{
private string BasketID { get; set; }
private const string BasketSessionKey = “BasketID”;
private StoreContext db = new StoreContext();
private string GetBasketID()
{
if (HttpContext.Current.Session[BasketSessionKey] == null)
{
if (!string.IsNullOrWhiteSpace(HttpContext.Current.User.Identity.Name))
{
HttpContext.Current.Session[BasketSessionKey] =
HttpContext.Current.User.Identity.Name;
}
else
{
Guid tempBasketID = Guid.NewGuid();
HttpContext.Current.Session[BasketSessionKey] = tempBasketID.ToString();
}
}
return HttpContext.Current.Session[BasketSessionKey].ToString();
}

public static Basket GetBasket()
{
Basket basket = new Basket();
basket.BasketID = basket.GetBasketID();
return basket;
}

public void AddToBasket(int productID, int quantity)
{
var basketLine = db.BasketLines.FirstOrDefault(b => b.BasketID == BasketID && b.ProductID == productID);
if(basketLine == null)
{
basketLine = new BasketLine
{
ProductID = productID,
BasketID = BasketID,
Quantity = quantity,
DateCreated = DateTime.Now
};
db.BasketLines.Add(basketLine);
}
else
{
basketLine.Quantity += quantity;
}
db.SaveChanges();
}

public void RemoveLine(int productID)
{
var basketLine = db.BasketLines.FirstOrDefault(b => b.BasketID == BasketID && b.ProductID == productID);
if(basketLine != null)
{
db.BasketLines.Remove(basketLine);
}
db.SaveChanges();
}

public void UpdateBasket(List lines)
{
foreach (var line in lines)
{
var basketLine = db.BasketLines.FirstOrDefault(b => b.BasketID == BasketID && b.ProductID == line.ProductID);
if (basketLine != null)
{
if (line.Quantity == 0)
{
RemoveLine(line.ProductID);
}
else
{
basketLine.Quantity = line.Quantity;
}
}
}
db.SaveChanges();
}

public void EmptyBasket()
{
var basketLines = db.BasketLines.Where(b => b.BasketID == BasketID);
foreach(var basketLine in basketLines)
{
db.BasketLines.Remove(basketLine);
}
db.SaveChanges();
}

public List GetBasketLines()
{
return db.BasketLines.Where(b => b.BasketID == BasketID).ToList();
}

public decimal GetTotalCost()
{
decimal basketTotal = decimal.Zero;
if(GetBasketLines().Count >0)
{
basketTotal = db.BasketLines.Where(b => b.BasketID == BasketID).Sum(b => b.Product.Price * b.Quantity);
}
return basketTotal;
}
public int GetNumberOfItems()
{
int numberOfItems = 0;
if(GetBasketLines().Count > 0)
{
numberOfItems = db.BasketLines.Where(b => b.BasketID == BasketID).Sum(b => b.Quantity);
}
return numberOfItems;
}

public void MigrateBasket(string userName)
{
//find the current basket and store it in memory using ToList()
var basket = db.BasketLines.Where(b => b.BasketID == BasketID).ToList();
//find if the user already has a basket or not and store it in memory using ToList()
var usersBasket = db.BasketLines.Where(b => b.BasketID == userName).ToList();
//if the user has a basket then add the current item to it

if(usersBasket != null)
{
//set the basketID to the userName
string prevID = BasketID;
BasketID = userName;
//add the lines in anonymous basket to the user’s basket
foreach(var line in basket)
{
AddToBasket(line.ProductID, line.ProductID);
}
//delete the lines in the anonymous basket from the database
BasketID = prevID;
EmptyBasket();
}
else
{
foreach(var basketLine in basket)
{
basketLine.BasketID = userName;
}
db.SaveChanges();
}
HttpContext.Current.Session[BasketSessionKey] = userName;
}

}
}
• Next, we are going to Add a Basket View Model. In your ViewModels folder, create a new class BasketViewModel.cs. The code that you can add is follows and add directives as needed.
public class BasketViewModel
{
public List BasketLines { get; set; }
[Display(Name = “Basket Total”)]
[DisplayFormat(DataFormatString = “{0:c}”)]
public decimal TotalCost { get; set; }
}

• Now, add a controller. Right click the controller folder and add an MVC5 Controller-Empty. Name this new controller BasketController. Update the new controller as follows to update the Index method.

namespace OnlineStore.Controllers
{
public class BasketController : Controller
{
// GET: Basket
public ActionResult Index()
{
Basket basket = Basket.GetBasket();
BasketViewModel viewModel = new BasketViewModel
{
BasketLines = basket.GetBasketLines(),
TotalCost = basket.GetTotalCost()
};
return View(viewModel);
}
• Next add the AddToBasket, UpdateBasket, and RemoveLine methods in the same file within BasketController square brackets, as follows

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddToBasket(int id, int quantity)
{
Basket basket = Basket.GetBasket();
basket.AddToBasket(id, quantity);
return RedirectToAction(“Index”);

}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UpdateBasket(BasketViewModel viewModel)
{
Basket basket = Basket.GetBasket();
basket.UpdateBasket(viewModel.BasketLines);
return RedirectToAction(“Index”);
}
[HttpGet]
public ActionResult RemoveLine(int id)
{
Basket basket = Basket.GetBasket();
basket.RemoveLine(id);
return RedirectToAction(“Index”);
}
• Now add a Basket Index View by right-clicking Index() method, and update as shown below:

• Update the newly created View\Basket\Index.cshtml as follows for allowing a user to add to basket, this code also checks whether the basket is empty.
@model OnlineStore.ViewModels.BasketViewModel

@{
ViewBag.Title = “Your Basket”;
}

@ViewBag.Title

@if (Model.BasketLines.Count() > 0)
{

@using (Html.BeginForm(“UpdateBasket”, “Basket”))
{
@Html.AntiForgeryToken();



for (int i = 0; i < Model.BasketLines.Count; i++) {

@Html.ActionLink(“Remove”, “RemoveLine”, “Basket”, new { id = Model.BasketLines[i].Product.ID }, null)

}
}

@Html.DisplayNameFor(model => model.TotalCost)
@Html.DisplayFor(model => model.TotalCost)

}
else
{

Your Basket is empty

}
@section Scripts{

@Html.ActionLink(“Continue Shopping”, “Index”, “Products”)

@Scripts.Render(“~/bundles/jqueryval”)
}

@*@Html.ActionLink(“Edit”, “Edit”, new { /* id = Model.PrimaryKey */ }) |
@Html.ActionLink(“Back to List”, “Index”)*@

• Before running this code, you need to update the Views\Products\Details.cshtml as well. The code below in bold has to be added:
@model OnlineStore.Models.Product

@{
ViewBag.Title = “Details”;
}

Details

@if (Request.IsAuthenticated && User.IsInRole(“Admin”))
{
@Html.ActionLink(“Edit”, “Edit”, new { id = Model.ID })
@Html.Raw(” | “)
}
@Html.ActionLink(“Back to List”, “Index”)

• If you run the code now, you should be able to see in Product Details a dropdown list as shown below:

• Next, we will display Basket Summary: Create a new class BasketSummaryViewModel in the ViewsModel folder to include a property to hold the number of items in the basket and the total cost of the basket as follows:
public class BasketSummaryViewModel
{
public int NumberOfItems { get; set; }
[DataType(DataType.Currency)]
[DisplayFormat(DataFormatString = “{0:c}”)]
public decimal TotalCost {get; set;}
}
• Add, the following code in the BasketController.cs. It will return a PartialViewResult and set the values in instance of the BasketSummaryViewModel.
public PartialViewResult Summary()
{
Basket basket = Basket.GetBasket();
BasketSummaryViewModel viewModel = new BasketSummaryViewModel
{
NumberOfItems = basket.GetNumberOfItems(),
TotalCost = basket.GetTotalCost()
};
return PartialView(viewModel);
}
• Right-click Summary() method and Add View. Create a new empty view named Summary with the model class BasketSummaryViewModel- as depicted in the figure below:

• Open the newly created Views\Basket\Summary.cshtml and edit it as follows:
@model OnlineStore.ViewModels.BasketSummaryViewModel

• Update the Shared\_Layout.cshtml as follows so that you are able to view the Summary in the Navigation bar as follows:

Code not included to save space….

@using (Html.BeginRouteForm(“ProductsIndex”, FormMethod.Get, new { @class = “navbar-form navbar-left” }))
{

@Html.TextBox(“Search”, null, new {@class = “form-control”, @placeholder = “Search Products”})


}
@Html.Partial(“_LoginPartial”)
@Html.Action(“Summary”, “Basket”);

Code not included to save space….

The view should be somewhat like the figure below:

• To Migrate a basket when a user logs in or register, make updating to AccountController.cs as following:
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}

//check if a user was previously logged in wihtout logging out.
bool userWasLoggedIn= false;
if (!string.IsNullOrWhiteSpace(User.Identity.Name))
{
userWasLoggedIn = true;
}

// This doesn’t count login failures towards account lockout
// To enable password failures to trigger account lockout, change to //shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
//this is needed to ensure that the previous user’s basket is not carried over
if(userWasLoggedIn)
{
Session.Abandon();
}
Basket basket = Basket.GetBasket();
//if there was no previously logged in user migrate the basket from GUID to the //username
if(!userWasLoggedIn)
{
basket.MigrateBasket(model.Email);
}
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View(“Lockout”);
case SignInStatus.RequiresVerification:
return RedirectToAction(“SendCode”, new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError(“”, “Invalid login attempt.”);
ViewBag.ReturnUrl = returnUrl;
return View(model);
}
}
• Then update the LogOff method of AccountController class:
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
Session.Abandon();
return RedirectToAction(“Index”, “Home”);
}
• For migrating the basket upon registeration, update the register method of AccountController.cs. as follow:
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task Register(RegisterViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser
{
UserName = model.Email,
Email = model.Email,
DateOfBirth = model.DateOfBirth,
FirstName = model.FirstName,
LastName = model.LastName,
Address = model.Address
};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await UserManager.AddToRoleAsync(user.Id, “Users”);
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
Basket basket = Basket.GetBasket();
basket.MigrateBasket(model.Email);
return RedirectToLocal(returnUrl);
//return RedirectToAction(“Index”, “Home”);
}
ViewBag.ReturnUrl = returnUrl;
AddErrors(result);
}

These are all the instructions for creating a Shopping Basket. Continue to the next part. Save the version and then update the code.