asp.net-mvc c# - ¿Qué es ViewModel en MVC?





@model razor (10)


MVC no tiene un modelo de visualización: tiene un modelo, una vista y un controlador. Un modelo de vista es parte de MVVM (Modelo-Vista-Modelo de vista). MVVM se deriva del Modelo de presentación y se populariza en WPF. También debería haber un modelo en MVVM, pero la mayoría de las personas se saltan el punto de ese patrón por completo y solo tendrán una vista y un modelo de vista. El modelo en MVC es similar al modelo en MVVM.

En MVC el proceso se divide en 3 responsabilidades diferentes:

  • View es responsable de presentar los datos al usuario.
  • Un controlador es responsable del flujo de la página.
  • Un modelo es responsable de la lógica empresarial.

MVC no es muy adecuado para aplicaciones web. Es un patrón introducido por Smalltalk para crear aplicaciones de escritorio. Un entorno web se comporta completamente diferente. No tiene mucho sentido copiar un concepto de 40 años del desarrollo de escritorio y pegarlo en un entorno web. Sin embargo, mucha gente piensa que esto está bien, porque su aplicación compila y devuelve los valores correctos. Es decir, en mi opinión, no es suficiente para declarar que una determinada opción de diseño está bien.

Un ejemplo de un modelo en una aplicación web podría ser:

public class LoginModel
{
    private readonly AuthenticationService authentication;

    public LoginModel(AuthenticationService authentication)
    {
        this.authentication = authentication;
    }

    public bool Login()
    {
        return authentication.Login(Username, Password);
    }

    public string Username { get; set; }
    public string Password { get; set; }
}

El controlador puede usarlo así:

public class LoginController
{
    [HttpPost]
    public ActionResult Login(LoginModel model)
    {
        bool success = model.Login();

        if (success)
        {
            return new RedirectResult("/dashboard");
        }
        else
        {
            TempData["message"] = "Invalid username and/or password";
            return new RedirectResult("/login");
        }
    }
}

Sus métodos de control y sus modelos serán pequeños, fáciles de probar y al punto.

Soy nuevo en ASP.NET MVC. Tengo un problema para entender el propósito de un ViewModel.

¿Qué es un ViewModel y por qué necesitamos un ViewModel para una aplicación MVC de ASP.NET?

Es mejor si puedo tener un ejemplo simple.




Edit: actualicé esta respuesta en mi blog:

http://www.samwheat.com/Post/The-function-of-ViewModels-in-MVC-web-development

Mi respuesta es un poco larga, pero creo que es importante comparar los modelos de vista con otros tipos de modelos comúnmente usados ​​para entender por qué son diferentes y por qué son necesarios.

Para resumir, y para responder directamente a la pregunta que se hace:

En términos generales, un modelo de vista es un objeto que contiene todas las propiedades y métodos necesarios para representar una vista. Las propiedades del modelo de visualización a menudo están relacionadas con objetos de datos como clientes y pedidos y, además, también contienen propiedades relacionadas con la página o la aplicación en sí, como el nombre de usuario, el nombre de la aplicación, etc. Los modelos de vista proporcionan un objeto conveniente para pasar a un motor de renderizado a crear una página html. Una de las muchas razones para usar un modelo de vista es que los modelos de vista proporcionan una manera de probar de forma unitaria ciertas tareas de presentación, como el manejo de la entrada del usuario, la validación de datos, la recuperación de datos para su visualización, etc.

Aquí hay una comparación de los modelos de entidad (a.ka. DTO's a.ka. models), Presentation Models y View Models.

Objetos de transferencia de datos alias "modelo"

Un objeto de transferencia de datos (DTO) es una clase con propiedades que coinciden con un esquema de tabla en una base de datos. Los DTO reciben su nombre por su uso común para transportar datos hacia y desde un almacén de datos.
Características de los DTO's:

• Son objetos de negocio: su definición depende de los datos de la aplicación.

• Por lo general, contienen propiedades solamente, sin código.

• Se utiliza principalmente para transportar datos hacia y desde una base de datos.

• Las propiedades coinciden exacta o exactamente con los campos de una tabla específica en un almacén de datos.

Las tablas de la base de datos generalmente están normalizadas, por lo tanto, las DTO también se normalizan normalmente. Esto los hace de uso limitado para presentar datos. Sin embargo, para ciertas estructuras de datos simples, a menudo funcionan bastante bien.

Aquí hay dos ejemplos de cómo se verían los DTO:

public class Customer
{
    public int ID { get; set; }
    public string CustomerName { get; set; }
}


public class Order
{
    public int ID { get; set; }
    public int CustomerID { get; set; }
    public DateTime OrderDate { get; set; }
    public Decimal OrderAmount { get; set; }
}

Modelos de presentacion

Un modelo de presentación es una clase de utilidad que se utiliza para representar datos en una pantalla o informe. Los modelos de presentación se utilizan normalmente para modelar estructuras de datos complejas que se componen de datos de múltiples DTO. Los modelos de presentación a menudo representan una vista desnormalizada de los datos.

Características de los modelos de presentación:

• Son objetos de negocio: su definición depende de los datos de la aplicación.

• Contiene principalmente propiedades. El código normalmente se limita al formato de datos o la conversión ao desde un DTO. Los modelos de presentación no deben contener lógica de negocios.

• A menudo presentan una vista desnormalizada de los datos. Es decir, a menudo combinan propiedades de múltiples DTO.

• A menudo contienen propiedades de un tipo base diferente al de un DTO. Por ejemplo, los montos en dólares se pueden representar como cadenas para que puedan contener comas y un símbolo de moneda.

• A menudo se define por la forma en que se utilizan, así como sus características de objeto. En otras palabras, un DTO simple que se utiliza como modelo de respaldo para representar una cuadrícula es, de hecho, también un modelo de presentación en el contexto de esa cuadrícula.

Los modelos de presentación se utilizan "según sea necesario" y "cuando sea necesario" (mientras que los DTO generalmente están vinculados al esquema de la base de datos). Un modelo de presentación puede usarse para modelar datos para una página completa, una cuadrícula en una página o un menú desplegable en una cuadrícula en una página. Los modelos de presentación a menudo contienen propiedades que son otros modelos de presentación. Los modelos de presentación a menudo se construyen para un solo uso, como para representar una cuadrícula específica en una sola página.

Un ejemplo de modelo de presentación:

public class PresentationOrder
{
    public int OrderID { get; set; }
    public DateTime OrderDate { get; set; }
    public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
    public string CustomerName { get; set; }
    public Decimal OrderAmount { get; set; }
    public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}

Ver modelos

Un modelo de vista es similar a un modelo de presentación, ya que es una clase de respaldo para representar una vista. Sin embargo, es muy diferente de un Modelo de presentación o un DTO en cómo se construye. Los modelos de visualización a menudo contienen las mismas propiedades que los modelos de presentación y DTO y, por esta razón, a menudo se confunden uno con el otro.

Características de los modelos de vista:

• Son la única fuente de datos utilizada para representar una página o pantalla. Por lo general, esto significa que un modelo de vista expondrá todas las propiedades que cualquier control de la página deberá representar correctamente. Hacer que el modelo de vista sea la única fuente de datos para la vista mejora en gran medida su capacidad y valor para las pruebas unitarias.

• Son objetos compuestos que contienen propiedades que consisten en datos de la aplicación, así como propiedades que utiliza el código de la aplicación. Esta característica es crucial al diseñar el modelo de vista para la reutilización y se describe en los ejemplos a continuación.

• Contiene el código de la aplicación. Los modelos de vista generalmente contienen métodos que se llaman durante la representación y cuando el usuario está interactuando con la página. Este código generalmente se relaciona con el manejo de eventos, la animación, la visibilidad de los controles, el estilo, etc.

• Contiene un código que llama a los servicios empresariales con el fin de recuperar datos o enviarlos a un servidor de base de datos. Este código a menudo se coloca erróneamente en un controlador. La llamada a servicios comerciales desde un controlador generalmente limita la utilidad del modelo de vista para la prueba unitaria. Para ser claros, los modelos de vista en sí mismos no deben contener lógica de negocios, sino que deben hacer llamadas a servicios que sí contienen lógica de negocios.

• A menudo contienen propiedades que son otros modelos de vista para otras páginas o pantallas.

• Se escriben “por página” o “por pantalla”. Normalmente, se escribe un modelo de vista único para cada página o pantalla en una aplicación.

• Generalmente se derivan de una clase base ya que la mayoría de las páginas y pantallas comparten propiedades comunes.

Ver la composición del modelo

Como se indicó anteriormente, los modelos de vista son objetos compuestos porque combinan las propiedades de la aplicación y las propiedades de los datos de negocios en un solo objeto. Algunos ejemplos de propiedades de aplicaciones que se usan comúnmente en los modelos de vista son:

• Propiedades que se utilizan para mostrar el estado de la aplicación, como mensajes de error, nombre de usuario, estado, etc.

• Propiedades utilizadas para formatear, mostrar, estilizar o animar controles.

• Las propiedades utilizadas para el enlace de datos, como los objetos de lista y las propiedades que contienen datos intermedios que ingresa el usuario.

Los siguientes ejemplos muestran por qué la naturaleza compuesta de los modelos de vista es importante y la mejor manera de construir un modelo de vista que sea eficiente y reutilizable.

Supongamos que estamos escribiendo una aplicación web. Uno de los requisitos del diseño de la aplicación es que el título de la página, el nombre de usuario y el nombre de la aplicación deben mostrarse en cada página. Si queremos crear una página para mostrar un objeto de orden de presentación, podemos modificar el modelo de presentación de la siguiente manera:

public class PresentationOrder
{
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }
    public int OrderID { get; set; }
    public DateTime OrderDate { get; set; }
    public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
    public string CustomerName { get; set; }
    public Decimal OrderAmount { get; set; }
    public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}

Este diseño podría funcionar ... pero, ¿y si queremos crear una página que muestre una lista de pedidos? Las propiedades PageTitle, UserName y ApplicationName se repetirán y se volverán difíciles de manejar. Además, ¿qué pasa si queremos definir alguna lógica de nivel de página en el constructor de la clase? Ya no podemos hacerlo si creamos una instancia para cada pedido que se mostrará.

Composición sobre herencia

Esta es una forma en la que podríamos redefinir el modelo de presentación del pedido para que se convierta en un verdadero modelo de vista y sea útil para mostrar un solo objeto PresentationOrder o una colección de objetos PresentationOrder:

public class PresentationOrderVM
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business properties
    public PresentationOrder Order { get; set; }
}


public class PresentationOrderVM
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business properties
    public List<PresentationOrder> Orders { get; set; }
}

Si observamos las dos clases anteriores, podemos ver que una forma de pensar sobre un modelo de vista es que es un modelo de presentación que contiene otro modelo de presentación como propiedad. El modelo de presentación de nivel superior (es decir, el modelo de vista) contiene propiedades que son relevantes para la página o la aplicación, mientras que el modelo de presentación (propiedad) contiene propiedades que son relevantes para los datos de la aplicación.

Podemos llevar nuestro diseño un paso más allá y crear una clase de modelo de vista base que puede usarse no solo para PresentationOrders, sino también para cualquier otra clase:

public class BaseViewModel
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }
}

Ahora podemos simplificar nuestro PresentationOrderVM de esta manera:

public class PresentationOrderVM : BaseViewModel
{
    // Business properties
    public PresentationOrder Order { get; set; }
}

public class PresentationOrderVM : BaseViewModel
{
    // Business properties
    public List<PresentationOrder> Orders { get; set; }
}

Podemos hacer que nuestro BaseViewModel sea aún más reutilizable al hacerlo genérico:

public class BaseViewModel<T>
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business property
    public T BusinessObject { get; set; }
}

Ahora nuestras implementaciones son sin esfuerzo:

public class PresentationOrderVM : BaseViewModel<PresentationOrder>
{
    // done!
}

public class PresentationOrderVM : BaseViewModel<List<PresentationOrder>>
{
    // done!
}



Un modelo de vista es un modelo conceptual de datos. Su uso es, por ejemplo, obtener un subconjunto o combinar datos de diferentes tablas.

Es posible que solo desee propiedades específicas, por lo que esto le permite cargar solo esas propiedades y no propiedades adicionales innecesarias




  • ViewModel contiene campos que están representados en la vista (para los ayudantes de LabelFor, EditorFor, DisplayFor)
  • ViewModel puede tener reglas de validación específicas utilizando anotaciones de datos o IDataErrorInfo.
  • ViewModel puede tener múltiples entidades u objetos de diferentes modelos de datos o fuentes de datos.

Diseñando ViewModel

public class UserLoginViewModel 
{ 
[Required(ErrorMessage = "Please enter your username")] 
[Display(Name = "User Name")]
[MaxLength(50)]
public string UserName { get; set; }
 [Required(ErrorMessage = "Please enter your password")]
 [Display(Name = "Password")]
 [MaxLength(50)]
 public string Password { get; set; } 
} 

Presentando el modelo de vista en la vista.

@model MyModels.UserLoginViewModel 
@{
 ViewBag.Title = "User Login";
 Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm())
{
<div class="editor-label">
 @Html.LabelFor(m => m.UserName)
</div>
<div class="editor-field">
 @Html.TextBoxFor(m => m.UserName)
 @Html.ValidationMessageFor(m => m.UserName)
</div>
<div class="editor-label">
 @Html.LabelFor(m => m.Password)
</div>
<div class="editor-field">
 @Html.PasswordFor(m => m.Password)
 @Html.ValidationMessageFor(m => m.Password)
</div>
<p>
 <input type="submit" value="Log In" />
</p>
</div>
}

Trabajando con Acción

public ActionResult Login()
{ 
return View();
}
[HttpPost]
public ActionResult Login(UserLoginViewModel user)
{
// To acces data using LINQ
DataClassesDataContext mobjentity = new DataClassesDataContext();
 if (ModelState.IsValid) 
{ 
try
 {
 var q = mobjentity.tblUsers.Where(m => m.UserName == user.UserName && m.Password == user.Password).ToList(); 
 if (q.Count > 0) 
 { 
 return RedirectToAction("MyAccount");
 }
 else
 {
 ModelState.AddModelError("", "The user name or password provided is incorrect.");
 }
 }
 catch (Exception ex)
 {
 } 
 } 
 return View(user);
} 
  1. En ViewModel, coloque solo los campos / datos que desea mostrar en la vista / página.
  2. Dado que view representa las propiedades de ViewModel, por lo tanto, es fácil de renderizar y mantener.
  3. Use un mapeador cuando ViewModel se vuelva más complejo.



Un view model representa los datos que desea mostrar en su vista / página, ya sea que se use para texto estático o para valores de entrada (como cuadros de texto y listas desplegables) que se pueden agregar a la base de datos (o editar). Es algo diferente a tu domain model . Es un modelo para la vista.

Digamos que tiene una clase de Employee que representa su modelo de dominio de empleado y que contiene las siguientes propiedades (identificador único, nombre, apellido y fecha de creación):

public class Employee : IEntity
{
     public int Id { get; set; }

     public string FirstName { get; set; }

     public string LastName { get; set; }

     public DateTime DateCreated { get; set; }
}

Los modelos de vista difieren de los modelos de dominio en que los modelos de vista solo contienen los datos (representados por propiedades) que desea utilizar en su vista. Por ejemplo, digamos que desea agregar un nuevo registro de empleado, su modelo de vista podría verse así:

public class CreateEmployeeViewModel
{
     public string FirstName { get; set; }

     public string LastName { get; set; }
}

Como se puede ver, solo contiene dos de las propiedades. Estas dos propiedades también están en el modelo de dominio de empleado. ¿Por qué es esto lo que puedes preguntar? Id posible que la Id no se establezca desde la vista, puede ser generada automáticamente por la tabla Empleado. Y DateCreated también se puede configurar en el procedimiento almacenado o en la capa de servicio de su aplicación. Por lo tanto, Id y DateCreated no son necesarios en el modelo de vista. Es posible que desee mostrar estas dos propiedades cuando vea los detalles de un empleado (un empleado que ya ha sido capturado) como texto estático.

Al cargar la vista / página, el método de creación de acción en el controlador de su empleado creará una instancia de este modelo de vista, llenará los campos si es necesario y luego pasará este modelo de vista a la vista / página:

public class EmployeeController : Controller
{
     private readonly IEmployeeService employeeService;

     public EmployeeController(IEmployeeService employeeService)
     {
          this.employeeService = employeeService;
     }

     public ActionResult Create()
     {
          CreateEmployeeViewModel model = new CreateEmployeeViewModel();

          return View(model);
     }

     public ActionResult Create(CreateEmployeeViewModel model)
     {
          // Do what ever needs to be done before adding the employee to the database
     }
}

Su vista / página podría verse así (asumiendo que está usando ASP.NET MVC y el motor de la vista Razor ):

@model MyProject.Web.ViewModels.CreateEmployeeViewModel

<table>
     <tr>
          <td><b>First Name:</b></td>
          <td>@Html.TextBoxFor(m => m.FirstName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.FirstName)
          </td>
     </tr>
     <tr>
          <td><b>Last Name:</b></td>
          <td>@Html.TextBoxFor(m => m.LastName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.LastName)
          </td>
     </tr>
</table>

Por lo tanto, la validación se haría solo en FirstName y LastName . Usando la validación fluida , podría tener una validación como esta:

public class CreateEmployeeViewModelValidator : AbstractValidator<CreateEmployeeViewModel>
{
     public CreateEmployeeViewModelValidator()
     {
          RuleFor(m => m.FirstName)
               .NotEmpty()
               .WithMessage("First name required")
               .Length(1, 50)
               .WithMessage("First name must not be greater than 50 characters");

          RuleFor(m => m.LastName)
               .NotEmpty()
               .WithMessage("Last name required")
               .Length(1, 50)
               .WithMessage("Last name must not be greater than 50 characters");
     }
}

Y con las anotaciones de datos podría verse esto:

public class CreateEmployeeViewModel : ViewModelBase
{
    [Display(Name = "First Name")]
    [Required(ErrorMessage = "First name required")]
    public string FirstName { get; set; }

    [Display(Name = "Last Name")]
    [Required(ErrorMessage = "Last name required")]
    public string LastName { get; set; }
}

La clave para recordar es que el modelo de vista solo representa los datos que desea utilizar , nada más. Puede imaginar todo el código y la validación innecesarios si tiene un modelo de dominio con 30 propiedades y solo desea actualizar un único valor. Dado este escenario, solo tendría este único valor / propiedad en el modelo de vista y no todas las propiedades que están en el objeto de dominio.

Un modelo de vista no solo puede tener datos de una tabla de base de datos. Puede combinar datos de otra tabla. Tome mi ejemplo anterior sobre la adición de un nuevo registro de empleado. Además de agregar solo los nombres y apellidos, es posible que también desee agregar el departamento del empleado. Esta lista de departamentos provendrá de su tabla de Departments . Así que ahora tiene datos de las tablas de Employees y Departments en un modelo de vista. Solo tendrá que agregar las siguientes dos propiedades a su modelo de vista y rellenarlo con datos:

public int DepartmentId { get; set; }

public IEnumerable<Department> Departments { get; set; }

Al editar los datos de los empleados (un empleado que ya se ha agregado a la base de datos) no diferiría mucho de mi ejemplo anterior. Cree un modelo de vista, EditEmployeeViewModel por ejemplo, EditEmployeeViewModel . Solo tenga los datos que desea editar en este modelo de vista, como el nombre y el apellido. Edite los datos y haga clic en el botón enviar. No me preocuparía demasiado el campo Id porque el valor de Id probablemente estará en la URL, por ejemplo:

http://www.yourwebsite.com/Employee/Edit/3

Tome esta Id y pásela a su capa de repositorio, junto con sus valores de nombre y apellido.

Cuando borro un registro, normalmente sigo la misma ruta que con el modelo de vista de edición. También tendría una URL, por ejemplo:

http://www.yourwebsite.com/Employee/Delete/3

Cuando la vista se carga por primera vez, obtendría los datos del empleado de la base de datos utilizando el Id 3. Luego, solo mostraría texto estático en mi vista / página para que el usuario pueda ver qué empleado está siendo eliminado. Cuando el usuario haga clic en el botón Eliminar, solo usaría el valor de Id 3 y lo pasaría a mi capa de repositorio. Solo necesita la Id para eliminar un registro de la tabla.

Otro punto, realmente no necesitas un modelo de vista para cada acción. Si se trata de datos simples, estaría bien usar solo EmployeeViewModel . Si se trata de vistas / páginas complejas y difieren entre sí, le sugiero que utilice modelos de vista independientes para cada una.

Espero que esto aclare cualquier confusión que haya tenido sobre los modelos de vista y los modelos de dominio.




Si tiene propiedades específicas de la vista y no relacionadas con el DB / Service / Data store, es una buena práctica usar ViewModels. Supongamos que desea dejar una casilla de verificación seleccionada en función de un campo DB (o dos), pero el campo DB en sí no es un valor booleano. Si bien es posible crear estas propiedades en el propio Modelo y mantenerlas ocultas del enlace a los datos, es posible que no desee saturar el Modelo en función de la cantidad de dichos campos y transacciones.

Si hay muy pocos datos y / o transformaciones específicos de la vista, puede utilizar el propio Modelo




Un montón de grandes ejemplos, permítanme explicar de manera clara y crujiente.

ViewModel = Modelo que se crea para servir a la vista.

La vista MVC de ASP.NET no puede tener más de un modelo, por lo que si es necesario mostrar las propiedades de más de un modelo en la vista, no es posible. ViewModel sirve a este propósito.

El modelo de vista es una clase de modelo que puede contener solo las propiedades que se requieren para una vista. También puede contener propiedades de más de una entidad (tablas) de la base de datos. Como su nombre lo indica, este modelo se crea específico para los requisitos de visualización.

Algunos ejemplos de modelos de vista están abajo.

  • Para listar datos de más de entidades en una página de vista, podemos crear un modelo de Vista y tener propiedades de todas las entidades para las cuales queremos listar datos. Únase a esas entidades de la base de datos y establezca las propiedades del modelo de Vista y regrese a la Vista para mostrar datos de diferentes entidades en una forma tabular
  • El modelo de vista puede definir solo campos específicos de una sola entidad que se requieren para la vista.

ViewModel también se puede usar para insertar, actualizar registros en más de una entidad, sin embargo, el uso principal de ViewModel es mostrar columnas de varias entidades (modelo) en una sola vista.

La forma de crear ViewModel es la misma que la creación de un modelo, la forma de crear una vista para el modelo de vista es la misma que la creación de una vista para el modelo.

Aquí hay un pequeño ejemplo de los datos de la lista usando ViewModel .

Espero que esto sea de utilidad.




ViewModel es un campo de trabajo que corrige la torpeza conceptual del marco MVC. Representa la 4ª capa en la arquitectura de 3 capas Modelo-Vista-Controlador. cuando el Modelo (modelo de dominio) no es apropiado, demasiado grande (más grande que 2-3 campos) para la Vista, creamos un ViewModel más pequeño para pasarlo a la Vista.




No leí todas las publicaciones, pero parece que a cada respuesta le falta un concepto que realmente me ayudó a "entenderlo" ...

Si un modelo es similar a una tabla de base de datos, entonces un ViewModel es similar a una vista de base de datos: una vista generalmente devuelve pequeñas cantidades de datos de una tabla o conjuntos complejos de datos de varias tablas (combinaciones).

Me encuentro usando ViewModels para pasar información a una vista / formulario, y luego transferir esos datos a un Modelo válido cuando el formulario se envía al controlador, también es muy útil para almacenar listas (IEnumerable).




Ahora hay un paquete ELMAH.MVC en NuGet que incluye una solución mejorada de Atif y también un controlador que maneja la interfaz elmah dentro del enrutamiento MVC (ya no es necesario usar ese axd)
El problema con esa solución (y con todos los que están aquí) es que de una u otra forma el controlador de errores de elmah en realidad está manejando el error, ignorando lo que podría querer configurar como una etiqueta customError o mediante ErrorHandler o su propio controlador de errores
La mejor solución IMHO es crear un filtro que actuará al final de todos los otros filtros y registrar los eventos que ya se han manejado. El módulo elmah debe encargarse de registrar los otros errores que no son manejados por la aplicación. Esto también le permitirá usar el monitor de estado y todos los demás módulos que se pueden agregar a asp.net para ver los eventos de error

Escribí esto mirando con el reflector al ErrorHandler dentro de elmah.mvc

public class ElmahMVCErrorFilter : IExceptionFilter
{
   private static ErrorFilterConfiguration _config;

   public void OnException(ExceptionContext context)
   {
       if (context.ExceptionHandled) //The unhandled ones will be picked by the elmah module
       {
           var e = context.Exception;
           var context2 = context.HttpContext.ApplicationInstance.Context;
           //TODO: Add additional variables to context.HttpContext.Request.ServerVariables for both handled and unhandled exceptions
           if ((context2 == null) || (!_RaiseErrorSignal(e, context2) && !_IsFiltered(e, context2)))
           {
            _LogException(e, context2);
           }
       }
   }

   private static bool _IsFiltered(System.Exception e, System.Web.HttpContext context)
   {
       if (_config == null)
       {
           _config = (context.GetSection("elmah/errorFilter") as ErrorFilterConfiguration) ?? new ErrorFilterConfiguration();
       }
       var context2 = new ErrorFilterModule.AssertionHelperContext((System.Exception)e, context);
       return _config.Assertion.Test(context2);
   }

   private static void _LogException(System.Exception e, System.Web.HttpContext context)
   {
       ErrorLog.GetDefault((System.Web.HttpContext)context).Log(new Elmah.Error((System.Exception)e, (System.Web.HttpContext)context));
   }


   private static bool _RaiseErrorSignal(System.Exception e, System.Web.HttpContext context)
   {
       var signal = ErrorSignal.FromContext((System.Web.HttpContext)context);
       if (signal == null)
       {
           return false;
       }
       signal.Raise((System.Exception)e, (System.Web.HttpContext)context);
       return true;
   }
}

Ahora, en tu configuración de filtro quieres hacer algo como esto:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        //These filters should go at the end of the pipeline, add all error handlers before
        filters.Add(new ElmahMVCErrorFilter());
    }

Tenga en cuenta que dejé un comentario allí para recordarle a la gente que si quieren agregar un filtro global que realmente maneje la excepción, debe ir ANTES de este último filtro, de lo contrario, se encontrará con el caso en el que ElmahMVCErrorFilter ignorará la excepción no controlada porque no ha sido manejado y debe ser registrado por el módulo Elmah pero luego el siguiente filtro marca la excepción como manejado y el módulo la ignora, lo que da como resultado que la excepción nunca se convierta en elmah.

Ahora, asegúrate de que los ajustes para elmah en tu webconfig tengan un aspecto como este:

<add key="elmah.mvc.disableHandler" value="false" /> <!-- This handles elmah controller pages, if disabled elmah pages will not work -->
<add key="elmah.mvc.disableHandleErrorFilter" value="true" /> <!-- This uses the default filter for elmah, set to disabled to use our own -->
<add key="elmah.mvc.requiresAuthentication" value="false" /> <!-- Manages authentication for elmah pages -->
<add key="elmah.mvc.allowedRoles" value="*" /> <!-- Manages authentication for elmah pages -->
<add key="elmah.mvc.route" value="errortracking" /> <!-- Base route for elmah pages -->

El importante aquí es "elmah.mvc.disableHandleErrorFilter", si esto es falso, usará el controlador dentro de elmah.mvc que realmente manejará la excepción usando el HandleErrorHandler predeterminado que ignorará sus configuraciones personalizadas de error

Esta configuración le permite establecer sus propias etiquetas ErrorHandler en clases y vistas, mientras aún registra esos errores a través de ElmahMVCErrorFilter, agregando una configuración personalizada de error a su web.config a través del módulo elmah, incluso escribiendo sus propios Controladores de errores. Lo único que debe hacer es recordar no agregar ningún filtro que realmente maneje el error antes del filtro elmah que hemos escrito. Y me olvidé de mencionar: no hay duplicados en elmah.





asp.net-mvc viewmodel