zoukankan      html  css  js  c++  java
  • 使用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给我,请继续保持你热情,让我们善始善终吧!

  • 相关阅读:
    android HashMap的几种遍历方法
    Android android:windowSoftInputMode属性详解
    Linux环境变量的设置和查看方法
    linux 定时执行shell脚本
    Linux 定时执行shell脚本_crontab
    Linux下修改字符集,转自
    解决Linux下Oracle中文乱码的一些心得体会 ,转自
    SSH Secure Shell Client连接Linux 命令行显示中文乱码问题 和oracle 查询数据中文乱码问题
    VMware 虚拟机(linux)增加根目录磁盘空间 转自
    linux 虚机增加硬盘大小 转自
  • 原文地址:https://www.cnblogs.com/bzwang/p/MVC4_EF_Mirgration.html
Copyright © 2011-2022 走看看