建立ASP.NET Core MVC應用程序(4)-添加CRUD動做方法和視圖

建立ASP.NET Core MVC應用程序(4)-添加CRUD動做方法和視圖

建立CRUD動做方法及視圖

參照VS自帶的基架(Scaffold)系統-MVC Controller with views, using Entity Framework咱們來建立CRUD方法。html

① 將上一篇的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視圖:mvc

④ 在Views/User文件夾中添加與上述Action方法名稱相對應的Index.cshtml文件、Create.cshtml文件、Details.cshtml文件、Edit.cshtml文件、Delete.cshtml文件。app

Create.cshtml運行效果:asp.net

Details.cshtml運行效果:async

Edit.cshtml運行效果:函數

Delete.cshtml運行效果:post

強類型模型和@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過來的數據是無效的,表單會從新顯示錯誤信息。

參考文檔

我的博客

個人我的博客

相關文章
相關標籤/搜索