Cart in ASP.NET Core 2.1

min read

Tạo giỏ hàng(Cart) trong ASP.NET Core 2.1, thường có trong các website bán hàng, điều có một chức năng giỏ hàng, để cho người dùng có thể biết được các sản phẩm mà họ vừa chọn và tổng giá tiền họ cần phải thanh toán là bao nhiêu!
Nay trong bài viết này, ta hãy cùng đi qua chức năng đó xem và cách xử lý Cart trong ASP.NET CORE 2.1 

Bước 1: Trong Visual Studio 2019, Bạn chọn File->New, chọn Project
Bước 2: Chọn Create a new project
Bước 3: Chọn ASP.NET Core Web Application template
Bước 4: Đặt tên Project, chọn Create. 
Bước 5: Select .NET Core, ASP.NET Core 2.1, Chọn Web Application (Model-View-Controller), sau đó bấm Create

Sau khi cài đặt xong, bấm vào project->mở Nuget Packager Manager -> Install (4 plugin sau)

Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Newtonsoft.Json

Open appsettings.json -> cài đặt chuỗi ConnectionStrings đến SQL SERVER

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "EFDataContext": "Server=DESKTOP-GCABV8F\\SQLExpress;Database=DB_Cart;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

Bạn có thể đặt tên Database tùy ý, không cần giống trong SQL SERVER, bởi vì, hồi ta chạy Migration , thì nó tự tạo Database và Table cho ta trong SQL SERVER
Bạn có thể xem lại tại đây: Database Relationships(One to Many, Many to Many) in ASP.NET CORE 2.1

Giớ ta hay tạo một class Product.cs trong thư mục Models, để cấu hình các thuộc tính dữ liệu

+ Models/Product.cs

public class Product
    {
        public int idProduct { get; set; }
        public string Title { get; set; }
        public string UrlImage { get; set; }
        public string Detail { get; set; }
        public decimal Price { get; set; }
    }

Tạo class EFDataContext.cs kế thừa từ class DBContext, DbContext là một lớp quan trọng trong Entity Framework. Nó là cầu nối giữa của các lớp thực thể và cơ sở dữ liệu
+ Models/EFDataContext.cs

using Microsoft.EntityFrameworkCore;
namespace CartASP_Core21.Models
{
    public class EFDataContext : DbContext
    {
        public EFDataContext(DbContextOptions<EFDataContext> options)
               : base(options) { }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Product>().HasKey(s => s.idProduct);

        }
        public DbSet<Product> Products { get; set; }
    }
}

Tiếp tục mở file Startup.cs trong project lên để gọi file EFDataContext.cs vừa tạo

public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            services.AddDbContext<EFDataContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("EFDataContext")));
        }

Ok vậy là xong! giờ ta chạy Migration để tạo database và table trong SQL SERVER cho ta
Open Nuget Packager Manager->Package Nuget Console lên chạy migration

add-migration dbcart
update-database

Sau khi tạo xong, bạn kiểm tra project sẽ thấy thư mục Migrations, và thử xem SQL SERVER của bạn có tạo ra database chưa nghe

Tạo dữ liệu mẫu: Bạn copy image đến thư mục wwwrooot/images


Ta nói rằng giỏ hàng của ta sẽ chứa tất sản phẩm và số lượng mua của từng sản phẩm của khách hàng
Tạo file Cart.cs trong thư mục Models

+ Models/Cart.cs

 public class Cart
    {
        public Product Product { get; set; }
        public int Quantity { get; set; }
    }

Mỗi lần mua một sản phẩm, ta lưu vào giỏ hàng gồm(Product,Quantity)
Chúng ta thường biết thường ta dùng SESSION để lưu giỏ hàng, đồng thời cần phải tạo một cái Key cho nó để ta có thể kiểm tra được, sản phẩm đó có tồn tại chưa
Vì thế để dùng được SESSION trong ASP.NET Core 2.1 bạn cần mở file Startup.cs lên và khai báo như sau:
Tại hàm ConfigureServices thêm dòng lệnh sau, khai báo thời gian cho nó

services.AddSession(options => {
                options.IdleTimeout = TimeSpan.FromMinutes(30);
});

Tại hàm Configure thêm dòng lệnh

app.UseSession();

Bạn có thể tìm hiểu thêm tại đây:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-3.1

Tiếp tục Controllers->add new Controller->ProductController.cs 

using Newtonsoft.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using CartASP_Core21.Models;
namespace CartASP_Core21.Controllers
{
    public class ProductController : Controller
    {
        private EFDataContext _db;
        // GET: Shop
        public ProductController(EFDataContext db)
        {
            this._db = db;
        }
        public ActionResult Index()
        {

            var _product = getAllProduct();
            ViewBag.product = _product;
            return View();
        }

    //GET ALL PRODUCT
        public List<Product> getAllProduct()
        {
            return _db.Products.ToList();
        }

        //GET DETAIL PRODUCT
        public Product getDetailProduct(int id)
        {
            var product = _db.Products.Find(id);
            return product;
        }

    }
}

+ Đoạn code trên ta gọi EFDataContext trong class EFDataContext.cs, để tạo lệnh kết nối tới database
+ getAllProduct(): Lấy tất cả các Product trong table Products
+ getDetailProduct(int id): Trả về product theo id cần tìm

 //ADD CART
        public IActionResult addCart(int id)
        {
            var cart = HttpContext.Session.GetString("cart");//get key cart
            if (cart == null)
            {
                var product = getDetailProduct(id);
                List<Cart> listCart = new List<Cart>()
                {
                    new Cart
                    {
                        Product = product,
                        Quantity = 1
                    }
                };
                HttpContext.Session.SetString("cart", JsonConvert.SerializeObject(listCart));

            }
            else
            {
                List<Cart> dataCart = JsonConvert.DeserializeObject<List<Cart>>(cart);
                bool check = true;
                for (int i = 0; i < dataCart.Count; i++)
                {
                    if (dataCart[i].Product.idProduct == id)
                    {
                        dataCart[i].Quantity++;
                        check = false;
                    }
                }
                if (check)
                {
                    dataCart.Add(new Cart
                    {
                        Product = getDetailProduct(id),
                        Quantity = 1
                    });
                }
                HttpContext.Session.SetString("cart", JsonConvert.SerializeObject(dataCart));
                // var cart2 = HttpContext.Session.GetString("cart");//get key cart
                //  return Json(cart2);
            }

            return RedirectToAction(nameof(Index));

        }

+ addCart(int id): Thêm một product vào giỏ hàng(cart)
- HttpContext.Session.GetString("cart"): Dùng lấy key "cart", sau đó kiểm tra xem, có tồn tại hay không
- Nếu cart==null: thì tạo List<Cart> listCart = new List<Cart>() chứa sản phẩm đầu tiên , tạo Session với key "cart"
- Nếu cart!=null: thì dùng JsonConvert.DeserializeObject<List<Cart>>(cart) chuyển đổi json về dạng List<Cart>
- Sau đó kiểm tra, nếu (dataCart[i].Product.idProduct == id): thì tăng quantity++;
- Ngược lại add product mới vào nửa
- Tạo Session mới chứa dữ liệu mới lại

public IActionResult ListCart()
        {
            var cart = HttpContext.Session.GetString("cart");//get key cart
            if (cart != null)
            {
                List<Cart> dataCart = JsonConvert.DeserializeObject<List<Cart>>(cart);
                if (dataCart.Count > 0)
                {
                    ViewBag.carts = dataCart;
                    return View();
                }
            }
            return RedirectToAction(nameof(Index));
        }

+ ListCart(): dùng hiển thị tất cả sản phẩm có trong giỏ hàng ra
- HttpContext.Session.GetString("cart"): lấy session key "cart"
- Nếu dataCart!=null: thì tạo List<Cart> chứ danh sách 
- Sau đó return ListCart.cshtml, foreach dữ liệu ra

[HttpPost]
        public IActionResult updateCart(int id, int quantity)
        {
            var cart = HttpContext.Session.GetString("cart");
            if (cart != null)
            {
                List<Cart> dataCart = JsonConvert.DeserializeObject<List<Cart>>(cart);
                if (quantity > 0)
                {
                    for (int i = 0; i < dataCart.Count; i++)
                    {
                        if (dataCart[i].Product.idProduct == id)
                        {
                            dataCart[i].Quantity = quantity;
                        }
                    }


                    HttpContext.Session.SetString("cart", JsonConvert.SerializeObject(dataCart));
                }
                var cart2 = HttpContext.Session.GetString("cart");
                return Ok(quantity);
            }
            return BadRequest();

        }

+ updateCart(int id, int quantity): chèn id product và quantity, để update product trong giỏ hàng(cart)
- HttpContext.Session.GetString("cart"): lấy tất cả product trong session key "cart"
- Tạo List<Cart> danh sách cart
- Kiểm tra sản phẩm, nếu dataCart[i].Product.idProduct == id, thì thay đổi dataCart[i].Quantity = quantity

 public IActionResult deleteCart(int id)
        {
            var cart = HttpContext.Session.GetString("cart");
            if (cart != null)
            {
                List<Cart> dataCart = JsonConvert.DeserializeObject<List<Cart>>(cart);

                for (int i = 0; i < dataCart.Count; i++)
                {
                    if (dataCart[i].Product.idProduct == id)
                    {
                        dataCart.RemoveAt(i);
                    }
                }
                HttpContext.Session.SetString("cart", JsonConvert.SerializeObject(dataCart));
                return RedirectToAction(nameof(ListCart));
            }
            return RedirectToAction(nameof(Index));
        }

+ deleteCart(int id): Kiểm tra id product có trong giỏ hàng không, nếu có thì xóa nó

Chỉnh sửa file Startup.cs , Gọi đến ProductControlelr.cs

app.UseMvc(routes =>
{
   routes.MapRoute(
       name: "default",
       template: "{controller=Product}/{action=Index}/{id?}");
});

+ Views/_ViewInport.cshtml: thêm 2 câu lệnh sau

@using Microsoft.AspNetCore.Http;
@using Newtonsoft.Json;

+ Views/Shared/_Layout.cshtml : Đặt đoạn mã sau trên đầu file

@{
    var data = Context.Session.GetString("cart");

    int coutCart = 0;
    if (data == null)
    {
        coutCart = 0;
    }
    else
    {
        var data2 = Context.Session.GetString("cart");
        List<Cart> dataCart = JsonConvert.DeserializeObject<List<Cart>>(data2);
        foreach (var item in dataCart)
        {
            coutCart += item.Quantity;
        }

    }

}

Đoạn code trên lấy danh sách giỏ hàng qua session key "cart", sau đó đếm nó, có bao nhiêu product trong giỏ hàng, mặc định  = 0

Chỉnh sửa lại chổ  RenderBody() trong _Layout.cshtml như sau, chèn thẻ <a></a> gọi tới ListCart.cshtml

<div class="container body-content">
        <br />

        <a asp-area=""
            asp-controller="Product"
            asp-action="ListCart" 
            style="background:#000;padding:10px;">Cart 
            <img src="~/images/cart.png" style="width:25px !important;height:25px;display:inline-block;" />
            @coutCart
         </a>

        <br />
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; 2020 - CartASP_Core21</p>
        </footer>
</div>

+ Views/Product/Index.cshtml

@model CartASP_Core21.Models.Product;
@{
    ViewData["Title"] = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<style>
    .list-product {
        width: 1200px;
        margin: auto;
        display: flex;
        flex-wrap: wrap;
    }

    .product {
        margin-top: 10px;
        margin-right: 10px;
        width: 120px;
        height: 200px;
    }

    img {
        width: 100px;
        height: 100px;
        display: block;
        margin: auto;
    }

    .title {
        display: block;
        font-size: 15px;
        font-weight: bold;
        text-align: center;
    }

    .viewProduct, .addProduct {
        width: calc((100% - 2px) / 2);
        display: inline-block;
        float: left;
        padding: 2px;
        font-size: 12px;
        box-sizing: border-box;
        color: #fff;
        background: #808080;
        text-decoration: none;
        text-align: center;
    }

    .addProduct {
        margin-left: 2px;
    }
</style>

<div class="list-product">
    @foreach (var item in ViewBag.product)
    {
    <div class="product">
        <img src="~/images/@item.UrlImage" class="img-responsive" />
        <span class="title">@item.Title</span>
        <a href="" class="viewProduct">View</a>
        <a asp-controller="Product" asp-action="addCart" asp-route-id="@item.idProduct" class="addProduct">Add order</a>
    </div>
    }


</div>

+ Views/Product/ListCart.cshtml

@{
    ViewData["Title"] = "ListCart";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<style>
    td, th {
        padding: 10px;
    }
</style>
<script src="~/js/jquery.min.js"></script>
<script>
    $(document).ready(function () {
        $(".updateCart").click(function (event) {
            event.preventDefault();
            var quantity = $(".quantity_" + $(this).attr("data-id")).val();
            console.log(quantity);
            $.ajax({
                type: "POST",
                url:"@Url.Action("updateCart","Product")",
                data: {
                    id: $(this).attr("data-id"),
                    quantity:quantity
                },
                success: function (result) {
                    window.location.href = '@Url.Action("ListCart","Product")';
                }
            });
        });
    });
</script>
<div class="list-cart">
    <table class="cart" border="1">
        <tr>
            <th>STT</th>
            <th>Title</th>
            <th>Image</th>
            <th>Quantity</th>
            <th>Price</th>
            <th>Total Price</th>
            <th>Update</th>
            <th>Delete</th>
        </tr>
        @{
            int STT = 0;
            foreach (var item in ViewBag.carts)
            {
                string txt_class = "quantity_" + item.Product.idProduct;
                STT++;
                int total = item.Product.Price * item.Quantity;
                <tr>
                    <td>@STT</td>
                    <td>@item.Product.Title</td>
                    <td><img src="~/images/@item.Product.UrlImage" width="100" height="100" /></td>
                    <td><input type="number" class="@txt_class" value="@item.Quantity" /></td>
                    <td>@item.Product.Price</td>
                    <td>@total</td>
                    <td><a href="" data-id="@item.Product.idProduct" class="updateCart">Update</a></td>
                    <td><a asp-controller="Product" asp-action="deleteCart" asp-route-id="@item.Product.idProduct">Delete</a></td>
                </tr>
            }
        }

    </table>
</div>

- Trong đoạn code trên ta hiển thị tất cả sản phẩm có trong giỏ hàng ra
- Viết câu lệnh jquery, để cập nhật giỏ hàng bằng ajax

Github: Cart in ASP.NET CORE 2.1