O framework ASP.NET MVC atribui ao desenvolvedor a responsabilidade de manter o estado da página, seguindo as mesmas restrições do protocolo HTTP que é stateless. E persistir o estado da página no ASP.NET MVC é o primeiro obstáculo que o programador deve superar ao utilizar essa tecnologia.

Nesse artigo vamos análisar um trecho de código do MVC Workflow Demos disponível no Codeplex.

Vamos a ele:

public class ArticlesController : Controller
{
    private DataContext db = new DataContext();

    //
    // GET: /Articles/Edit/5
    public ActionResult Edit(int id = 0)
    {
        Article article = db.Articles.Find(id);
        if (article == null)
        {
            return HttpNotFound();
        }
        return View(article);
    }

    //
    // POST: /Articles/Edit/5
    [HttpPost]
    public ActionResult Edit(Article article)
    {
        if (ModelState.IsValid)
        {
            db.Entry(article).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(article);
    }

    ...
}

Nesse Classe (Controller) temos dois métodos (Actions) que tratam da tela de edição de artigos. A primeira Action é responsável por receber requisições GET.

Esta primeira Action será chamada quando o navegador enviar uma requisição para o endereço /Articles/Edit/42 onde 42 pode ser qualquer valor inteiro.

O método em questão será executado sendo que o argumento id irá assumir o valor 42.

Nesse método específico temos uma váriável do tipo Context que é responsável por preencher uma classe Article com o conteúdo do registro no banco de dados.

A parte mais interessante nesse método é o retorno. Em especial a seguinte linha:

return View(article);

Nessa linha o controlador indica que a View Edit (quando o nome da View não é passado, subentende que a View a ser chamada tem o nome da Action). Veja que ele passou o parâmetro article, este será o modelo enviado para a View.

@model MVCWorkflow.Models.Article

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

    <fieldset>
        <legend>Article</legend>

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

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

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

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

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

Esta View é fortemente tipada. Como sabemos disso? Pela instrução @model MVCWorkflow.Models.Article no cabeçalho da View.

Nesse caso, esta view apenas será renderizada se o modelo passado pra ela for desse tipo específico. Com a View considerando essa restrição, ela passa a utilizar propriedades do modelo dentro do seu código. Como podemos ver na linha:

@Html.EditorFor(model => model.Title)

Esta linha, gera um código HTML semelhante a esse:

<input class="text-box single-line" id="Title" name="Title" type="text" value="Coisa" />

O HTML Helper ActionLink utilzou dos atributos da propriedade Title para gerar esse HTML. A parte mais valiosa nesse HTML é o name. Este name é usado para fazer a serialização/deserialização do objeto.

Vamos dar uma olhada mais ampla no formulário gerado:

image

E no HTML:

<form action="/Articles/Edit/2" method="post">
    <fieldset>
        <legend>Article</legend>

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

        <input id="State" name="State" type="hidden" value="Retained" />

        <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="Ivan Paulovich" />
            <span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
</form>
<div>
    <a href="/Articles">Back to List</a>
</div>

Nesse HTML temos alguns códigos relevantes. Vejamos a tag form, ela tem o atributo action definido como Article/Edit/2 e está definido como protocolo post. Isto indica que ao submeter este formulário através do botão.

Os campos do tipo input dessa página serão enviados para aquela URL. Nessa aplicação temos a nossa Action esperando por requisições que se encaixam nesse critério. Veja

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

Esta action está aguardando um objeto article como argumento. O framework ASP.NET MVC recebe os campos enviados pelo formuláro e deserializa criando um objeto do tipo Article.

Este método tem mais pontos interessantes que valem comentar. Temos aqui a propriedade ModelState.IsValid, está variável é atualizada em true ou false caso os valores do modelo atendam as restrições de validação. Você pode ver mais detalhes sobre Data Annotations nesse outro artigo.

Nesse método temos duas possibilidades de retorno. Se o ModelState estiver válido e a operação de atualização em banco for executada temos um redirecionamento através do RedirectToAction para a página Index. Essa operação, envolve um retorno para o navegador com uma instrução de redirecionamento para uma outra página.

Temos também o caso onde o ModelState está inválido e retornamos para a mesma View passando o article como argumento, voltando lá atrás na situação anterior.

Nesse caso específico a View irá renderizar também as mensagens indicando que o objeto está inválido:

image

 

Vamos tirara dúvidas nos comentários.