Los controladores en una arquitectura MVC es el componente que se encarga de administrar las llamadas del usuario, utilizar el Modelo para administrar los datos y construir una respuesta para dárselo a Vista. Se podría decir que los controladores actúan como intermediarios entre el Modelo y la Vista.

En un proyecto ASP.NET MVC los controladores son clases que se encuentran en la carpeta Controllers y es obligatorio que su nombre deba terminar con la palabra Controller, como por ejemplo HomeController.

Las Acciones que realiza el usuario se administran con funciones dentro del controlador. Estas funciones devuelven un objeto ActionResult donde se obtiene con return View();

public class HomeController : Controller
{
    // GET: Home
    public ActionResult Index()
    {
        return View();
    }
}

Las acciones o llamadas que realiza el usuarios se hacen mediante la Url, donde la estructura es Dominio/Controlador/Acción; como por ejemplo al controlador Home y la acción index se accede mediante la Url localhost/Home/Index.

Además una Acción posee diferentes funciones dependiendo el método de llamada HTTP, como por ejemplo GET, POST, PUT o DELETE. El método de llamada HTTP por defecto es GET por lo que no hace falta especificar, los demás métodos se especifican por medio de atributos antes de declarar la función

// GET: Home
public ActionResult Index()
{
    return View();
}
...
[HttpPost]
public ActionResult Index()
{
    return View();
}
...
[HttpPut]
public ActionResult Index()
{
    return View();
}
...
[HttpDelete]
public ActionResult Index()
{
    return View();
}

Cabe mencionar que en el lenguaje C# no se pude declarar dos funciones en una misma clase con el mismo nombre y tipos de parámetros, deben tener diferentes tipos de parámetros para reconocer la sobrecarga. Si la función Index sin parámetros existe se debe crear la siguiente función Index con algún parámetro como por ejemplo un entero

// GET: Home
public ActionResult Index()
{
    return View();
}

[HttpPost]
public ActionResult Index(int Id)
{
    return View();
}

Puede pasar que exista una acción con dos métodos de llamada HTTP distintos y requieran el mismo tipo de parámetro. Por ejemplo en una acción Eliminar, podemos tener una función para el método GET que se utiliza para confirmar la eliminación y recibe como parámetro el Id del registro que va a eliminar. Además podemos tener una función para el método POST que realiza la eliminación y también recibe como parámetro el Id del registro que va a eliminar.

Para solucionar el problemas de las sobrecargas de funciones se puede cambiar el nombre de la función para el método POST y vincularlo a la acción Eliminar, como por ejemplo ConfirmarEliminar

public ActionResult Eliminar(int Id)
{
    ...
}

[HttpPost, ActionName("Eliminar")]
public ActionResult ConfirmarEliminar(int Id)
{
    ...
}

Si se accede a Dominio/Controlador/Eliminar mediante el método POST (por medio de un formulario), la función que se va a ejecutar es ConfirmarEliminar

Comunicación con la Vista

Cada Controlador posee una carpeta en Views con su mismo nombre para alojar las vistas de las acciones que lo requieran. Estas vistas tienen el mismo nombre que la acción a la que pertenecen para vincularlas automáticamente. En la acción del controlador con el código return View(); busca la vista con su mismo nombre. Sin embargo se puede cambiar la vista ingresando el nombre como parametro de la función View()

public ActionResult Index()
{
    return View("About");
}

Incluso se puede vincular una vista que no se encuentran en la carpeta del controlador en Views. Se debe especificar la ruta completa donde está la vista, como por ejemplo ~/Views/Common/List.cshtml, lo que nos permite tener vistas genéricas que comparten varios controladores.

public ActionResult Index()
{
    return View("~/Views/Common/List.cshtml");
}

En los controladores se puede interaccionar con el Modelo pero se lo debe pasar a la Vista si desea mostrar el resultado. Por ejemplo si se tiene un Modelo Persona como el siguiente

public partial class Persona
{
    public int PersonaId { get; set; }
    public string Nombre { get; set; }
    public string Apellido { get; set; }
    public int Documento { get; set; }
    public string Sexo { get; set; }
    public Nullable<System.DateTime> Fecha_Nacimiento { get; set; }
}

En el controlador se pasa el modelo por medio de la función View()

public ActionResult Index()
{
    var persona = new Persona()
    {
        Nombre = "Juan",
        Apellido = "Perez"
    };

    return View(persona);
}

En la vista existe una propiedad llamada Model que accede al modelo pasado desde el controlador. Como Model es una propiedad genérica se debe especificar el tipo de dato al principio de la vista con @model

@model Persona
@{
    ViewBag.Title = "Inicio";
}

<h2>@Model.Nombre @Model.Apellido</h2>

También se puede pasar a la vista una lista del Modelo

public ActionResult Index()
{
    var personas = new List<Persona>();

    personas.Add(new Persona() { Apellido = "Perez", Nombre = "Juan" });
    personas.Add(new Persona() { Apellido = "Gomez", Nombre = "Adrian" });

    return View(personas);
}

Luego en la vista la propiedad Model se declara y se trata como una lista

@model List<Persona>
@{
    ViewBag.Title = "Inicio";
}

@foreach (var persona in Model)
{
    <p>Nombre: @persona.Nombre</p>
    <p>Apellido: @persona.Apellido</p>
    <hr />
}

Por otro lado las variables y objetos que se crean en el controlador y no pertenecen al modelo no tienen interacción con la vista. Para poder pasar valores a la vista existes dos propiedades del controlador y la vista llamada ViewBag y ViewData.

La función de ViewBag y ViewData es la misma; pasar valores a la vista con solo asignarlos, pero se usan de manera distintas:

  • ViewBag: Es un tipo dinámico que puede crear propiedades al asignárselo. Por ejemplo ViewBag.Numero = 2
  • ViewData: Es un tipo diccionario que agregar claves y valores al momento de asignárselo. Por ejemplo ViewData[“Numero”] = 2
public ActionResult Index()
{
    ViewBag.Numero = 2;
    ViewBag.Fecha = DateTime.Now;

    ViewData["Numero"] = 2;
    ViewData["Fecha"] = DateTime.Now;

    return View();
}

Y en la vista se utilizan de la siguiente manera

@{
    ViewBag.Title = "Inicio";
}

@ViewBag.Numero
@(((DateTime)ViewBag.Fecha).ToShortDateString())

@ViewData["Numero"]
@(((DateTime)ViewData["Fecha"]).ToShortDateString())

También existe una propiedad llamada TempData que se utiliza para pasar valores entre acciones de los controladores. TempData al igual que ViewData es un tipo Diccionario y los valores enviados solo duran una petición. Por ejemplo en una acción que guarda en la base de datos y luego redirecciona a otra acción pero antes asigna un mensaje en TempData

[HttpPost]
public ActionResult Agregar(Persona persona)
{
    if (ModelState.IsValid)
    {
        db.Persona.Add(persona);
        db.SaveChanges();
        TempData["Mensaje"] = "Persona guardada correctamente";
        return RedirectToAction("Index");
    }    
    return View(persona);
}

RedirectToAction(“Index”); redirecciona a otra acción sin pasar por la vista. Luego en la acción Index se puede capturar el mensaje guardado en TempData

public ActionResult Index()
{
    var personas = db.Persona.ToList();
    var mensaje = TempData["Mensaje"];
    ....
    return View(personas);
}

También es una propiedad que puede usarse en la vista como ViewBag y ViewData. Esta propiedad solo dura una petición para que se mantenga hacia otra petición se puede usar la función

public ActionResult Index()
{
    var personas = db.Persona.ToList();
    TempData.Keep("Mensaje");
    return View(venta.ToList());
}

Keep() mantiene el valor de TempData una petición más. Se debe usar Keep() en todas las peticiones que se requiere mantener