zoukankan      html  css  js  c++  java
  • ASP.NET MVC5 学习笔记-3 Model

    1. Model

    1.1 添加一个模型

    注意,添加属性时可以输入"prop",会自动输入代码段。

    public class CheckoutAccount
    {
        public int Id { get; set; }
    
        public string AccountNumber { get; set; }
    
        public string FirstName { get; set; }
    
        public string LastName { get; set; }
    
        public string Balance { get; set; }
    }
    

    1.2 添加一个“包含读写操作的MVC 5控制器”CheckingAccountController

    去掉Details的参数Id,因为现在我没还没有数据库。

    public ActionResult Details()
    {
        return View();
    }
    

    1.3 在Details上添加视图

    在Details动作上右键,添加视图:

    • 视图名称Details
    • 模板Details
    • 模型CheckoutAccount
    • 数据上下文类:空
    • 创建为分布视图:不勾选
    • 引用脚本库:不勾选
    • 使用布局页:默认布局
    @model AspNetMVCEssential.Models.CheckoutAccount
    
    @{
        ViewBag.Title = "Details";
    }
    
    <h2>Details</h2>
    
    <div>
        <h4>CheckoutAccount</h4>
        <hr />
        <dl class="dl-horizontal">
            <dt>
                @Html.DisplayNameFor(model => model.AccountNumber)
            </dt>
    
            <dd>
                @Html.DisplayFor(model => model.AccountNumber)
            </dd>
    
            <dt>
                @Html.DisplayNameFor(model => model.FirstName)
            </dt>
    
            <dd>
                @Html.DisplayFor(model => model.FirstName)
            </dd>
    
            <dt>
                @Html.DisplayNameFor(model => model.LastName)
            </dt>
    
            <dd>
                @Html.DisplayFor(model => model.LastName)
            </dd>
    
            <dt>
                @Html.DisplayNameFor(model => model.Balance)
            </dt>
    
            <dd>
                @Html.DisplayFor(model => model.Balance)
            </dd>
    
        </dl>
    </div>
    <p>
        @Html.ActionLink("Edit", "Edit", new { id = Model.Id }) |
        @Html.ActionLink("Back to List", "Index")
    </p>
    

    说明:

    • 第一行代码指明模板Model类型,任何@Model都是CheckoutAccount类型。
    • Html.DisplayNameFor(model => model.AccountNumber)模型属性的名称
    • Html.DisplayFor(model => model.AccountNumber)模型属性的值

    1.4 添加首页到Details页面的链接

    <div class="col-md-6 margin-top-20">
        <a href="@Url.Action("Details", "CheckingAccount")" class="btn btn-primary btn-lg btn-block"><span class="glyphicon glyphicon-question-sign"></span>余额查询</a>
    </div>
    

    @Url.Action("action", "controller")返回URL,而不是<a>


    2. 显示和验证模型属性

    2.1 清理Details视图

    @model AspNetMVCEssential.Models.CheckoutAccount
    
    @{
        ViewBag.Title = "Details";
    }
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h2>账户查询</h2>
            <dl class="dl-horizontal">
                <dt>
                    @Html.DisplayNameFor(model => model.AccountNumber)
                </dt>
    
                <dd>
                    @Html.DisplayFor(model => model.AccountNumber)
                </dd>
    
                <dt>
                    @Html.DisplayNameFor(model => model.FirstName)
                </dt>
    
                <dd>
                    @Html.DisplayFor(model => model.FirstName)
                </dd>
    
                <dt>
                    @Html.DisplayNameFor(model => model.LastName)
                </dt>
    
                <dd>
                    @Html.DisplayFor(model => model.LastName)
                </dd>
    
                <dt>
                    @Html.DisplayNameFor(model => model.Balance)
                </dt>
    
                <dd>
                    @Html.DisplayFor(model => model.Balance)
                </dd>
            </dl>
        </div>
    </div>
    

    2.2 修改Model

    [Display(Name="Account")]

    public class CheckoutAccount
    {
        public int Id { get; set; }
    
        [Display(Name = "账户")]
        public string AccountNumber { get; set; }
    
        public string FirstName { get; set; }
    
        public string LastName { get; set; }
        [Display(Name = "姓名")]
        public string Name { get { return FirstName + " " + LastName; } }
        [DataType(DataType.Currency)]
        public int Balance { get; set; }
    }
    

    2.3 创建Create对应的视图

    此时要选择引用脚本库。

    @using (Html.BeginForm()) 
    {
        @Html.AntiForgeryToken()
        
        <div class="form-horizontal">
            <h4>CheckoutAccount</h4>
            <hr />
            @Html.ValidationSummary(true)
    
            <div class="form-group">
                @Html.LabelFor(model => model.AccountNumber, new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.AccountNumber)
                    @Html.ValidationMessageFor(model => model.AccountNumber)
                </div>
            </div>
     }
    
    • @Html.LabelFor(model=>model.Account)产生Label
    • @Html.EditorFor(model=>model.Account)产生input
    • @Html.ValidationMessageFor(model=>model.AccountNumber)验证信息
    • @Html.ValidationSummary(true)验证汇总

    2.4 修改CheckAccount

    [Required]
    //[StringLength(10, MinimumLength = 6)]
    [RegularExpression(@"d{6,10}", ErrorMessage = "账户必须是6-10个字符")]
    [Display(Name = "账户")]
    public string AccountNumber { get; set; }
    
    [Required]
    public string FirstName { get; set; }
    
    [Required]
    public string LastName { get; set; }
    [Display(Name = "姓名")]
    public string Name { get { return FirstName + " " + LastName; } }
    
    [Required]
    [DataType(DataType.Currency)]
    public int Balance { get; set; }
    

    都是一些验证字段,注意每个都可以有ErrorMessage参数。

    --

    3. ViewModel

    ViewModel不在数据库中存储,专门用于处理Form,类似Django中的Form类。

    3.1 ViewModel定义

    打开Models文件夹下的AccountViewModel,我们看到ViewModel定义与普通Model并没有明显区别,只是命名约定都是以"ViewModel"结尾。

    public class LoginViewModel
    {
        [Required]
        [Display(Name = "用户名")]
        public string UserName { get; set; }
    
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "密码")]
        public string Password { get; set; }
    
        [Display(Name = "记住我?")]
        public bool RememberMe { get; set; }
    }
    

    3.2 ViewModel在Action中的应用

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            var user = await UserManager.FindAsync(model.UserName, model.Password);
            if (user != null)
            {
                await SignInAsync(user, model.RememberMe);
                return RedirectToLocal(returnUrl);
            }
            else
            {
                ModelState.AddModelError("", "Invalid username or password.");
            }
        }
    
        // 如果我们进行到这一步时某个地方出错,则重新显示表单
        return View(model);
    }
    

    可以看到ViewModel只是接收用户的输入并封装,然后再使用封装的字段查找数据库中对应的Model。

    3.3 ViewModel在视图中的应用

    @model AspNetMVCEssential.Models.LoginViewModel
    
    @{
        ViewBag.Title = "登录";
    }
    
    <h2>@ViewBag.Title。</h2>
    <div class="row">
        <div class="col-md-8">
            <section id="loginForm">
                @using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
                {
                    @Html.AntiForgeryToken()
                    <h4>使用本地帐户登录。</h4>
                    <hr />
                    @Html.ValidationSummary(true)
                    <div class="form-group">
                        @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
                        <div class="col-md-10">
                            @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
                            @Html.ValidationMessageFor(m => m.UserName)
                        </div>
                    </div>
                    ...
                }
                 ...
    

    ViewModel在视图中使用与普通Model并没有区别。


    4. 数据库对象和Entity Framework

    4.1 打开IdentityModels.cs文件

    打开文件后,我们看到两个类:

    • ApplicationUser继承自IdentityUser,后者是用户类
    • ApplicationDbContext继承自IdentityDbContext<ApplicationUser>,后者又继承自DbContext,数据库上下文类,直接与数据库进行交互。

    4.2 添加CheckoutAccount到数据库上下文

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }
    
        public DbSet<CheckoutAccount> CheckoutAccounts { get; set; }
    }
    

    这样就可以操作数据库了。

    4.3 添加Model外键

    我们对CheckoutAccount添加如下两个属性:

    public virtual ApplicationUser User { get; set; }
    public string ApplicationUserId { get; set; }
    

    其中的User使用virtual关键字是用来延迟加载的, 第二个属性用来指明外键,Entity Framework很聪明,能理解你的意思是指定它为外键。

    4.4 在Action中使用

    我们让用户在注册时系统自动生成一个CheckoutAccount记录:

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Register(RegisterViewModel model)
    {
        if (ModelState.IsValid)
        {
            var user = new ApplicationUser() { UserName = model.Email, Email = model.Email};
            var result = await UserManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {
                //创建CheckoutAcount
                var db = new ApplicationDbContext();
                var checkoutAccount = new CheckoutAccount
                {
                    AccountNumber = "00000123",
                    FirstName = model.FirstName,
                    LastName = model.LastName,
                    Balance = 0,
                    ApplicationUserId = user.Id
                };
                db.CheckoutAccounts.Add(checkoutAccount);
                db.SaveChanges();
    
                await SignInAsync(user, isPersistent: false);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                AddErrors(result);
            }
        }
    
        // 如果我们进行到这一步时某个地方出错,则重新显示表单
        return View(model);
    }
    

    运行,注册账户,我们会发现自动注册了CheckoutAccount对象,并且添加了外键。

    4.5 Model在视图中的使用以及@Model关键字

    之前使用ViewBag来向模板传递对象,ViewBag是动态类型的。ASP.NET MVC还提供传递强类型数据到模板的方法。强类型的好处是VS提供了智能提示。
    比如之前我们使用了

    return View(model);
    

    检查对应的视图:

    @model MvcMovie.Models.Movie
    
    @{
        ViewBag.Title = "Details";
    }
    
    <h2>Details</h2>
    
    <div>
        <h4>Movie</h4>
    	<hr />
        <dl class="dl-horizontal">
            <dt>
                @Html.DisplayNameFor(model => model.Title)
            </dt>
             @*Markup omitted for clarity.*@        
        </dl>
    </div>
    <p>
        @Html.ActionLink("Edit", "Edit", new { id = Model.ID }) |
        @Html.ActionLink("Back to List", "Index")
    </p>
    

    我们看到在模板头部我们添加了@model语句,然后我们就可以在视图中使用Model对象来访问对应的对象及其属性。

    对于列表型的数据,我们可以使用如下方法:

    @model IEnumerable<MvcMovie.Models.Movie> 
    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.ActionLink("Edit", "Edit", new { id=item.ID }) 
            </td>
        </tr>
    }
    

    5. Entity Framework Code First迁移

    现在我们想修改数据库,当然可以直接使用SQL Server修改,但也可以通过Code First迁移进行修改。

    5.1 修改CheckoutAccount

    [Required]
    [StringLength(10)]
    [Column(TypeName = "varchar")]
    [RegularExpression(@"d{6,10}", ErrorMessage = "账户必须是6-10个字符")]
    [Display(Name = "账户")]
    public string AccountNumber { get; set; }
    

    5.2 打开迁移

    打开NuPackage程序包管理器控制台。

    PM> Enable-Migrations -ContextType ApplicationDbContext
    正在检查上下文的目标是否为现有数据库...
    检测到使用数据库初始值设定项创建的数据库。已搭建与现有数据库对应的迁移“201410311619291_InitialCreate”的基架。若要改用自动迁移,请删除 Migrations 文件夹并重新运行指定了 -EnableAutomaticMigrations 参数的 Enable-Migrations。
    已为项目 AspNetMVCEssential 启用 Code First 迁移。
    

    5.3 添加迁移

    PM> Add-Migration AccountNumberChanges
    正在为迁移“AccountNumberChanges”搭建基架。
    此迁移文件的设计器代码包含当前 Code First 模型的快照。在下一次搭建迁移基架时,将使用此快照计算对模型的更改。如果对要包含在此迁移中的模型进行其他更改,则您可通过再次运行“Add-Migration AccountNumberChanges”重新搭建基架。
    

    也可以生成迁移脚本:

    PM> Update-Database -Script
    正在应用显式迁移: [201410311643166_AccountNumberChanges]。
    正在应用显式迁移: 201410311643166_AccountNumberChanges。
    

    此时系统会自动弹出一个包含Script的迁移。

    5.4 应用迁移

    PM> Update-Database -Verbose
    Using StartUp project 'AspNetMVCEssential'.
    Using NuGet project 'AspNetMVCEssential'.
    指定“-Verbose”标志以查看应用于目标数据库的 SQL 语句。
    目标数据库为: “aspnet-AspNetMVCEssential-20141031091551”(DataSource: .,提供程序: System.Data.SqlClient,来源: Configuration)。
    正在应用显式迁移: [201410311643166_AccountNumberChanges]。
    正在应用显式迁移: 201410311643166_AccountNumberChanges。
    ...
    正在运行 Seed 方法。
    

    5.5 回滚迁移

    PM> Update-Databse -TargetMigration IntialCreate
    

    5.6 自动迁移

    打开Migrations文件夹下的Configuration

    internal sealed class Configuration : DbMigrationsConfiguration<AspNetMVCEssential.Models.ApplicationDbContext>
    {
        public Configuration()
        {
            //设置自动迁移
            AutomaticMigrationsEnabled = true;
    
            ContextKey = "AspNetMVCEssential.Models.ApplicationDbContext";
        }
        //...
    }
    

    此后,发生变更时,直接使用:

    PM> Update-Database -Verbose
    Using StartUp project 'AspNetMVCEssential'.
    Using NuGet project 'AspNetMVCEssential'.
    指定“-Verbose”标志以查看应用于目标数据库的 SQL 语句。
    目标数据库为: “aspnet-AspNetMVCEssential-20141031091551”(DataSource: .,提供程序: System.Data.SqlClient,来源: Configuration)。
    没有挂起的显式迁移。
    正在应用自动迁移: 201410311656382_AutomaticMigration。
    System.Data.Entity.Migrations.Infrastructure.AutomaticDataLossException: 未应用自动迁移,因为自动迁移会导致数据丢失。如果要在可能导致数据丢失的情况下允许应用自动迁移,请在 DbMigrationsConfiguration 上将 AutomaticMigrationDataLossAllowed 设置为 "true"。也可以将 Update-Database 与 "-Force" 选项一起使用,或者构建基架执行显式迁移。
       在 System.Data.Entity.Migrations.DbMigrator.AutoMigrate(String migrationId, VersionedModel sourceModel, VersionedModel targetModel, Boolean downgrading)
    。。。
       在 System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)
    未应用自动迁移,因为自动迁移会导致数据丢失。如果要在可能导致数据丢失的情况下允许应用自动迁移,请在 DbMigrationsConfiguration 上将 AutomaticMigrationDataLossAllowed 设置为 "true"。也可以将 Update-Database 与 "-Force" 选项一起使用,或者构建基架执行显式迁移。
    

    我们看到上面报了一些错误,这是因为可能会造成丢失,这就让我们可以强制更新:

    PM> Update-Database -Verbose -Force
    
  • 相关阅读:
    sublime开启vim模式
    git命令行界面
    搬进Github
    【POJ 2886】Who Gets the Most Candies?
    【UVA 1451】Average
    【CodeForces 625A】Guest From the Past
    【ZOJ 3480】Duck Typing
    【POJ 3320】Jessica's Reading Problemc(尺取法)
    【HDU 1445】Ride to School
    【HDU 5578】Friendship of Frog
  • 原文地址:https://www.cnblogs.com/liulixiang/p/4299302.html
Copyright © 2011-2022 走看看