Examinar los métodos y la vista Edit (VB)

por Rick Anderson

Este tutorial le enseñará los aspectos básicos de la creación de una aplicación web MVC de ASP.NET con Microsoft Visual Web Developer 2010 Express Service Pack 1, que es una versión gratuita de Microsoft Visual Studio. Antes de empezar, asegúrese de que ha instalado los requisitos previos que se enumeran a continuación. Para instalar todos ellos, haga clic en el vínculo siguiente: instalador de plataforma web. Como alternativa, puede instalar individualmente los requisitos previos con los siguientes vínculos:

Si utiliza Visual Studio 2010 en lugar de Visual Web Developer 2010, instale los requisitos previos haciendo clic en el siguiente vínculo: requisitos previos de Visual Studio 2010.

Un proyecto de Visual Web Developer con código fuente de VB.NET está disponible para acompañar este tema. Descargue la versión de VB.net. Si prefiere C#, cambie a la versión de c# de este tutorial.

En esta sección, examinará las vistas y los métodos de acción generados para el controlador de película. A continuación, agregará una página de búsqueda personalizada.

Ejecute la aplicación y vaya al Movies controlador anexando /Movies a la dirección URL en la barra de direcciones del explorador. Mantenga el puntero del mouse sobre un vínculo de edición para ver la dirección URL a la que está vinculado.

EditLink_sm

El Edit método genera el vínculo de edición Html.ActionLink en la vista Views\Movies\Index.vbhtml :

@Html.ActionLink("Edit", "Edit", New With {.id = currentItem.ID}) |

EditLink_sm

El Html objeto es una aplicación auxiliar que se expone mediante una propiedad en la WebViewPage clase base. El ActionLink método del ayudante facilita la generación dinámica de hipervínculos HTML que se vinculan a los métodos de acción en los controladores. El primer argumento del ActionLink método es el texto del vínculo que se va a representar (por ejemplo, <a>Edit Me</a> ). El segundo argumento es el nombre del método de acción que se va a invocar. El último argumento es un objeto anónimo que genera los datos de la ruta (en este caso, el identificador 4).

El vínculo generado que se muestra en la imagen anterior es http://localhost:xxxxx/Movies/Edit/4 . La ruta predeterminada toma el modelo de dirección URL {controller}/{action}/{id} . Por lo tanto, ASP.NET http://localhost:xxxxx/Movies/Edit/4 se traduce en una solicitud al Edit método de acción del Movies controlador con el parámetro ID igual a 4.

También puede pasar parámetros de método de acción mediante una cadena de consulta. Por ejemplo, la dirección URL http://localhost:xxxxx/Movies/Edit?ID=4 también pasa el parámetro ID de 4 al Edit método de acción del Movies controlador.

EditQueryString

Abra el Movies controlador. EditA continuación se muestran los dos métodos de acción.

'
' GET: /Movies/Edit/5

Function Edit(id As Integer) As ViewResult
    Dim movie As Movie = db.Movies.Find(id)
    Return View(movie)
End Function

'
' POST: /Movies/Edit/5

<HttpPost()>
Function Edit(movie As Movie) As ActionResult
    If ModelState.IsValid Then
        db.Entry(movie).State = EntityState.Modified
        db.SaveChanges()
        Return RedirectToAction("Index")
    End If

    Return View(movie)
End Function

Observe que el segundo método de acción Edit va precedido del atributo HttpPost. Este atributo especifica que Edit solo se puede invocar la sobrecarga del método para las solicitudes post. Podría aplicar el HttpGet atributo al primer método de edición, pero no es necesario porque es el valor predeterminado. (Haremos referencia a los métodos de acción a los que se asignan implícitamente el HttpGet atributo como HttpGet métodos).

El HttpGet Edit método toma el parámetro de ID. de película, busca la película con el Find método Entity Framework y devuelve la película seleccionada a la vista de edición. Cuando el sistema de scaffolding creó la vista de edición, examinó la clase Movie y creó código para representar los elementos <label> y <input> para cada propiedad de la clase. En el ejemplo siguiente se muestra la vista de edición que se generó:

@ModelType MvcMovie.Movie

@Code
    ViewData("Title") = "Edit"
End Code

<h2>Edit</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@Using Html.BeginForm()
    @Html.ValidationSummary(True)
    @<fieldset>
        <legend>Movie</legend>

        @Html.HiddenFor(Function(model) model.ID)

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

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

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

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

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

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

Observe cómo la plantilla de vista tiene una @ModelType MvcMovie.Models.Movie instrucción en la parte superior del archivo; esto especifica que la vista espera que el modelo de la plantilla de vista sea de tipo Movie .

El código con scaffolding usa varios métodos auxiliares para optimizar el marcado HTML. El Html.LabelFor ayudante muestra el nombre del campo ( " title " , " ReleaseDate " , " Genre " o " Price " ). El Html.EditorFor ayudante muestra un elemento HTML <input> . El Html.ValidationMessageFor ayudante muestra cualquier mensaje de validación asociado a esa propiedad.

Ejecute la aplicación y vaya a la dirección URL de /Movies . Haga clic en un vínculo Edit (Editar). En el explorador, vea el código fuente de la página. El código HTML de la página es similar al del ejemplo siguiente. (El marcado de menú se excluyó para mayor claridad).

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Edit</title>
    <link href="/Content/Site.css" rel="stylesheet" type="text/css" />
    <script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
    <script src="/Scripts/modernizr-1.7.min.js" type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <header>
            <div id="title">
                <h1>MVC Movie App</h1>
            </div>
           ...
        </header>
        <section id="main">

<h2>Edit</h2>

<script src="/Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>

<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-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="9.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>
<div>
    <a href="/Movies">Back to List</a>
</div>

        </section>
        <footer>
        </footer>
    </div>
</body>
</html>

Los <input> elementos están en un <form> elemento HTML cuyo action atributo está establecido en post en la dirección URL de /Movies/Edit . Los datos del formulario se publicarán en el servidor cuando se haga clic en el botón Editar .

Procesamiento de la solicitud POST

En la siguiente lista se muestra la versión HttpPost del método de acción Edit.

'
' POST: /Movies/Edit/5

<HttpPost()>
Function Edit(movie As Movie) As ActionResult
    If ModelState.IsValid Then
        db.Entry(movie).State = EntityState.Modified
        db.SaveChanges()
        Return RedirectToAction("Index")
    End If

    Return View(movie)
End Function

El enlazador de modelos de ASP.NET Framework toma los valores del formulario expuesto y crea un Movie objeto que se pasa como movie parámetro. La ModelState.IsValid protección del código comprueba que los datos enviados en el formulario se pueden usar para modificar un Movie objeto. Si los datos son válidos, el código guarda los datos de la película en la Movies colección de la MovieDBContext instancia. Después, el código guarda los nuevos datos de la película en la base de datos llamando al SaveChanges método de MovieDBContext , que conserva los cambios en la base de datos. Después de guardar los datos, el código redirige al usuario al Index método de acción de la MoviesController clase, lo que hace que la película actualizada se muestre en la lista de películas.

Si los valores expuestos no son válidos, se vuelven a mostrar en el formulario. Las Html.ValidationMessageFor aplicaciones auxiliares de la plantilla de vista Edit. vbhtml se encargan de mostrar los mensajes de error correspondientes.

abcNotValid

Nota acerca de las configuraciones regionales Si trabaja normalmente con una configuración regional que no sea inglés, consulte compatibilidad con la validación de ASP.NET MVC 3 con configuraciones regionales distintas del inglés.

Aumentar la solidez del método de edición

El HttpGet Edit método generado por el sistema de scaffolding no comprueba que el identificador que se le pasa es válido. Si un usuario quita el segmento de identificador de la dirección URL ( http://localhost:xxxxx/Movies/Edit ), se muestra el siguiente error:

Null_ID

Un usuario también podría pasar un identificador que no existe en la base de datos, como http://localhost:xxxxx/Movies/Edit/1234 . Puede realizar dos cambios en el HttpGet Edit método de acción para abordar esta limitación. En primer lugar, cambie el ID parámetro para que tenga un valor predeterminado de cero cuando no se pase explícitamente un identificador. También puede comprobar que el Find método realmente encontró una película antes de devolver el objeto de película a la plantilla de vista. EditA continuación se muestra el método actualizado.

Public Function Edit(Optional ByVal id As Integer = 0) As ActionResult
    Dim movie As Movie = db.Movies.Find(id)
    If movie Is Nothing Then
        Return HttpNotFound()
    End If
    Return View(movie)
End Function

Si no se encuentra ninguna película, HttpNotFound se llama al método.

Todos los HttpGet métodos siguen un patrón similar. Obtienen un objeto de película (o una lista de objetos, en el caso de Index ) y pasan el modelo a la vista. El Create método pasa un objeto de película vacío a la vista de creación. Todos los métodos que crean, editan, eliminan o modifican los datos lo hacen en la sobrecarga HttpPost del método. La modificación de los datos en un método HTTP GET es un riesgo para la seguridad, como se describe en la entrada de blog de ASP.NET MVC Tip #46 – no usar los vínculos de eliminación porque crean carencias de seguridad. La modificación de los datos en un método GET también infringe los procedimientos recomendados de HTTP y el patrón REST de la arquitectura, que especifica que las solicitudes GET no deben cambiar el estado de la aplicación. En otras palabras, realizar una operación GET debe ser una operación segura sin efectos secundarios.

Agregar un método de búsqueda y una vista de búsqueda

En esta sección, agregará un SearchIndex método de acción que le permitirá buscar películas por género o nombre. Estará disponible mediante la dirección URL de /Movies/SearchIndex . La solicitud mostrará un formulario HTML que contiene los elementos de entrada que un usuario puede rellenar para buscar una película. Cuando un usuario envía el formulario, el método de acción obtendrá los valores de búsqueda publicados por el usuario y usará los valores para buscar en la base de datos.

SearchIndx_SM

Mostrar el formulario SearchIndex

Comience agregando un SearchIndex método de acción a la MoviesController clase existente. El método devolverá una vista que contenga un formulario HTML. Este es el código:

Public Function SearchIndex(ByVal searchString As String) As ActionResult
    Dim movies = From m In db.Movies
                 Select m 

    If Not String.IsNullOrEmpty(searchString) Then 
        movies = movies.Where(Function(s) s.Title.Contains(searchString)) 
    End If
    Return View(movies) 
End Function

La primera línea del SearchIndex método crea la siguiente consulta LINQ para seleccionar las películas:

Dim movies = From m In db.Movies    Select m

La consulta se define en este punto, pero aún no se ha ejecutado en el almacén de datos.

Si el searchString parámetro contiene una cadena, la consulta de películas se modifica para filtrar según el valor de la cadena de búsqueda, mediante el código siguiente:

Si no es String. IsNullOrEmpty (searchString)
películas = películas. Where (Functions s. title. Contains (searchString))
Finalizar si

Las consultas LINQ no se ejecutan cuando se definen o cuando se modifican llamando a un método como Where o OrderBy . En su lugar, se aplaza la ejecución de la consulta, lo que significa que la evaluación de una expresión se retrasa hasta que su valor realizado se recorra en iteración o ToList se llame al método. En el SearchIndex ejemplo, la consulta se ejecuta en la vista SearchIndex. Para más información sobre la ejecución de consultas en diferido, vea Ejecución de la consulta.

Ahora puede implementar la SearchIndex vista que mostrará el formulario al usuario. Haga clic con el botón secundario en el SearchIndex método y, a continuación, haga clic en Agregar vista. En el cuadro de diálogo Agregar vista , especifique que va a pasar un Movie objeto a la plantilla de vista como su clase de modelo. En la lista de plantillas scaffolding , elija listay, a continuación, haga clic en Agregar.

AddSearchView

Al hacer clic en el botón Agregar , se crea la plantilla de vista Views\Movies\SearchIndex.vbhtml . Dado que seleccionó lista en la lista de plantillas scaffolding , Visual Web Developer generará automáticamente (scaffolding) algún contenido predeterminado en la vista. El scaffolding creó un formulario HTML. Examinó la Movie clase y creó código para representar <label> elementos para cada propiedad de la clase. En la siguiente lista se muestra la vista de creación que se generó:

@ModelType IEnumerable(Of MvcMovie.Movie)

@Code
    ViewData("Title") = "SearchIndex"
End Code

<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>

@For Each item In Model
    Dim currentItem = item
    @<tr>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.Title)
        </td>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.ReleaseDate)
        </td>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.Genre)
        </td>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.Price)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", New With {.id = currentItem.ID}) |
            @Html.ActionLink("Details", "Details", New With {.id = currentItem.ID}) |
            @Html.ActionLink("Delete", "Delete", New With {.id = currentItem.ID})
        </td>
    </tr>
Next

</table>

Ejecute la aplicación y vaya a /Movies/SearchIndex. Anexe una cadena de consulta como ?searchString=ghost a la dirección URL. Se muestran las películas filtradas.

SearchQryStr

Si cambia la firma del SearchIndex método para que tenga un parámetro denominado id , el id parámetro coincidirá con el {id} marcador de posición de las rutas predeterminadas establecidas en el archivo global. asax .

{controller}/{action}/{id}

El SearchIndex método modificado tendría el siguiente aspecto:

Public Function SearchIndex(ByVal id As String) As ActionResult
Dim searchString As String = id
Dim movies = From m In db.Movies
             Select m

If Not String.IsNullOrEmpty(searchString) Then
    movies = movies.Where(Function(s) s.Title.Contains(searchString))
End If

Return View(movies)
End Function

Ahora puede pasar el título de la búsqueda como datos de ruta (un segmento de dirección URL) en lugar de como un valor de cadena de consulta.

SearchRouteData

Sin embargo, no se puede esperar que los usuarios modifiquen la dirección URL cada vez que quieran buscar una película. Ahora va a agregar una interfaz de usuario que le ayude a filtrar las películas. Si ha cambiado la firma del SearchIndex método para probar cómo pasar el parámetro de identificador enlazado a ruta, cámbielo de nuevo para que el SearchIndex método tome un parámetro de cadena denominado searchString :

Abra el archivo Views\Movies\SearchIndex.vbhtml y, justo después @Html.ActionLink("Create New", "Create") , agregue lo siguiente:

@Code
    ViewData("Title") = "SearchIndex"
    Using (Html.BeginForm())
         @<p> Title: @Html.TextBox("SearchString") 
         <input type="submit" value="Filter" /></p>
        End Using
End Code

El Html.BeginForm ayudante crea una etiqueta de apertura <form> . El Html.BeginForm ayudante hace que el formulario se publique en sí mismo cuando el usuario envía el formulario haciendo clic en el botón filtro .

Ejecute la aplicación e intente buscar una película.

SearchIndxIE9_title

No hay ninguna HttpPost sobrecarga del SearchIndex método. No es necesario porque el método no cambia el estado de la aplicación, simplemente filtrando los datos. Si agregó el método siguiente HttpPost SearchIndex , el invocador de acción coincidiría con el HttpPost SearchIndex método y el HttpPost SearchIndex método se ejecutaría tal y como se muestra en la imagen siguiente.

<HttpPost()>
 Public Function SearchIndex(ByVal fc As FormCollection, ByVal searchString As String) As String
     Return "<h3> From [HttpPost]SearchIndex: " & searchString & "</h3>"
 End Function

SearchPostGhost

Agregar búsqueda por género

Si agregó la HttpPost versión del SearchIndex método, elimínela ahora.

A continuación, agregará una característica para permitir que los usuarios busquen películas por género. Reemplace el método SearchIndex por el código siguiente:

Public Function SearchIndex(ByVal movieGenre As String, ByVal searchString As String) As ActionResult
    Dim GenreLst = New List(Of String)()

    Dim GenreQry = From d In db.Movies
                   Order By d.Genre
                   Select d.Genre
    GenreLst.AddRange(GenreQry.Distinct())
    ViewBag.movieGenre = New SelectList(GenreLst)

    Dim movies = From m In db.Movies
                 Select m

    If Not String.IsNullOrEmpty(searchString) Then
        movies = movies.Where(Function(s) s.Title.Contains(searchString))
    End If

    If String.IsNullOrEmpty(movieGenre) Then
        Return View(movies)
    Else
        Return View(movies.Where(Function(x) x.Genre = movieGenre))
    End If

End Function

Esta versión del SearchIndex método toma un parámetro adicional, es decir, movieGenre . Las primeras líneas de código crean un List objeto para contener géneros de película de la base de datos.

El código siguiente es una consulta LINQ que recupera todos los géneros de la base de datos.

Dim GenreQry = From d In db.Movies
                   Order By d.Genre
                   Select d.Genre

El código usa el AddRange método de la List colección genérica para agregar todos los géneros distintos a la lista. (Sin el Distinct modificador, se agregarán géneros duplicados; por ejemplo, comedia se agregaría dos veces en nuestro ejemplo). A continuación, el código almacena la lista de géneros en el ViewBag objeto.

En el código siguiente se muestra cómo comprobar el movieGenre parámetro. Si no está vacío, el código restringe aún más la consulta de películas para limitar las películas seleccionadas al género especificado.

If String.IsNullOrEmpty(movieGenre) Then
        Return View(movies)
    Else
        Return View(movies.Where(Function(x) x.Genre = movieGenre))
    End If

Agregar marcado a la vista SearchIndex para admitir la búsqueda por género

Agregue una Html.DropDownList aplicación auxiliar al archivo Views\Movies\SearchIndex.vbhtml , justo antes de la TextBox aplicación auxiliar. A continuación se muestra el marcado completado:

<p>
    @Html.ActionLink("Create New", "Create")
    @Code
    ViewData("Title") = "SearchIndex"
    Using (Html.BeginForm())
         @<p> Genre: @Html.DropDownList("movieGenre", "All")
         Title: @Html.TextBox("SearchString") 
         <input type="submit" value="Filter" /></p>
        End Using
End Code
</p>

Ejecute la aplicación y vaya a /Movies/SearchIndex. Pruebe una búsqueda por género, por nombre de película y por ambos criterios.

En esta sección se han examinado los métodos de acción CRUD y las vistas generadas por el marco. Ha creado un método de acción de búsqueda y una vista que permiten a los usuarios buscar por título y género de la película. En la siguiente sección, veremos cómo agregar una propiedad al Movie modelo y cómo agregar un inicializador que creará automáticamente una base de datos de prueba.