zoukankan      html  css  js  c++  java
  • 创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图

    创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图

    创建CRUD动作方法及视图

    参照VS自带的基架(Scaffold)系统-MVC Controller with views, using Entity Framework我们来创建CRUD方法。

    ① 将上一篇的Models/UserContext.cs文件中的用来指定使用的数据库逻辑的OnConfiguring方法删除,将逻辑移到Startup.cs文件中的ConfigureServices方法中。

    public void ConfigureServices(IServiceCollection services)
    {
        string connectionString = Configuration.GetConnectionString("MyConnection");
    
        services.AddDbContext<UserContext>(options =>
                    options.UseMySQL(connectionString));
    
        // Add framework services.
        services.AddMvc();
    }
    

    ② 在UserController.cs 构造函数中采用依赖注入来注入一个数据库上下文到该控制器。数据库上下文将被应用到控制器中的每一个CRUD方法。

    private readonly UserContext _context;
    
    public UserController(UserContext context)
    {
        _context = context;
    }
    

    ③ 在UserController.cs中添加基本的CRUD方法:

    // GET: /<controller>/
    public async Task<IActionResult> Index()
    {
        return View(await _context.Users.ToListAsync());
    }
    
    // GET: User/Details/1
    public async Task<IActionResult> Details(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }
    
        var user = await _context.Users.SingleOrDefaultAsync(u => u.ID == id);
    
        if (user == null)
        {
            return NotFound();
        }
    
        return View(user);
    }
    
    // GET: User/Create
    public IActionResult Create()
    {
        return View();
    }
    
    // POST: User/Create
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create([Bind("ID,Name,Email,Bio")]User user)
    {
        if (ModelState.IsValid)
        {
            _context.Add(user);
            await _context.SaveChangesAsync();
            return RedirectToAction("Index");
        }
        return View(user);
    }
    
    //GET: User/Edit/1
    public async Task<IActionResult> Edit(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }
    
        var user = await _context.Users.SingleOrDefaultAsync(u => u.ID == id);
        if (user == null)
        {
            return NotFound();
        }
        return View(user);
    }
    
    // POST: User/Edit/1
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Edit(int id, [Bind("ID,Name,Email,Bio")]User user)
    {
        if (id != user.ID)
        {
            return NotFound();
        }
    
        if (ModelState.IsValid)
        {
            try
            {
                _context.Update(user);
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!UserExists(user.ID))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }
            return RedirectToAction("Index");
        }
    
        return View(user);
    }
    
    //// GET: User/Delete/5
    public async Task<IActionResult> Delete(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }
    
        var user = await _context.Users.SingleOrDefaultAsync(u => u.ID == id);
        if (user == null)
        {
            return NotFound();
        }
        return View(user);
    }
    
    // POST: User/Delete/1
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> DeleteConfirmed(int id)
    {
        var user = await _context.Users.SingleOrDefaultAsync(u => u.ID == id);
        _context.Users.Remove(user);
        await _context.SaveChangesAsync();
        return RedirectToAction("Index");
    }
    
    private bool UserExists(int id)
    {
        return _context.Users.Any(e => e.ID == id);
    }
    

    一个http://localhost:5000/User 这样的请求到达User控制器后,将会从User表返回所有的数据,将将这些数据传递到Index视图:

    ④ 在Views/User文件夹中添加与上述Action方法名称相对应的Index.cshtml文件、Create.cshtml文件、Details.cshtml文件、Edit.cshtml文件、Delete.cshtml文件。

    Create.cshtml运行效果:

    Details.cshtml运行效果:

    Edit.cshtml运行效果:

    Delete.cshtml运行效果:

    强类型模型和@model关键字

    MVC提供了传递强类型对象给视图的能力,这样为你的代码提供了更好的编译时检查,并在VS中提供了更丰富的智能感知功能。

    查看UserController/Details方法:

    // GET: User/Details/1
    public async Task<IActionResult> Details(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }
    
        var user = await _context.Users.SingleOrDefaultAsync(u => u.ID == id);
    
        if (user == null)
        {
            return NotFound();
        }
    
        return View(user);
    }
    

    id参数通常作为路由数据来传递,比如 http://localhost:5000/user/details/1 会:

    • Controller设置为user(第一个URL段)
    • Action设置为details(第二个URL段)
    • id设置为1(第三个URL段)

    你也可以通过查询字符串来传递id:
    http://localhost:5000/user/details?id=1

    如果指定的User被找到,则User Model实例将被传递到Details视图:

    return View(user);
    

    查看Views/User/Details.cshtml文件:

    @model IEnumerable<MyFirstApp.Models.User>
    
    @{
        ViewData["Title"] = "Index - User List";
    }
    
    <h2>Index - User List</h2>
    
    <p>
        <a asp-action="Create">Create New</a>
    </p>
    
    <table class="table">
        <thead>
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.Name)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Email)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Bio)
                </th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in Model) {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Email)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Bio)
                </td>
                <td>
                    <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
                    <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                    <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
                </td>
            </tr>
            }
        </tbody>
    </table>
    

    你会发现在顶部有一个@model语句,你可以指定视图所期望的对象类型。

    @model MyFirstApp.Models.User
    

    @model指令允许你通过使用强类型的Model对象来访问从控制器传递到视图的User对象。例如,在Details.cshtml视图中,通过使用强类型的Model对象传递User的每一个字段到DisplayNameForDisplayFor HTML Helper。

    再来查看Index.cshtml文件和User控制器中的Index方法。注意在调用View方法时,是如何创建一个List对象的。下面的代码将从Index Action方法传递整个User到视图中。

    User控制器中的Index方法:

    public async Task<IActionResult> Index()
    {
        return View(await _context.Users.ToListAsync());
    }
    

    Index.cshtml文件最顶部:

    @model IEnumerable<MyFirstApp.Models.User>
    

    @model指令允许你访问通过强类型的Model从控制器传递到视图的User列表。例如,在Index.cshtml视图中,在强类型的Model对象上通过foreach语句遍历了整个User列表:

    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Email)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Bio)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
    }
    

    添加仓储类

    首先,新建一个Repositories文件夹。在该文件夹下定义一个IUserRepository接口。

    namespace MyFirstApp.Repositories
    {
        public interface IUserRepository
        {
            Task<IEnumerable<User>> GetAll();
            Task<User> Get(int id);
            void Add(User user);
            void Update(User user);
            void Delete(int id);
            bool UserExists(int id);
        }
    }
    

    接着再添加一个UserRepository来实现IUserRepository接口。将之前定义的UserContext.cs逻辑移到该类中,在UserRepository.cs 构造函数中采用依赖注入来注入一个数据库上下文(UserContext)到该仓储类。数据库上下文将被应用到仓储类中的每一个CRUD方法。

    public class UserRepository : IUserRepository
    {
        private readonly UserContext _context;
    
        public UserRepository(UserContext context)
        {
            _context = context;
        }
    
        public async Task<IEnumerable<User>> GetAll()
        {
            return await _context.Users.ToListAsync();
        }
    
        public async Task<User> Get(int id)
        {
            return await _context.Users.SingleOrDefaultAsync(u => u.ID == id);
        }
    
        public async void Add(User user)
        {
            //_context.Users.Add(user);
            _context.Add(user);
            await _context.SaveChangesAsync();
        }
    
        public async void Update(User user)
        {
            //_context.Users.Update(user);
            _context.Update(user);
            await _context.SaveChangesAsync();
        }
    
        public async void Delete(int id)
        {
            var user =   _context.Users.SingleOrDefault(u => u.ID == id);
            _context.Users.Remove(user);
            await _context.SaveChangesAsync();
        }
    
        public bool UserExists(int id)
        {
            return _context.Users.Any(e => e.ID == id);
        }
    }
    

    在Controller构造函数中依赖注入UserRepository

    再修改Controllers/UserController.cs文件,将private readonlyUserContext变量删除:

    private readonly UserContext _context;

    添加IUserRepository变量:

    private readonly IUserRepository _userRepository;
    public UserController(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }
    

    将所有方法中的_context操作删除,替换成_userRepository。例如,将Index方法中的_context.Users.ToListAsync()删除:

    return View(await _context.Users.ToListAsync());

    替换成

    return View(await _context.Users.ToListAsync());
    

    最终的UserController.cs如下:

    public class UserController : Controller
    {
        private readonly IUserRepository _userRepository;
    
        public UserController(IUserRepository userRepository)
        {
            _userRepository = userRepository;
        }
    
        // GET: /<controller>/
        public async Task<IActionResult> Index()
        {
            return View(await _userRepository.GetAll());
        }
    
        // GET: User/Details/1
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var user = await _userRepository.Get(id.Value);
    
            if (user == null)
            {
                return NotFound();
            }
    
            return View(user);
        }
    
        // GET: User/Create
        public IActionResult Create()
        {
            return View();
        }
    
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Create([Bind("ID,Name,Email,Bio")]User user)
        {
            if (ModelState.IsValid)
            {
                _userRepository.Add(user);
                return RedirectToAction("Index");
            }
            return View(user);
        }
    
        //GET: User/Edit/1
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var user = await _userRepository.Get(id.Value);
            if (user == null)
            {
                return NotFound();
            }
            return View(user);
        }
    
        // POST: User/Edit/1
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Edit(int id, [Bind("ID,Name,Email,Bio")]User user)
        {
            if (id != user.ID)
            {
                return NotFound();
            }
    
            if (ModelState.IsValid)
            {
                try
                {
                    _userRepository.Update(user);
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!_userRepository.UserExists(user.ID))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction("Index");
            }
    
            return View(user);
        }
    
        //// GET: User/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var user = await _userRepository.Get(id.Value);
            if (user == null)
            {
                return NotFound();
            }
            return View(user);
        }
    
        // POST: User/Delete/1
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public IActionResult DeleteConfirmed(int id)
        {
                _userRepository.Delete(id);
            return RedirectToAction("Index");
        }
    }
    

    注册仓储

    通过定义Repository接口,从MVC Controller中解耦该repository类。通过注入一个UserRepository来代替直接在Controller里面实例化一个UserRepository类。

    为了注入一个Repository到Controller,我们必须通过DI容器来注册它,打开Startup.cs文件,在ConfigureServices方法添加如下代码:

    // Add our repository type
    services.AddScoped<IUserRepository, UserRepository>();
    

    DataAnnotations & Tag Helpers

    我们为Models/User.cs文件添加DisplayDataType注解,首先要添加必要的命名空间using System.ComponentModel.DataAnnotations;

    再将属性在视图上显示成中文:

    Display Attribute指定字段的显示名,DataTypeAttribute指定数据类型。

    最终的显示效果如下:

    打开Views/User/Index.cshtml,你会发现Edit,Details,Delete链接是由MVC Core Anchor Tag Helper生成的。

    <td>
        <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
        <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
        <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
    </td>
    

    Tag Helpers允许服务器代码在Razor文件中参与创建和渲染HTML元素。在上述代码中,AnchorTagHelper从Controller Action动作方法和路由ID动态生成HTMLhref属性值。

    查看Startup.cs中的Configure方法:

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
    

    ASP.NET Core会将http://localhost:5000/User/Edit/4 转换成发送给User控制器的Edit方法(带有值为4的Id参数)的请求。

    查看UserController.cs中的[HttpPost]版本的Edit方法:

    // POST: User/Edit/1
    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Edit(int id, [Bind("ID,Name,Email,Bio")]User user)
    {
        if (id != user.ID)
        {
            return NotFound();
        }
    
        if (ModelState.IsValid)
        {
            try
            {
                _context.Update(user);
                await _context.SaveChangesAsync();
                //_userRepository.Update(user);
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!_userRepository.UserExists(user.ID))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }
            return RedirectToAction("Index");
        }
    
        return View(user);
    }
    

    [Bind] Attribute是一种防止over-posting(过度提交)的方式。应该只把你需要改变的属性包含到[Bind] Attribute中。

    [ValidateAntiForgeryToken] Attribute是用来防止伪造请求的,会与Views/User/Edit.cshtml视图文件生成的反伪造标记(Token)进行配对。Views/User/Edit.cshtml视图文件通过Form Tag Helper来生成反伪造标记(Token)。

    <form asp-action="Edit">
    

    Form Tag Helper生成一个隐藏的防伪标记必须和User控制器中的Eidt方法的[ValidateAntiForgeryToken]产生的防伪标记相匹配。

    查看Edit.cshtml,会发现基架系统(Scaffolding System)会为User类的每一个属性生成用来呈现的<label><input>元素。

    <form asp-action="Edit">
        <div class="form-group">
            <label asp-for="Email" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger" />
            </div>
        </div>
    </form>
    

    基架代码使用了多个Tag Helper方法来简化HTML标记。

    • Label Tag Helper用来显示字段的名字。
    • Input Tag Helper用来呈现HTML<input>元素。
    • Validation Tag Helper用来显示关联属性的验证信息。

    最终在浏览器中为<form>元素所生成的HTML如下:

    HTML<form>中的actionAttribute设置成POST到/User/Edit/idURL(所有<input>元素都在该<form>元素中)。当点击Save按钮时,表单数据会被发送(POST)到服务器。在</form>元素的上面显示了Form Tag Helper所生成的隐藏的XSRF反伪造标记。

    处理POST请求

    查看[HttpPost]版本的Edit方法:

    [ValidateAntiForgeryToken]验证Form Tag Helper中的反伪造标记生成器所生成的隐藏的XSRF反伪造标记。

    模型绑定(Model Binding)机制接受POST过来的表单数据并创建一个User对象并作为user参数。ModelState.IsValid方法验证从表单提交过来的数据可以用来修改一个User对象。如果数据有效,就可以进行保存。被更新的数据通过调用数据库的上下文(Database Context)的SaveChangesAsync方法来保存到数据库中。数据保存之后,代码将用户重定向到UserController类的Index方法。该页面会显示刚刚被改动后的最新的用户集合。

    在表单被POST到服务器之前,客户端验证会检查所有字段上的验证规则,如果有任何验证错误,则会显示该错误信息,并且表单不会被发送到服务器。如果禁用了JS,将不会有客户端验证,但服务器会检测POST过来的数据是无效的,表单会重新显示错误信息。

    参考文档

    个人博客

    我的个人博客

  • 相关阅读:
    dev控件学习笔记之----CxGrid2
    dev控件学习笔记之----CxGrid
    Bootstrap-table一个表格组件神器(学习一)使用详情
    vue学习(十一)组件全攻略
    bower学习(三)npm和bower的区别
    bower学习(二)命令
    brower学习(一)安装
    动画方案animate.css学习(一)
    vue学习(十)v-bind全攻略
    Vue学习(九)v-model全攻略
  • 原文地址:https://www.cnblogs.com/charliechu/p/6113429.html
Copyright © 2011-2022 走看看