2010-05-10 21 views
10

Encontré que los controles de usuario son increíblemente útiles cuando se trabaja con formularios web ASP.NET. Al encapsular el código requerido para mostrar un control con el marcado, la creación de componentes reutilizables fue muy directa y muy, muy útil.ASP.NET MVC: ¿Buena sustitución para el control del usuario?

Si bien MVC proporciona una conveniente separación de preocupaciones, esto parece romper la encapsulación (es decir, puede agregar un control sin agregar o usar su código de soporte, lo que lleva a errores de tiempo de ejecución). Tener que modificar un controlador cada vez que agrego un control a una vista me parece que integra preocupaciones, no separarlas. Prefiero romper la ideología purista de MVC que renunciar a los beneficios de los controles reutilizables empaquetados.

Necesito poder incluir componentes similares a los controles de usuario de formularios web en todo el sitio, pero no para todo el sitio, y no en un nivel que pertenece a una página maestra. Estos componentes deberían tener su propio código, no solo marcado (para interactuar con la capa empresarial), y sería genial si el controlador de la página no necesitara saber sobre el control. Como los controles de usuario de MVC no tienen código subyacente, no veo una buena manera de hacerlo.

Actualización FINALMENTE, una buena (y, en retrospectiva, obvia) forma de lograr esto.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.Mvc; 

namespace K.ObjectModel.Controls 
{ 
    public class TestControl : ViewUserControl 
    { 
     protected override void Render(System.Web.UI.HtmlTextWriter writer) 
     { 
      writer.Write("Hello World"); 
      base.Render(writer); 
     } 
    } 
} 

crear una nueva clase que hereda ViewUserControl

Reemplazar el método .Render() como se muestra arriba.

registrar el control a través de su ASCX asociada como lo haría en un formulario web:

<%@ Register TagName="tn" TagPrefix="k" Src="~/Views/Navigation/LeftBar.ascx"%>

utilizar la etiqueta correspondiente en cualquier punto de vista o página maestra que necesita:

<k:tn runat="server"/>

Asegúrese de que su .ascx herede su nuevo control:

<%@ Control Language="C#" Inherits="K.ObjectModel.Controls.TestControl" %>

Voila, ya está en marcha. Esto se prueba con ASP.NET MVC 2, VS 2010 y .NET 4.0.

Su etiqueta personalizada hace referencia a la vista parcial ascx, que hereda de la clase TestControl. Luego, el control anula el método Render(), que se llama para representar la vista, lo que le brinda control total sobre el proceso desde la etiqueta hasta la salida.

La diferencia entre utilizar este enfoque y llamar a Html.RenderPartial() o `Html.RenderAction() 'es agregar el control a una vista que se realiza con una etiqueta tipo webforms, que no solo es más cómoda para los diseñadores, sino que también tener que estar al tanto de los nombres y métodos de los controladores. El nombre de la clase de control está aislado en ASCX, lo que facilita la colocación de estos en un ensamblaje y su reutilización en proyectos separados.

Algunos pueden decir que esto infringe el SoC, pero creo que este enfoque es funcionalmente equivalente a vincular una vista parcial y un controlador al mismo tiempo manteniendo un marcado limpio. Sin embargo, debe quedar claro que aún le corresponde al desarrollador mantener solo la lógica relacionada con la presentación en el control. La lógica de acceso a datos y negocios aún pertenece a sus respectivas capas.

+0

Argh ... Su edición es frustrante ya que ha cambiado su pregunta. los "controles reutilizables y empaquetados" con MVC viven en un mundo totalmente diferente a RenderPartial y RenderAction. - Lo que comienza a hablar es el debate/discusión RenderPartial vs. RenderAction que se ha respondido anteriormente y es principalmente un argumento filosófico sobre las responsabilidades del código. – jfar

+0

@jfar ?? He agregado la sección de "opciones" porque eso es lo que todos parecen ofrecer como soluciones. Si tienes una idea mejor, estaría encantado de escucharla. –

+2

@David Lively: no puede comparar las opciones RenderPartial y Portable Areas sin definir el alcance de la solución que intenta resolver. Es como decir "¿cómo debo matar a este insecto, el matamoscas, el bate de béisbol o el arma nuclear desde la órbita" sin decirle a la persona que le pregunta si está tratando de matar a un mosquito o una especie alienígena sintética con sangre ácida? Cuando dices "controles reutilizables y empaquetados" estás hablando de un gran problema, pero si incluyes RenderPartial como opción, dices "reutilización de marcado simple". ¿Cuál es? ¿Qué tipo de error estás tratando de matar? – jfar

Respuesta

4

A primera vista, es fácil descartar que MVC no tenga las capacidades para componentes reutilizables.

Una vez que conozca ASP.NET MVC encontrará several techniques for creating rich controls and components y encapsulando aspectos de MVC siguiendo along the same pathways como encapsulando una aplicación WebForms.

Creo que lo que hace es solo mirar los aspectos de Vista de MVC y no cómo todos los M y C subyacentes pueden encapsularse y vincularse. Las vistas parciales, Render Action/Partial son solo pequeñas partes de las capacidades de los componentes subyacentes de MVC. Hay mucha más riqueza bajo las sábanas.

+1

"Riqueza bajo las sábanas" eso es lo que dijo. –

5

Estoy un poco confundido aquí.

En primer lugar, .NET MVC equivalente a los controles de usuario es Partial Views. Las Vistas parciales son una manera conveniente de encapsular la funcionalidad de vista común en una sola ubicación. Luego puede llamar a una Vista parcial desde otra Vista.

En segundo lugar, modificar una Vista no debe significar también la modificación de un controlador. Si tiene que hacer un cambio en ambos simplemente porque su Vista cambió (y no los datos subyacentes), entonces hay un problema de código en algún lugar a lo largo de la línea.

+0

Entiendo tu punto (creo), pero considera esto: quiero mostrar, por ejemplo, un contador de visitas en una página. Sin tener un código vinculado a ese control o vista parcial, tendré que modificar el controlador subyacente para asegurarme de que el recuento de aciertos actual se proporciona en el ViewData o modelo que se pasa a la vista. Cualquier controlador que necesite mostrar esta vista parcial necesitará proporcionar esta información, y por lo tanto tendrá que ser modificado. ¿Pensamientos? Esto podría proporcionarse en una clase de controlador base común, pero el requisito para mostrar esto puede no reflejar una jerarquía de clases. –

+0

@David Lively: coloque el código del contador html en la página maestra (usando la vista parcial o no). Establezca el valor del contador en ViewData en su clase BaseController. Debería tener una clase de controlador base para compartir características comunes. – LukLed

+1

@LukLed Eso ayuda en muchas situaciones, pero en este solo parece tener sentido si las páginas que necesitan mostrar el contador tienen una relación jerárquica. Además, si empiezo a arrojar todo esto en una clase BaseController, cada controlador que desciende de eso llevará esta información que no es necesaria para todas las vistas. –

1

Consideremos el siguiente ejemplo:

  • Mi opinión (CustomerDetail.ascx) se une a ICustomerDetail vista-modelo que se parece a:

    interfaz ICustomerDetail {
    string Nombre {get; }
    Dirección CurrentAddress {get; }
    }

  • I puede crear una vista Address.ascx parcial que se une a IAddress vista-modelo

  • Cuando estoy creando la CustomerDetail.ascx, puedo colocar el Address.ascx en la misma superficie & se unen al campo oCustomerDetail.Address

  • OMI - debemos estar componiendo múltiples vistas desde estos puntos de vista parciales más pequeñas en MVC & Aquí es donde se verá la reutilización & el poder de los controles de usuario (vista parcial)

  • Ahora si mi controlador vuelve ICustomerDetail, voy a ser capaz de volver a utilizar el Address.ascx sin ningún problema

HTH.

+0

Esa es una buena explicación, sin embargo, un punto que todos parecen perder es que con RenderAction siempre tendrá algún código de acción del controlador que se ejecutará primero. Entonces, si necesita que ocurra algo especial, antes de que se visualice View o ViewUserControl, entonces use RenderAction. Y usa RenderPartial() cuando no necesites cargar nada desde otro lugar (ya tienes todo en el Modelo actual en la Vista desde donde llamas a RenderPartial() o no necesitas nada más y puedes llamar a RenderPartial() con solo el nombre ViewUserControl como su argumento). – mare

2

un control usuario es sólo algunas materia que representa HTML, en mvc usted tiene ayudantes HTML y vistas parciales y vistas normales (se puede hacer con RenderAction)

Html.Helper("someStuff") 
Html.RenderPartial("viewname") 
Html.RenderAction<Controller>(o => o.Action()); 

así que básicamente es sólo los ayudantes

en realidad se puede sustituir fácilmente una llamada a

Html.TextBoxFor(o => o.Name); 

con

Html.RenderPartial("textbox", Model.Name); 
1

Tomemos una página de registro para un sitio de comercio electrónico, como un ejemplo. Solicitas al usuario su nombre, contraseña, información postal, raza de perro favorita, etc. En algún otro lugar de la aplicación, también debes recopilar una dirección de facturación y una dirección de envío. Para aplicar DRY, crea un control de usuario que gestiona la entrada de la información de la dirección.

Así, para ilustrar aún más, su clase de dirección se ve algo como esto:

public class Address 
{ 
    public string StreetAddress { get; set; } 
    public string City { get; set; } 
    ... 
} 

Su clase de registro:

public class UserReg 
{ 
    public string UserName { get; set; } 
    public Address MailingAddress { get; set; } 
    ... 
} 

Sus factura y envío puede descender de la clase Dirección:

public class BillingAddress : Address 
{ 
    ... 
} 

public class ShippingAddress : Address 
{ 
    ... 
} 

Para los ejemplos siguientes, supongo que ha agregado System.Web.Mvc a la sección namespaces de web.config. En base a esta jerarquía de clases, el control de usuario tendrá una etiqueta de control que se refiere sólo a la clase Dirección:

<%@ Control Language="C#" Inherits="ViewUserControl<Address>" %> 

Puesto que usted ha hecho esto, sólo hay que pasar la referencia modelo apropiado de la página. En la página de registro de usuario:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="ViewPage<UserReg>" %> 
    ... 
    <% Html.RenderPartial("AddressControl", Model.MailingAddress); %> 

en la página de dirección de facturación:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="ViewPage<BillingAddress>" %> 
    ... 
    <% Html.RenderPartial("AddressControl", Model); %> 

en la página de dirección de envío:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="ViewPage<ShippingAddress>" %> 
    ... 
    <% Html.RenderPartial("AddressControl", Model); %> 

me puede pasar el modelo directamente de la facturación y envío página, porque la clase desciende directamente de la dirección. Mientras la lógica esté en su lugar para procesar las direcciones correctamente, no tendrá que hacer muchos cambios en el controlador, si corresponde.

+0

+1 para el código, pero esto no parece responder al núcleo de la pregunta, que puede haber sido pobremente expresado. Necesito mantener el código para la vista parcial con la vista, para que pueda ejecutarse sin tener que modificar el controlador. –

Cuestiones relacionadas