Professional Documents
Culture Documents
Ghi nhớ học MVC 5
Ghi nhớ học MVC 5
Cách đơn giản nhất để thêm Ninject vào một dự án MVC là sử dụng hỗ trợ được tích hợp của Visual Studio cho NuGet, thứ giúp việc cài đặt
một loạt các gói và giữ cho chúng được cập nhật trở nên dễ dàng. Tôi đã sử dụng NuGet trong Chươ ng 2 để cài đặt thư viện Bootstrap,
nhưng có một danh mục lớn các gói có sẵn, bao gồm cả Ninject.
Chọn Tools Library Package Manager Package Manager Console trong Visual Studio
để mở cửa sổ dòng lệnh NuGet và nhập vào các lệnh sau:
Lệnh đầu tiên cài đặt các gói cốt lõi của Ninject và số còn lại thì cài đặt phần mở rộng cho lõi khiến Ninject hoạt động tốt với các ứng
dụng ASP.NET (tôi sẽ giải thích ngay sau đây). Đừng hiểu nhầm với phần tham chiếu MVC3 trong tên của gói cuối cùng– nó vẫn hoạt động
tốt với MVC 5.
Sử dụng Ninject
public ActionResult Index()
{
IKernel ninjectKernel = new StandardKernel();
ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
IValueCalculator calc = ninjectKernel.Get<IValueCalculator>(); Commented [TMT1]: Thay thế cho
LinqValueCalculator calc = new
LinqValueCalculator();
ShoppingCart cart = new ShoppingCart(calc)
{
Products = products
};
Tôi cần tạo ra một triển khai của interface Ninject.IKernel, bằng cách tạo ra một instance mới của lớp StandardKernel.
Ninject có thể được mở rộng và tùy chỉnh để sử dụng các loại kernel khác nhau, nhưng tôi chỉ cần StandardKernel được tích hợp sẵn
trong chương này. (Trên thực tế, tôi đã được sử dụng Ninject nhiều năm và tôi chỉ cần mỗi StandardKernel).
Bước thứ hai của quá trình này là cấu hình kernel của Ninject để nó hiểu các đối tượng triển khai mà tôi muốn sử dụng cho mỗi
interface mà tôi đang làm việc với. Đây là câu lệnh từ bảng liệt kê mà làm việc đó:
ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
Ninject sử dụng các tham số kiểu của C # để tạo ra một mối quan hệ: tôi thiết lập interface mà tôi muốn làm việc với như tham số kiểu cho
phương thức Bind và gọi phương thức To trên kết quả nó trả về. Tôi thiết lập các lớp triển khai tôi muốn khởi tạo như tham số kiểu cho
phương thức To. Câu lệnh này nói với Ninject rằng các phụ thuộc trên interface IValueCalculator nên được giải quyết bằng cách
tạo ra một instance của lớp LinqValueCalculator. Bước cuối cùng là sử dụng Ninject để tạo ra một đối tượng, thông qua
phương thức Get của kernel, như thế này:
IValueCalculator calc = ninjectKernel.Get<IValueCalculator>();
Các tham số kiểu sử dụng cho phương thức Get nói với Ninject rằng interface nào mà tôi đang quan tâm và kết quả từ phương thức này là
một instance của kiểu triển khai tôi đã chỉ định với phương thức To lúc nãy.
Thay đổi đầu tiên tôi sẽ làm là tạo ra một custom dependency resolver. MVC Framework sử dụng một dependency resolver để tạo ra các
instance của các lớp mà nó cần phải phục vụ các yêu cầu. Bằng cách tạo ra một custom resolver, tôi có thể đảm bảo rằng MVC Framework
sử dụng Ninject bất cứ khi nào nó tạo ra một đối tượng–bao gồm cả các instance của các controller, chẳng hạn.
Để thiết lập resolver, Tôi đã tạo một thư mục mới có tên là Infrastructure, đó là thư mục mà tôi sử dụng để bỏ các lớp mà không
phù hợp với các thư mục khác trong một ứng dụng MVC. Tôi đã thêm một tập tin lớp mới vào thư mục có tên là
NinjectDependencyResolver.cs, nội dung của nó bạn có thể xem trong
public class NinjectDependencyResolver : IDependencyResolver
{
private IKernel kernel;
// GET: Home
public ActionResult Index()
{
/*IKernel ninjectKernel = new StandardKernel();
ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
IValueCalculator calc = ninjectKernel.Get<IValueCalculator>();*/ Commented [TMT3]: Bị loại bỏ
ShoppingCart cart = new ShoppingCart(calc)
{
Products = products
};
InThreadScope() Tạo một instance duy nhất được được dùng để giải quyết các phụ thuộc cho các đối tượng được yêu cầu
bởi một thread duy nhất.
InRequestScope() Tạo một instance duy nhất được dùng để giải quyết các phụ thuộc cho các đối tượng được yêu cầu bởi
một yêu cầu HT T P duy nhất .
using System.Collections.Generic;
using SportsStore.Domain.Entities;
namespace SportsStore.Domain.Abstract
{
public interface IProductRepository
{
IEnumerable<Product> Products { get; }
}
}
Inerface này sử dụng IEnumerable<T> cho phép người gọi nhận được một trình tự của các đối tượng Product, mà không cho biết làm thế
nào và nơi nào mà dữ liệu được lưu hay truy xuất. Một lớp mà phụ thuộc vào interface IProductRepository có thể có được các đối tượng
Product mà không cần phải biết gì về nơi nó đến hoặc làm thế nào mà các lớp thực thi sẽ chuyển nó đi. Đây là bản chất của repository
pattern. Tôi sẽ xem lại interface IProductRepository trong suốt quá trình phát triển để thêm vào các tính năng.
Bước 2. Thêm một constructor để khai báo một sự phụ thuộc trên interface IproductRepository trong mỗi controller, kèm theo 1
biến private
Bước 3. Viết 1 class định nghĩa tất cả các phương thức có trong interface
Thêm PagingInfo.cs
Để hiển thị link liên kết chuyển thông tin đến view về số lượng của các trang sẵn có, trang hiện tại, và tổng sản phẩm trong repository.
public class PagingInfo
{
public int TotalItems { get; set; }
public int ItemsPerPage { get; set; }
public int CurrentPage { get; set; }
public int TotalPages
{
get
{
return (int)Math.Ceiling((decimal)TotalItems / ItemsPerPage);
}
}
}
Thêm phương thức HTML Helper
Bây giờ tôi có một view model, tôi có thể thực thi các phương thức HTML helper, mà tôi sẽ gọi PageLinks. Tạo một thư mục mới trong
project SportsStore.WebUI tên là HtmlHelpers và thêm một lớp mới gọi là PagingHelpers.cs.
public static class PagingHelpers
{
public static MvcHtmlString PageLinks(this HtmlHelper html, PagingInfo pagingInfo,
Func<int, string> pageUrl)
{
StringBuilder result = new StringBuilder();
Thêm Namespace cho phương thức HTML Helper vào file Views/web.config
<add namespace="SportsStore.WebUI.HtmlHelpers"/>
Thêm Dữ liệu View Model
Tôi chưa hoàn toàn sẵn sàng để sử dụng phương thức HTML helper. Tôi chưa cung cấp một thể hiện của lớp view model PagingInfo đến view.
Tôi có thể làm điều này bằng cách sử dụng tính năng view bag, nhưng tôi muốn bọc tất cả dữ liệu mà tôi sẽ gửi từ controller đến view trong
một lớp view model duy nhất. Để làm điều này, tôi đã thêm một file tên là ProductsListViewModel.cs vào thư mục Models của project
SportsStore.WebUI
return View(model);
}
Cập nhật file List.cshtml
@model SportsStore.WebUI.Models.ProductsListViewModel
@{
ViewBag.Title = "Products";
}
@foreach (var p in Model.Products)
{
<div>
<h3>@p.Name</h3>
@p.Description
<h4>@p.Price.ToString("c")</h4>
</div>
}
@Html.ActionLink("Trang chủ", "List", "Product", null, new { @class="btn btn-block btn-default btn-lg"})
Nút Add to Cart sẽ được xuất hiện bên cạnh mỗi sản phẩm trong catalog. Nhấn vào nút này sẽ thể thiện
tổng số sản phẩm mà customer đã được lựa chọn cho đến thời điểm đó, bao gồm cả tổng tiền. Lúc này,
customer sẽ có thể nhấn vào nút Continue Shopping để quay trở lại trang danh mục sản phẩm, hoặc nhấn nút
Checkout Now để hoàn tất việc đặt hàng và kết thúc việc shopping.
Defining Cart Entity
public class Cart
{
private List<CartLine> lineCollection = new List<CartLine>();
/// <summary>
/// Thêm 1 sản phẩm vào Cart
/// </summary>
/// <param name="product">Thông tin sản phẩm</param>
/// <param name="quantity">Số lượng</param>
public void AddItem(Product product, int quantity)
{
//Trước khi thêm kiểm tra xem nó có trong giỏ hàng chưa
CartLine line = lineCollection.Where(p => p.Product.ProductID == product.ProductID)
.FirstOrDefault();
//Chưa có thì thêm
if(line==null)
{
lineCollection.Add(new CartLine { Product = product, Quantity = quantity });
}else//có rồi thì cộng dồn số lượng
{
line.Quantity += quantity;
}
}
/// <summary>
/// Xóa thông tin sản phẩm trong Cart
/// </summary>
/// <param name="product">Thông tin của sản phẩm cần xóa</param>
public void RemoveItem(Product product)
{
lineCollection.RemoveAll(l => l.Product.ProductID == product.ProductID);
}
/// <summary>
/// Tính tổng thành tiền
/// </summary>
/// <returns></returns>
public decimal ComputeTotalValue()
{
return lineCollection.Sum(p => p.Product.Price);
}
/// <summary>
/// Xóa toàn bộ thông tin giỏ hàng
/// </summary>
public void Clear()
{
lineCollection.Clear();
}
/// <summary>
/// Lấy toàn bộ thông tin của giỏ hàng
/// </summary>
public IEnumerable<CartLine> Lines
{
get
{
return lineCollection;
}
}
}
public class CartLine
{
public Product Product
{
get; set;
}
public int Quantity
{
get; set;
}
}
Class CartLine được sử dụng cho class Cart, được định nghĩa chung một tên, để đại diện cho sản phẩm đã được chọn bở customer và
lượng lớn người dùng muốn mua.
Có một vài điểm cần chú ý về Controller này.Đây là lần đầu tác giả sử dụng tính năng trạng thái phiên ASP.NET
đối với store và lấy lại đối tượng Cart.Đây là mục đích của phương thức GetCart.ASP.NET là một tính năng phiên đẹp
mà nó sử dụng cookies hoặc viết lại URL để liên kết nhiều thành phần yêu cầu chung từ người dùng tới tới hình thức
của một phiên truy cập. Những tính năng liên quan là trạng thái phiên mà nó liên kết dữ liệu với phiên. Đây là ý tưởng
hợp cho class Cart. Tác giả muốn mỗi người dùng sẽ có một giỏ hàng riêng, và mong muốn giỏ hàng sẽ tạo thành mối
liên kết với dữ liệu. Dữ liệu liên kết với phiên sẽ bị xoá khi một phiên hết hạn sử dụng ( Thường bởi vì một người
dùng sẽ không tạo yêu cầu trong một khoảng thời gian nào đó), điều đó có nghĩa là tác giả không cần quản lý kho hàng
hoặc vòng đời của đối tượng Cart . Để thêm đối tượng vào phiên trạng thái, tác giả đã thêm vào giá trị cho đối tượng
Session như sau:
Session["Cart"] = cart;
Để lấy lại đối tượng lần nữa. Chỉ cần đọc chung một khoá:
Cart cart = (Cart)Session["Cart"];
Hướng dẫn: đối tượng trạng thái phiên được lưu trữ ở bộ nhớ mặc đinh của ASP.NET server, nhưng bạn có
thể hiệu chỉnh lại các phương pháp lưu trữ, bao gồm cả việc dùng SQL database. Details : Pro ASP.NET MVC5
Platform.
Về phương thức Add to Cart, Remove from Cart, tác giả đã sử dụng tên thông số khớp với yếu tố
Input trong HTML form được tạo nằm trong ProductSummary.cshtml view. Điều này cho phép MVC
Framework liên kết những giá trị tới từ Post với những thông số này, nghĩa là không cần hiệu chỉnh form.
Displaying the Contents of the Cart
Điểm cuối cùng cần chú ý về controller Cart là cả hai phương thức Add to Cart và Remove from Cart
điều gọi phương thức RedirectToAction. Điều này sẽ ảnh hưởng tới việc gửi đi cấu trúc chuyển hướng HTTP tới
trình duyệt client, hỏi trình duyệt về yêu cầu một URL mới. Trong trường hợp này, tác giả đã yêu cầu trình duyệt
để gửi yêu cầu một URL mà sẽ gọi phương thức hoạt động Index của controller Cart.
Tạo ra một class mới tên là CartIndexViewModel.cs trong thư mục Models của project SportStore.WebUI.
public class CartIndexViewModel
{
public Cart Cart { get; set; }
public string RetrunUrl { get; set; }
}
Bây giờ ta đã có view model, ta có thể hoàn thành phương thức hoạt động Index trong class điều khiển
Cart.
public ActionResult Index(string returnUrl)
{
return View(new CartIndexViewModel { Cart = GetCart(), ReturnUrl = returnUrl });
}
Completing the Cart
Sử dụng Model Binding
Tôi tạo một mô hình Binder tuỳ chỉnh bằng triển khai interface System.Web.Mvc.IModelBinder. Để thực hiện, thêm một thư mục vào project
SportsStore.WebUI được gọi là Infrastructure/Binders và tạo một tập tin lớp CartModelBinder.cs
public class CartModelBinder : IModelBinder
{
private const string sessionKey = "Cart";
<td>
@using (Html.BeginForm("RemoveFromCart", "Cart"))
{
@Html.Hidden("productID", line.Product.ProductID)
@Html.HiddenFor(x=>x.ReturnUrl)
<input type="submit" class="btn btn-sm btn-warning" value="Xóa" />
}
</td>
Adding the Cart Summary
public PartialViewResult Summary(Cart cart)
{
return PartialView(cart);
}
Tạo thêm view
@model SportsStore.Domain.Entities.Cart
<div class="navbar-right">
@Html.ActionLink("Checkout", "Index", "Cart",
new { returnUrl = Request.Url.PathAndQuery },
new { @class = "btn btn-default navbar-btn" })
</div>
[HttpPost]
public ViewResult Checkout(Cart cart, ShippingDetails shippingDetails)
{
if (cart.Lines.Count() == 0)
{
ModelState.AddModelError("", "Sorry, your cart is empty!");
}
if (ModelState.IsValid)
{
orderProcessor.ProcessOrder(cart, shippingDetails);
cart.Clear();
return View("Completed");
}
else
{
return View(shippingDetails);
}
}
@{
ViewBag.Title = "SportStore: Checkout";
}
<h2>Check out now</h2>
<p>Please enter your details, and we'll ship your goods right away!</p>
@using (Html.BeginForm())
{ @Html.ValidationSummary("Thông báo lỗi",htmlAttributes:new { @class="text-danger"})
<h3>Ship to</h3>
<div class="form-group">
<label>Name:</label>
@Html.TextBoxFor(x => x.Name, new { @class = "form-control" })
</div>
<h3>Address</h3>
foreach (var property in ViewData.ModelMetadata.Properties)
{
if (property.PropertyName != "Name" && property.PropertyName != "GiftWrap")
{
<div class="form-group">
<label>@(property.DisplayName ?? property.PropertyName)</label>
@Html.TextBox(property.PropertyName, null, new { @class = "form-control" })
</div>
}
}
<h3>Options</h3>
<div class="checkbox">
<label>
@Html.EditorFor(x => x.GiftWrap) Gift wrap these items
</label>
</div>
<div class="text-center">
<input class="btn btn-primary" type="submit" value="Complete order" />
</div>
}
3. Bật tắt tính năng xác thực phía Client bằng câu lệnh trong từng VIEW muốn sử dụng
HtmlHelper.ClientValidationEnabled = false;
HtmlHelper.UnobtrusiveJavaScriptEnabled = false;
Hoặc thiết lặp cho cả hệ thống bằng cách chỉnh sửa trong file Web.config
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" /
</appSettings>
Xác định tên người dùng và mật khẩu trong tập tin Web.config
<authentication mode="Forms">
<forms loginUrl="/Account/Login" timeout="2880">
<credentials passwordFormat="Clear">
<user name="admin" password="024125"/>
</credentials>
</forms>
</authentication>
Thêm thuộc tính quyền sở hữu vào tập tin AdminControl.cs
Thêm thuộc tính [Authorize] vào đầu controller Admin
Các phương pháp xác nhận thông tin được cung cấp bởi người sử dụng.
Phương pháp SetAuthCookie thêm một cookie để đáp ứng các trình duyệt, do đó người dùng không cần phải xác thực mỗi khi họ thực hiện một
yêu cầu.
Tạo một thư mục mới có tên gọi Abstract trong tập tin Infrastructure của dự án SportsStore.WebUI và thêm một giao diện mới gọi là IAuthProvider.
Nội dung của giao diện này:
namespace SportsStore.WebUI.Infrastructure.Abstract
{
public interface IAuthProvider
{
bool Authenticate(string username, string password, bool rememberMe = false);
}
}
namespace SportsStore.WebUI.Infrastructure.Concrete
{
public class FormsAuthProvider : Abstract.IAuthProvider
{
public bool Authenticate(string username, string password, bool rememberMe=false)
{
#pragma warning disable CS0618 // Type or member is obsolete
bool result = FormsAuthentication.Authenticate(username, password);
#pragma warning restore CS0618 // Type or member is obsolete
if (result)
FormsAuthentication.SetAuthCookie(username, rememberMe);
return result;
}
}
}
[DisplayName("Ghi nhớ")]
public bool RememberMe { get; set; }
}
Tạo controller
public class AccountController : Controller
{
private IAuthProvider account;
[HttpPost]
public ActionResult Login(LoginViewModel model,string returnUrl)
{
if (ModelState.IsValid)
{
if (account.Authenticate(model.UserName, model.Password, model.RememberMe))
return Redirect(returnUrl ?? Url.Action("Index", "Admin"));
else
{
ModelState.AddModelError("", "Tên đăng nhập hoặc tài khoản không đúng!");
return View();
}
}
return View();
}
}
Tạo view
Thêm dòng sau đây vào đầu của controller muốn thay đổi redirect Url
[Route("DangNhap")]
Remove ?returnurl=%2f
Thêm bộ code sau vào tập tin Global.asax.cs
#region Custum Redirect Url
private const string ReturnUrlRegexPattern = @"\?ReturnUrl=.*$";
public MvcApplication()
{
PreSendRequestHeaders += MvcApplicationOnPreSendRequestHeaders;
}
private void MvcApplicationOnPreSendRequestHeaders(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(redirectUrl)
|| !System.Text.RegularExpressions.Regex.IsMatch(redirectUrl, ReturnUrlRegexPattern))
{
return;
Response.RedirectLocation = System.Text.RegularExpressions.Regex.Replace(redirectUrl,
ReturnUrlRegexPattern,
string.Empty);
}
#endregion
Tạo modal
Chèn đoạn code sau vào dưới cùng của trang view
<script>
$(document).ready(function () {
$('#deleteProductModal').on('show.bs.modal', function (event) { // id of the modal with event
var button = $(event.relatedTarget) // Button that triggered the modal
var productid = button.data('productid') // Extract info from data-* attributes
var productname = button.data('productname')
// And if you wish to pass the productid to modal's 'Yes' button for further processing
modal.find('button.btn-danger').val(productid)
})
})
</script>
Tạo 1 div giữ chỗ cho modal
<!-- Modal -->
<div class="modal fade" id="deleteProductModal" tabindex="-1" role="dialog" aria-labelledby="deleteProductModalLabel" aria-
hidden="true"> // thêm 1 option nữa để không thể bỏ qua modal bằng việc click chuột sang chỗ khác
//-------------------- data-backdrop="false" --------------------------//
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-
hidden="true">×</span></button>
<h4 class="modal-title" id="deleteProductModalLabel">Confirm Delete</h4>
</div>
<div class="modal-body">
Are you sure want to delete the product?
</div>
<div class="modal-footer">
@using (Html.BeginForm("Delete", "Admin", FormMethod.Post))
{ //Mặc định
<button name="ProductID" type="submit" class="btn btn-danger">
Yes
</button>
<script>
function callModal(jQuery) {
var title = 'Thông báo lỗi'
var content = 'Bạn không thể block user này'
</script>
Gọi nó bằng cách dùng callModal();
return PartialView("GetMenuXml",listMenu.ToList());
}
Tạo Partial view có tên GetMenuXml
@{
Layout = null;
@model IEnumerable<SportsStore.Domain.Entities.ListMenu>
<script>
$('button#menuLink').on('click', function (event) { // id of the modal with event
var button = $(this);
var value = button.val();
$("#textFind").val(value);
});
<div class="modal-body">
<p class="warning">
<ul>
<li>
Nhấp chuột trái để chọn
</li>
<li>
Nhập đúp chuột để chọn và đóng
</li>
</ul>
</p>
@Html.Action("GetMenuXml", "GetAllLinkInApp")
</div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('#MenuModal').on('show.bs.modal', function (event) { // id of the modal with event
})
})
</script>
Chèn dòng này vào trang _Layout
<!--Open modal to get menu link-->
@Html.Partial("_ModalGetMenu")
<!--End modal to get menu link-->
Nhúng CKFinder và CKEditor vào Website
1. Chép toàn bộ file trong CKFinder & CKEditor vào nơi nào đó trong Website
2. Mở file Config.ascx trong CKFinder và sửa thông tin sau
LicenseName = "TranMinhThuan";
LicenseKey = "ANYKPAVJ3CRKMYT2SJU22L9SSQYC1HEP";
3. Viết đoạn Script sau đây vào View cần nhúng CKFinder & CKEditor
<script>
$("#btnSelectImg").on("click", function (e) {
e.preventDefault();
var finder = new CKFinder();
finder.selectActionFunction = function (url) {
url = url.replace("%20", " ");
url = url.replace("%2C", ",");
$("#txtImage").val(url);
};
finder.popup();
});
4. Để sử dụng được Ckeditor phải chuyển thành TextArea cho tất cả các textbox cần chuyển
5. Thêm attribute [ValidateInput(false)] vào action gọi view để không bị lỗi
6. Chuyển các textbox thành @Html.Raw(@Model.Description) để hiển thị được nội dung.