天天看点

C# MVC 自学笔记—6 编辑方法和编辑视图

==============================翻译==============================

运行该应用程序并浏览到

Movies

控制器通过将/Movies追加到您的浏览器的地址栏中的 URL。将鼠标指针悬停在编辑链接,看到它链接到的 URL。

C# MVC 自学笔记—6 编辑方法和编辑视图

编辑链接是由Views\Movies\Index.cshtml视图中的

Html.ActionLink

方法生成的:

@Html.ActionLink("Edit", "Edit", new { id=item.ID })       
C# MVC 自学笔记—6 编辑方法和编辑视图

Html

对象是一个帮助器,公开使用System.Web.Mvc.WebViewPage基类的类上的属性。利用帮助程序的

ActionLink

方法,可以很容易地动态生成相应的 HTML 超链接,以链接到控制器上的操作方法。

ActionLink

方法的第一个参数是要呈现的链接文本 (例如,

<a>Edit Me</a>

)。第二个参数是要调用的操作方法的名称。最后一个参数是数据生成的路由 (在本例中,ID 为 4 的) 中的匿名对象。

上图中所示的生成的链接是http://localhost:xxxxx/电影/编辑/4。默认的路由 (在App_Start\RouteConfig.cs建立) 采用 URL 模式

{controller}/{action}/{id}

。因此,ASP.NET 将http://localhost:xxxxx/电影/编辑/4转化到

Movies

控制器与参数

ID

等于 4 的

Edit

操作方法的请求。检查App_Start\RouteConfig.cs文件中的以下代码。

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", 
            id = UrlParameter.Optional }
    );
}      

您还可以传递使用查询字符串的操作方法参数。例如,URLhttp://localhost:xxxxx/电影/编辑? ID = 4还将参数

ID

为 4 的传递给

Edit

Movies

控制器的操作方法。

C# MVC 自学笔记—6 编辑方法和编辑视图

打开

Movies

控制器。两个

Edit

操作方法如下所示。

//
// GET: /Movies/Edit/5

public ActionResult Edit(int id = 0)
{
    Movie movie = db.Movies.Find(id);
    if (movie == null)
    {
        return HttpNotFound();
    }
    return View(movie);
}

//
// POST: /Movies/Edit/5

[HttpPost]
public ActionResult Edit(Movie movie)
{
    if (ModelState.IsValid)
    {
        db.Entry(movie).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(movie);
}      

注意第二个

Edit

操作方法的前面是

HttpPost

属性。此属性指定的

Edit

方法的重载可以仅为 POST 请求调用。您可以将

HttpGet

属性应用于第一种编辑方法,但这并不必要,因为它是默认值。(我们会参阅于隐式地为

HttpGet

方法分配的

HttpGet

属性的操作方法。

HttpGet

Edit

方法获取电影 ID 参数、 查找电影使用实体框架

Find

方法,并返回到编辑视图的选定的影片。ID 参数指定默认值为零,如果不带参数调用该

Edit

的方法。如果不能找到一部电影,则返回HttpNotFound。当脚手架系统创建的编辑视图时,它审查

Movie

类并创建呈现

<label>

<input>

元素的每个属性的类的代码。下面的示例显示了编辑视图生成的:

@model MvcMovie.Models.Movie

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Movie</legend>

        @Html.HiddenFor(model => model.ID)

        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.ReleaseDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.ReleaseDate)
            @Html.ValidationMessageFor(model => model.ReleaseDate)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Genre)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Genre)
            @Html.ValidationMessageFor(model => model.Genre)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Price)
            @Html.ValidationMessageFor(model => model.Price)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}      

注意视图模板如何在文件的顶部有一

@model MvcMovie.Models.Movie

声明 — — 这指定视图期望的模型视图模板的类型

Movie

类型.

基架的代码使用的帮助器方法的几种简化的 HTML 标记。

Html.LabelFor

helper 显示字段 ("标题"、"ReleaseDate"、"流派"或"价格") 的名称。

Html.EditorFor

帮助器将呈现 HTML

<input>

元素。

Html.ValidationMessageFor

帮助器将显示与该属性相关联的任何验证消息。

运行该应用程序,然后定位到/Movies的 URL。单击编辑链接。在浏览器中查看页面源代码。表单的表单元素的 HTML 如下所示。

<form action="/Movies/Edit/4" method="post">    <fieldset>
        <legend>Movie</legend>

        <input data-val="true" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="4" />

        <div class="editor-label">
            <label for="Title">Title</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" id="Title" name="Title" type="text" value="Rio Bravo" />
            <span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>
        </div>

        <div class="editor-label">
            <label for="ReleaseDate">ReleaseDate</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" data-val="true" data-val-date="The field ReleaseDate must be a date." data-val-required="The ReleaseDate field is required." id="ReleaseDate" name="ReleaseDate" type="text" value="4/15/1959 12:00:00 AM" />
            <span class="field-validation-valid" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
        </div>

        <div class="editor-label">
            <label for="Genre">Genre</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" id="Genre" name="Genre" type="text" value="Western" />
            <span class="field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
        </div>

        <div class="editor-label">
            <label for="Price">Price</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number." data-val-required="The Price field is required." id="Price" name="Price" type="text" value="2.99" />
            <span class="field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
</form>      

<input>

元素在其

action

属性设置为发送到/Movies/EditURL 的 HTML

<form>

元素。窗体数据将张贴到服务器,单击编辑按钮时。

处理 POST 请求

下面的清单显示

HttpPost

版本的

Edit

操作方法。

[HttpPost] 
public ActionResult Edit(Movie movie)  
{ 
    if (ModelState.IsValid)  
    { 
        db.Entry(movie).State = EntityState.Modified; 
        db.SaveChanges(); 
        return RedirectToAction("Index"); 
    } 
    return View(movie); 
}      

ASP.NET MVC 中的模型联编程序已过帐的窗体值并创建一个

Movie

对象,作为

movie

参数传递。

ModelState.IsValid

方法验证提交表单中的数据可以用于修改 (编辑或更新)

Movie

对象。如果数据是有效的电影数据将保存到的

Movies

集合

db(MovieDBContext

instance)。通过调用

SaveChanges

方法

MovieDBContext

的情况下,新的电影数据保存到数据库。保存数据之后,代码把用户重定向到的

MoviesController

类,其中显示了

Index

操作方法的电影收藏,包括刚才所做的更改。

如果发送的值不是有效的系统会将他们重新显示在窗体中。Edit.cshtml视图模板中的

Html.ValidationMessageFor

佣工照顾显示相应的错误消息。

C# MVC 自学笔记—6 编辑方法和编辑视图

注意要支持 jQuery 验证为非英语区域设置,使用逗号 (",") 十进制的小数点,你必须包括globalize.js和你具体cultures/globalize.cultures.js文件 (从https://github.com/jquery/globalize) 和 JavaScript 可以使用

Globalize.parseFloat

。下面的代码演示对要与"FR-FR"文化工作的 Views\Movies\Edit.cshtml 文件的修改:

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    <script src="~/Scripts/globalize.js"></script>
    <script src="~/Scripts/globalize.culture.fr-FR.js"></script>
    <script>
        $.validator.methods.number = function (value, element) {
            return this.optional(element) ||
                !isNaN(Globalize.parseFloat(value));
        }
        $(document).ready(function () {
            Globalize.culture('fr-FR');
        });
    </script>
    <script>
        jQuery.extend(jQuery.validator.methods, {    
            range: function (value, element, param) {        
                //Use the Globalization plugin to parse the value        
                var val = $.global.parseFloat(value);
                return this.optional(element) || (
                    val >= param[0] && val <= param[1]);
            }
        });

    </script>
}      

十进制的字段可能需要一个逗号,不是小数点。作为临时的修复程序,可以将全球化元素添加到项目根 web.config 文件。下面的代码显示设置为美国英语的文化全球化元素。

<system.web>
    <globalization culture ="en-US" />
    <!--elements removed for clarity-->
  </system.web>      

所有

HttpGet

方法都遵循类似的模式。他们获得一个电影对象 (或对象的列表,如

Index

),并将模型传递给视图。

Create

方法将一个空影片对象传递给创建视图。创建、 编辑、 删除或以其他方式修改数据的方法这样做

HttpPost

重载的方法。修改 HTTP GET 方法中的数据是安全风险,在博客邮政条目中所述ASP.NET MVC 提示 #46 — — 不使用删除链接,因为它们创建安全漏洞。在 GET 方法中修改数据也违反了 HTTP 的最佳做法和建筑的其余部分图案,指定 GET 请求不应更改应用程序的状态。换句话说,执行 GET 操作,应该是安全的操作,没有任何副作用,不会修改您的持久化的数据。

添加一个搜索方法和搜索视图

在本节中,您将添加允许您搜索电影按类型排列的主题或名称

SearchIndex

操作方法。这将可使用/Movies/SearchIndex的 URL。该请求将显示一个 HTML 表单,包含输入的元素,用户可以输入要搜索的一部电影。当用户提交窗体时,在操作方法将获取用户张贴的搜索值和使用的值在数据库中搜索。

显示 SearchIndex 窗体

通过将

SearchIndex

操作方法添加到现有的

MoviesController

类开始。该方法将返回包含 HTML 表单的视图。下面是代码:

public ActionResult SearchIndex(string searchString) 
{           
    var movies = from m in db.Movies 
                 select m; 
 
    if (!String.IsNullOrEmpty(searchString)) 
    { 
        movies = movies.Where(s => s.Title.Contains(searchString)); 
    } 
 
    return View(movies); 
}      

SearchIndex

方法的第一行创建以下的LINQ查询,以选择看电影:

var movies = from m in db.Movies 
                 select m;      

这里只是定义了一个查询,并没有真正对数据存储运行查询。

如果

searchString

参数包含一个字符串,修改电影查询要筛选的值的搜索字符串,使用以下代码:

if (!String.IsNullOrEmpty(searchString)) 
    { 
        movies = movies.Where(s => s.Title.Contains(searchString)); 
    }      

s => s.Title

上面的代码是一个Lambda 表达式。例如,其中的方法在上面的代码中使用标准查询运算符方法的参数的方法基于LINQ查询使用,lambda。当他们被定义或被修改的调用 (如

Where

OrderBy

方法时不执行 LINQ 查询。相反,延迟查询执行,这意味着表达式的计算延迟,直到其实现的价值实际上来反复或

ToList

方法调用。在

SearchIndex

示例中,SearchIndex 视图中执行查询。有关延迟的查询执行的详细信息,请参阅查询执行.

现在,您可以实现

SearchIndex

视图,以便向用户显示该窗体。在

SearchIndex

方法内右键单击,然后单击添加视图。在添加视图对话框中,指定您要将

Movie

对象传递到视图模板作为其模型类。在基架模板列表中,选择列表,然后单击添加.

C# MVC 自学笔记—6 编辑方法和编辑视图

当您单击添加按钮时,创建Views\Movies\SearchIndex.cshtml视图模板。因为您选择 (启用了基架) 在脚手架模板列表中,Visual Studio 将自动生成的列表视图中的一些默认标记。脚手架创建 HTML 表单。它审查

Movie

类,并创建用于呈现

<label>

元素的类的每个属性的代码。下面的列表显示了生成创建视图:

@model IEnumerable<MvcMovie.Models.Movie> 
 
@{ 
    ViewBag.Title = "SearchIndex"; 
} 
 
<h2>SearchIndex</h2> 
 
<p> 
    @Html.ActionLink("Create New", "Create") 
</p> 
<table> 
    <tr> 
        <th> 
            Title 
        </th> 
        <th> 
            ReleaseDate 
        </th> 
        <th> 
            Genre 
        </th> 
        <th> 
            Price 
        </th> 
        <th></th> 
    </tr> 
 
@foreach (var item in Model) { 
    <tr> 
        <td> 
            @Html.DisplayFor(modelItem => item.Title) 
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.ReleaseDate) 
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.Genre) 
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.Price) 
        </td> 
        <td> 
            @Html.ActionLink("Edit", "Edit", new { id=item.ID }) | 
            @Html.ActionLink("Details", "Details", new { id=item.ID }) | 
            @Html.ActionLink("Delete", "Delete", new { id=item.ID }) 
        </td> 
    </tr> 
} 
 
</table>      

运行该应用程序,然后定位到/Movies/SearchIndex。如追加查询字符串

?searchString=ghost

到的 URL。显示已筛选的电影。

C# MVC 自学笔记—6 编辑方法和编辑视图

如果您更改的

SearchIndex

方法,有一个名为

id

参数的签名,

id

参数将匹配

{id}

占位符在Global.asax文件中设置的默认路由。

{controller}/{action}/{id}      

SearchIndex

的原始方法看起来像这样::

public ActionResult SearchIndex(string searchString) 
{           
    var movies = from m in db.Movies 
                 select m; 
 
    if (!String.IsNullOrEmpty(searchString)) 
    { 
        movies = movies.Where(s => s.Title.Contains(searchString)); 
    } 
 
    return View(movies); 
}      

修改后的

SearchIndex

方法将如下所示:

public ActionResult SearchIndex(string id) 
{ 
    string searchString = id; 
    var movies = from m in db.Movies 
                 select m; 
 
    if (!String.IsNullOrEmpty(searchString)) 
    { 
        movies = movies.Where(s => s.Title.Contains(searchString)); 
    } 
 
    return View(movies); 
}      

现在,可以将搜索标题作为路由数据 (URL 部分) 而不是作为查询字符串值传递。

C# MVC 自学笔记—6 编辑方法和编辑视图

但是,你不能指望用户修改 URL,每次他们想要搜索的一部电影。所以,现在您将添加用户界面,帮助他们筛选的电影。如果您更改了的

SearchIndex

方法来测试如何传递路线绑定 ID 参数的签名,更改它以便您

SearchIndex

方法采用一个名为

searchString

的字符串参数:

public ActionResult SearchIndex(string searchString) 
{           
     var movies = from m in db.Movies 
                  select m; 
 
    if (!String.IsNullOrEmpty(searchString)) 
    { 
        movies = movies.Where(s => s.Title.Contains(searchString)); 
    } 
 
    return View(movies); 
}      

打开Views\Movies\SearchIndex.cshtml文件,并只在

@Html.ActionLink("Create New", "Create")

之后, 添加以下内容:

@using (Html.BeginForm()){    
         <p> Title: @Html.TextBox("SearchString")<br />  
         <input type="submit" value="Filter" /></p> 
        }      

下面的示例演示具有添加筛选标记的Views\Movies\SearchIndex.cshtml文件的一部分。

@model IEnumerable<MvcMovie.Models.Movie> 
 
@{ 
    ViewBag.Title = "SearchIndex"; 
} 
 
<h2>SearchIndex</h2> 
 
<p> 
    @Html.ActionLink("Create New", "Create") 
     
     @using (Html.BeginForm()){    
         <p> Title: @Html.TextBox("SearchString") <br />   
         <input type="submit" value="Filter" /></p> 
        } 
</p>      

Html.BeginForm

帮助器创建开放

<form>

标记。在用户通过单击筛选按钮提交窗体时,

Html.BeginForm

帮助器将导致窗体张贴到其本身。

运行该应用程序并试着寻找一部电影。

C# MVC 自学笔记—6 编辑方法和编辑视图

没有任何

HttpPost

重载的

SearchIndex

方法。你不需要它,因为该方法不更改状态的应用程序,只筛选数据。

您可以添加以下

HttpPost SearchIndex

方法。在这种情况下,操作调用程序将匹配

HttpPost SearchIndex

方法,并且

HttpPost SearchIndex

方法将运行,如下图所示。

[HttpPost] 
public string SearchIndex(FormCollection fc, string searchString) 
{ 
    return "<h3> From [HttpPost]SearchIndex: " + searchString + "</h3>"; 
}      
C# MVC 自学笔记—6 编辑方法和编辑视图

但是,即使您添加此

HttpPost

版本的

SearchIndex

方法,有这一切如何执行的限制。想象一下你想要添加书签特定的搜索或你想要的链接发送给朋友他们可以单击看看同一筛选列表中的电影。请注意 HTTP POST 请求的 URL 是 GET 请求 (localhost:xxxxx/电影/SearchIndex) 的 URL 相同 — — 在 URL 本身没有搜索信息。现在,搜索字符串信息作为窗体字段值发送到服务器。这意味着您不能捕获该搜索信息以创建书签或在 URL 中发送给朋友。

解决方案是使用

BeginForm

,它指定 POST 请求应添加到 URL 的搜索信息和它应该被路由到公共版本的

SearchIndex

方法的重载。现有无参数

BeginForm

方法替换为以下内容:

@using (Html.BeginForm("SearchIndex","Movies",FormMethod.Get))      
C# MVC 自学笔记—6 编辑方法和编辑视图

现在当您提交一个搜索,该 URL 包含一个搜索的查询字符串。搜索还会去

HttpGet SearchIndex

操作方法,即使你有

HttpPost SearchIndex

的一种方法。

C# MVC 自学笔记—6 编辑方法和编辑视图

添加搜索按流派

如果您添加

HttpPost

版本的

SearchIndex

方法,它现在将其删除。

下一步,您将添加一个功能,让用户搜索电影按类型排列。

SearchIndex

方法替换为以下代码:

public ActionResult SearchIndex(string movieGenre, string searchString) 
{ 
    var GenreLst = new List<string>(); 
 
    var GenreQry = from d in db.Movies 
                   orderby d.Genre 
                   select d.Genre; 
    GenreLst.AddRange(GenreQry.Distinct()); 
    ViewBag.movieGenre = new SelectList(GenreLst); 
 
    var movies = from m in db.Movies 
                 select m; 
 
    if (!String.IsNullOrEmpty(searchString)) 
    { 
        movies = movies.Where(s => s.Title.Contains(searchString)); 
    } 
 
    if (string.IsNullOrEmpty(movieGenre)) 
        return View(movies); 
    else 
    { 
        return View(movies.Where(x => x.Genre == movieGenre)); 
    } 
 
}      

此版本的

SearchIndex

方法需要一个额外的参数,即

movieGenre

。第一次几行代码创建一个

List

对象来保存电影流派从数据库。

下面的代码是从数据库中检索所有流派的 LINQ 查询。

var GenreQry = from d in db.Movies 
                   orderby d.Genre 
                   select d.Genre;      

该代码使用泛型

List

集合的

AddRange

方法向列表中添加所有不同的流派。(不带

Distinct

修饰符,会添加重复流派 — — 例如,会在我们的示例中两次添加喜剧)。该代码然后

ViewBag

对象中存储的流派的列表。

下面的代码演示如何检查

movieGenre

参数。如果它不是空的代码进一步限制电影查询,以限制到指定的体裁选看电影。

if (string.IsNullOrEmpty(movieGenre)) 
        return View(movies); 
    else 
    { 
        return View(movies.Where(x => x.Genre == movieGenre)); 
    }      

将标记添加到 SearchIndex 视图,支持按类型排列的主题搜索

添加到Views\Movies\SearchIndex.cshtml文件中,只是之前在

TextBox

佣工

Html.DropDownList

帮助器。已完成的标记如下所示:

<p> 
    @Html.ActionLink("Create New", "Create") 
    @using (Html.BeginForm("SearchIndex","Movies",FormMethod.Get)){     
         <p>Genre: @Html.DropDownList("movieGenre", "All")   
           Title: @Html.TextBox("SearchString")   
         <input type="submit" value="Filter" /></p> 
        } 
</p>      

运行该应用程序并浏览到/Movies/SearchIndex。按流派、 按电影名称,和由这两个条件,请尝试搜索。

C# MVC 自学笔记—6 编辑方法和编辑视图

在这一节中您审查的 CRUD 操作方法和框架所生成的视图。您创建了一个搜索操作方法和视图,让用户搜索的电影标题和流派。在下一节中,你就会看看如何将一个属性添加到

Movie

模型以及如何添加一个初始值设定项,将自动创建一个测试数据库。

==============================翻译==============================

修改后的

SearchIndex

方法将如下所示:

public ActionResult SearchIndex(string id) 
{ 
    string searchString = id; 
    var movies = from m in db.Movies 
                 select m; 
 
    if (!String.IsNullOrEmpty(searchString)) 
    { 
        movies = movies.Where(s => s.Title.Contains(searchString)); 
    } 
 
    return View(movies); 
}      

这里是不是觉得很奇怪了,就只改了黄色背景的那俩行

效果如下以前用户需输入

http://..../movies/searchIndex/searchString="ghost"

现在只需要输入

http://..../movies/searchIndex/ghost

这就是MVC的约定大于配置

这东西文字说明真难组织...

能看懂明白就好,哥写不出来了...

但是,即使您添加此

HttpPost

版本的

SearchIndex

方法,有这一切如何执行的限制。想象一下你想要添加书签特定的搜索或你想要的链接发送给朋友他们可以单击看看同一筛选列表中的电影。请注意 HTTP POST 请求的 URL 是 GET 请求 (localhost:xxxxx/电影/SearchIndex) 的 URL 相同 — — 在 URL 本身没有搜索信息。现在,搜索字符串信息作为窗体字段值发送到服务器。这意味着您不能捕获该搜索信息以创建书签或在 URL 中发送给朋友。

解决方案是使用

BeginForm

,它指定 POST 请求应添加到 URL 的搜索信息和它应该被路由到公共版本的

SearchIndex

方法的重载。现有无参数

BeginForm

方法替换为以下内容:

这段大致说的是一个post方法基于get方法查询的基础上