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
    
  • 相关阅读:
    Ansible跳板机自动部署
    nginx展示文件目录
    【转】消息钩子注册浅析
    windows临界区
    windbg定位死锁
    Windows工作集内存
    我的spring boot,杨帆、起航!
    CursorFileManager对cursor文件的读写
    eclipse执行maven install命令时跳过test
    bASE--Risk
  • 原文地址:https://www.cnblogs.com/liulixiang/p/4299302.html
Copyright © 2011-2022 走看看