使用MVC4,Ninject,EF,Moq,構建一個真實的應用電子商務SportsStore(十一)


我們的項目已經進入了非常好的良性循環,項目中涵蓋了多數現在的主流開源框架的使用。就Ninject而言,我們的運用是非常的成功,沒有任何一點多余的代碼,你不在每個控制器的構造函數中去調用Ninject的任何代碼,控制器工廠類會自動為你注入你想要的對象,這一點希望大家能記住並運用到你今后的項目中,之所以作為重點提及它,是因為網上有很多錯誤的教程和做法,既沒有顯示出Ninject的本質,也誤導了讀者。今天,我們就對該項目的剩余功能做個完結,下篇我們將把注意力集中在網絡安全上,沒有跟上進度的兄弟,或沒有理解的很透的兄弟,一定要多讀幾遍,多調試下代碼,有很多細節的東西,我們沒有寫太多的筆墨,這並不是我懶,而是我覺得那些對你沒有問題,你搞得定它!

添加模塊驗證

我們的管理員也是人,所以也有犯錯誤的可能,而且幾乎是一定會犯錯誤。所以,我們也要添加些驗證的規則,去規避錯誤,例如在價格中輸入了負數,商品簡介沒有填寫等,而我們還是使用系統的功能和特性去完成這些工作,這也有助於我們對系統的理解,現在我們就在Product類上添加以下特性:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace SportsStore.Domain.Entities
{
    public class Product
    {
        [HiddenInput(DisplayValue = false)]
        public int ProductID { get; set; }
        [Required(ErrorMessage = "請輸入商品名稱")]
        public string Name { get; set; }

        [DataType(DataType.MultilineText)]
        [Required(ErrorMessage = "請輸入商品介紹")]
        public string Description { get; set; }
        [Required]
        [Range(0.01, double.MaxValue, ErrorMessage = "請輸入一個有效的價格")]
        public decimal Price { get; set; }
        [Required(ErrorMessage = "請指定一個商品類別")]
        public string Category { get; set; }
    }
}

編輯一個Product時,Html.EditorForModel helper 方法就創建一個form元素,MVC框架添加markup並加載了CSS類,這些都是顯示驗證錯誤所必需的,運行一下程序,我們的驗證規則開始發揮作用了:

image

結果並非我們所預期的,而是拋出了一個異常,仔細分析原因,原來是因為我們改動了數據模型,導致EFDbContext不再適用於原來的數據庫,我們需要做數據遷移。

Entity Framework Migrations

處理這種情況,我們可能有下面幾種選擇方案:

  • 刪除數據庫並重建它, 當這將丟失數據庫中的數據, 而且你的老板和客戶都會不喜歡這種方案。
  • 根據錯誤提示做Code-Migration。

那么,我們怎么該怎么做呢?

Step 1:在Global.asax, 在Application_Start 事件中添加下行代碼:

Database.SetInitializer<EFDbContext>(null); //For Changes

哦對了,如果你的程序不能認出Database這個名字,你要添加一條引用語句在代碼的引用部分

using System.Data.Entity;

Step 2:打開程序包管理控制台,工具/庫程序包管理器/程序包管理控制台,執行下列命令:

PM> Enable-Migrations -ProjectName SportsStore.Domain -ContextTypeName SportsStore.Domain.Concrete.EFDbContext

執行命令后, 你應該在工程文件夾下,看到一個新文件夾被命名為 ‘Migrations’ 的文件夾。

  • Configuration - ‘Seed’ 方法.
  • InitialCreate - Up 和Down 方法.

Step 3 執行下面的命令:
PM> add-migration -ProjectName SportsStore.Domain  Initial

這個命令將會在‘Migrations’ 文件夾中添加一個命名為<TimeStamp>_Initial.cs的文件。

 

Step 4:PM> update-database -ProjectName SportsStore.Domain

更新數據庫,現在運行程序,你應該可以看到預期的結果了

現在我們可以把剛才添加到Global.asax的Application_Start 事件中的代碼再完善一下:

 Database.SetInitializer<EFDbContext>(new CreateDatabaseIfNotExists<EFDbContext>()); //For Changes

我們的驗證方法並不科學,不但導致程序不可讀,而且,每次驗證都要在服務器端處理,而不能直接在瀏覽器端解決,這很浪費資源和時間,之所以繞出這么遠,主要是為了掩飾EF框架數據遷移的用法,如果你覺得這是有助於學習,你得到了幫助,那么請為我留下個推薦吧!

改用客戶端校驗

打開_AdminLayout.cshtml文件,添加如下語句:

    <script src="~/Scripts/jquery-1.7.1.js"></script>
    <script src="~/Scripts/jquery.validate.min.js"></script>
    <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
    <title></title>

你可以在通過設置web.config文件,去關閉整個工程的客戶端驗證:

<appSettings>
    <add key="webpages:Version" value="2.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
 <add key="ClientValidationEnabled" value="false" /> <add key="UnobtrusiveJavaScriptEnabled" value="false" />
    <add key="Email.WriteAsFile" value="true"/>
  </appSettings>

添加新產品

創建一個新的action方法到AdminController類:

public ViewResult Create()
        {
            return View("Edit", new Product());
        }

我們這個創建的方法沒有自己對應的View,我們重用了Edit view,一個action方法鏈接到一個存在的view上,是個完美的設計,在這里,我們注入一個新的產品對象作為View model,使Edit view彈出一個空窗體,然而,我們還是過於樂觀了,現在的代碼還不是我們所期望的結果。因為它不能返回到初始的Edit action,我們需要修改一下Views/Admin/Edit.cshtml文件:

@model SportsStore.Domain.Entities.Product
@{
    ViewBag.Title = "Admin: 編輯" + @Model.Name;
    Layout = "~/Views/Shared/_AdminLayout.cshtml";
    }

    <h1>Edit @Model.Name</h1>

@using (Html.BeginForm("Edit", "Admin"))
{
    @Html.EditorForModel()
    <input type="submit" value="保存" />
    @Html.ActionLink("取消並返回", "Index")
}

 

我們為BeginForm()添加了兩個參數,保證返回的地址是正確的。

刪除商品

添加一個新的方法到IProductRepository接口:

Product DeleteProduct(int productID);

然后到EFProductRepository類中去實現這個方法:

public Product DeleteProduct(int productID)
        {
            Product dbEntry = context.Products.Find(productID);
            if (dbEntry != null)
            {
                context.Products.Remove(dbEntry);
                context.SaveChanges();
            }
            return dbEntry;
        }

最后一步,我們要在AdminController中添加一個刪除的action方法,這里我們只要支持Post,因為這個刪除的操作不是冪等操作,冪等操作不在本系了教程之內,如果有朋友感興趣,可以去查閱相關資料,這里你只需知道,Get方法make一個請求時,不需要用戶的顯示同意,就會釋放瀏覽器和緩存,這個極為危險的,為了回避這一點,我們必須使用Post。

        [HttpPost]
        public ActionResult Delete(int productId)
        {

            Product deletedProduct = repository.DeleteProduct(productId);
            if (deletedProduct != null)
            {
                TempData["message"] = string.Format("{0} was deleted",
                deletedProduct.Name);
            }
            return RedirectToAction("Index");
        }

到AdminTest文件中,添加測試方法吧:

        [TestMethod]
        public void Can_Delete_Valid_Products() {
                // Arrange - create a Product
                Product prod = new Product { ProductID = 2, Name = "Test" };
                // Arrange - create the mock repository
                Mock<IProductsRepository> mock = new Mock<IProductsRepository>();
                mock.Setup(m => m.Products).Returns(new Product[] {
                new Product {ProductID = 1, Name = "P1"},
                prod,
                new Product {ProductID = 3, Name = "P3"},
                }.AsQueryable());
                // Arrange - create the controller
                AdminController target = new AdminController(mock.Object);
                // Act - delete the product
                target.Delete(prod.ProductID);
                // Assert - ensure that the repository delete method was
                // called with the correct Product
                mock.Verify(m => m.DeleteProduct(prod.ProductID));
        }

 

運行它去看看今天的成果吧!如果你完全理解和掌握了這個系列的所有知識點,現在你已經是MVC的專家了,你可以應用這些技術到你的項目中。今天就到這里吧,下篇我們將進行最后的潤色,爭取做到完美收官!有任何問題可直接留言或Email給我,請繼續保持你熱情,讓我們善始善終吧!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM