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给我,请继续保持你热情,让我们善始善终吧!

  • 相关阅读:
    图片上传-下载-删除等图片管理的若干经验总结3-单一业务场景的完整解决方案
    图片上传-下载-删除等图片管理的若干经验总结2
    HDU 1195 Open the Lock
    HDU 1690 Bus System
    HDU 2647 Reward
    HDU 2680 Choose the best route
    HDU 1596 find the safest road
    POJ 1904 King's Quest
    CDOJ 889 Battle for Silver
    CDOJ 888 Absurdistan Roads
  • 原文地址:https://www.cnblogs.com/bzwang/p/MVC4_EF_Mirgration.html
Copyright © 2011-2022 走看看