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


上一篇中,我們建立了一個基本的項目框架,如果你細心的去研究這個框架,你一定已經發現,我們實際上已經使用了一個領域模型代替了MVC中的model,為什么要這么做呢?只要是因為在MVC這個古老的三層架構中,M的本意是用來向Controller提供數據,封裝業務邏輯的,C在通過調用View來展示數據給用戶。反過來,用戶通過觸發View的某些事件來將自己的意圖傳遞給C,C再分發用戶的命令道M,去獲取用戶想要的結果。這是一種理想的狀態,在真正的項目中,經常會出現M繞過C,直接調用V,而View也會直接調用M的現象。這就導致了一種錯誤的、混亂的數據流的出現,是項目潛在風險遞增的根源。MVP就是為了克服這種現象而產生的,好了,我們不去管什么MVP,MVVM了,現在就 言歸正傳,回到我們的項目中,繼續展示MVC的精彩與魅力。
 
我們知道,我們需要一些途徑或方式,去數據庫中取得Product entities。為了保持架構上的完美,我們要遵循持久邏輯與域模型實體分離的原則,要做到這一點,我們使用repository 設計模式. 我們不需要擔心怎樣去實現持久層,我們從定義一個接口開始,去啟動它。
 
在Abstract文件夾上右擊,選擇添加一個接口,命名為IProductsRepository,代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SportsStore.Domain.Entities;

namespace SportsStore.Domain.Abstract
{
    public interface IProductsRepository
    {
        IQueryable<Product> Products { get; }
    }
}

這個借口使用了IQueryable<T>接口去獲取一個Product對象,我們沒有告訴它去哪或怎么樣去取得數據,一個使用IProductsRepository 接口的類能夠取得Product 對象,而不需要知道它們從哪來或被誰傳遞,這就是 repository設計模式的本質。接下來我們就通過添加一些特性到我們代碼中,去再次拜訪一下這個接口。
 
構建一個Mock Repository
現在我們已經定義了一個 abstract interface, 我們能夠實現這個持久化機制並且掛接到數據庫,不過這是不是現在要做的,為了能夠啟動這個項目的其他部分,現在我們要創建一個IProductsRepository接口的Mock實現,我們需要在我們的SportsStore.WebUI工程的NinjectControllerFactory 類的AddBindings方法中去做這件事。
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using Moq;
using Ninject;
 
namespace SportsStore.WebUI.Infrastructure
{
    public class NinjectControllerFactory: DefaultControllerFactory
    {
 
            private IKernel ninjectKernel;
 
            public NinjectControllerFactory() {
                ninjectKernel = new StandardKernel();
                AddBindings();
            }
 
            protected override IController GetControllerInstance(RequestContext
                requestContext, Type controllerType) {
 
                return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType);
            }
 
            private void AddBindings() {
 
                Mock<IProductsRepository> mock = new Mock<IProductsRepository>();
 
                mock.Setup(m => m.Products).Returns(new List<Product> {
                    new Product { Name = "Football", Price = 25 },
                    new Product { Name = "Surf board", Price = 179 },
                    new Product { Name = "Running shoes", Price = 95 }
                }.AsQueryable());
                ninjectKernel.Bind<IProductsRepository>().ToConstant(mock.Object);
            }
         }
    }
 
為了使這些添加的代碼能夠正常的運行,需要添加幾個命名空間,但是我們創建Mock repository實現的過程使用了Moq技術,AsQueryable 方法是一個 LINQ 擴展方法,它轉換IEnumerable<T>進入
IQueryable<T>, 這里我們需要去匹配我們的接口簽名。無論 IProductsRepository在哪獲得了一個請求, 我們都需要Ninject去返回同樣的mock對象,這就是 為什么我們使用ToConstant方法的原因。
...
ninjectKernel.Bind<IProductRepository>(). ToConstant(mock.Object);
...
 
展示產品列表
 
已經做了這么久,我們還沒有看到任何可視化的效果,這對於有些心急的朋友來說是不公平的,看不見有任何成績出來,將會打擊我們做項目的信心,這對開發團隊是很不利的事情,現在就讓我們添加一個Controller到
SportsStore.WebUI工程中,選擇添加控制器,命名為ProductController,確保模板選型為空,如下圖:
 
 
接下來,你要刪除VS自動為你添加的代碼,並用如下代碼代替:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
 
namespace SportsStore.WebUI.Controllers
{
    public class ProductController : Controller
    {
        private IProductsRepository repository;
        public ProductController(IProductsRepository productRepository)
        {
            this.repository = productRepository;
        }
 
        public ViewResult List()
        {
            return View(repository.Products);
        }
 
    }
}
 
如果你的工程中,using SportsStore.Domain.Abstract; 這條語句存在錯誤,你需要添加對SportsStore.Domain工程的引用,如下圖:
 
現在要做的是添加一個View,在List方法上右擊並選擇添加View,如圖:
 
這里請注意了,在模型類的下拉列表中,你並不能找到IEnumerable<SportsStore.Domain.Entities.Product>項,你需要手工輸入它。然后,點擊添加按鈕,創建View。
 
渲染View數據
 
@model IEnumerable<SportsStore.Domain.Entities.Product>
 
@{
    ViewBag.Title =   "Products";
}
@foreach (var p in Model) {
<div class="item">
        <h3>@p.Name</h3>
        @p.Description
        <h4>@p.Price.ToString("c")</h4>
</div>
}
 
我們改變一下這個頁的標題,注意這里我們不需要使用Razor text 或者@:elements去展示數據,因為每行內容都是一個HTML 元素.
 
設置Default Route
 
現在我們需要去做的,就是告訴MVC框架,當一個請求到達時,我們的網站要映射到
ProductController類的List 活動方法,這需要去修改
App_Start/RouteConfig.cs文件的
RegisterRoutes方法,代碼如下:
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
 
namespace SportsStore.WebUI
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = " Product", action = " List", id = UrlParameter.Optional }
            );
        }
    }
}
 
完成修改后,運行你的應用,你將看到如下畫面:
 
 
你可以去下載我的源碼,地址是:
 
這部分我們展示了我們應用,運用了部分Moq技術和Ninject技術,但是我們現在的數據還只是Mock對象中的模擬數據,還不是真正的數據庫中的數據,下一部分,我們將引入EF框架,去領略一下ORM數據操作的風采。如果你覺得我寫的辛苦,你覺得你得到了你想要的東西,那么請推薦它給其他有需要的人吧!請繼續關注我的續篇,精彩才剛剛開始!


免責聲明!

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



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