2012-05-22 14 views
8

Acabo de empezar a usar y me estoy enamorando del patrón de diseño MVC.MVC Reducir el código repetitivo

Mi único problema favorito es que parece producir muchos códigos repetitivos. Por ejemplo.

Tengo una aplicación MVC estándar con mi base de datos (modelos) en un proyecto, separada de mis controladores/vistas/modelos de vista en otro, nuevamente separado de mis métodos de prueba en otro. Todo funciona genial

Modelos: Ahora, tengo un montón de buenas clases de EF4 en mi proyecto de base de datos, para lo cual tengo que usar ViewModels en mi proyecto real para acceder a mis datos. No hay problema aqui

Controladores: Sin embargo, cada controlador que tengo, básicamente hace exactamente lo mismo. Obtiene y establece los datos de ViewModels, por lo que aunque cada controlador es diferente, ya que solo obtiene datos diferentes, todos realizan esencialmente el mismo trabajo, de la misma manera. (Actualmente tengo 9, pero esto podría explotar fácilmente a más de 50).

Por ejemplo:

public class Dummy1Controller : Controller 
{ 
    private MyProj.Data.Entities _entities = new Data.Entities(); 
    private MyProj.Data.Entities2 _coreEntities = new Data.Entities2(); 

    //GET: /Customers/ 
    public ActionResult Index() 
    { 
     if (_entities.table1.Count() == 0) return View(); 

     var pastObj = _entities.table1.First(); 
     return View(new Table1ViewModel() 
     { 
      Id = pastObj.Id, 
      FirstName = pastObj.FirstName, 
      LastName = pastObj.LastName, 
      . 
      . 
      . 
      . 
     }); 
    } 
} 

public class Dummy2Controller : Controller 
{ 
    private MyProj.Data.Entities _entities = new Data.Entities(); 
    private MyProj.Data.Entities2 _coreEntities = new Data.Entities2(); 

    //GET: /Vehicles/ 
    public ActionResult Index() 
    { 
     if (_entities.table2.Count() == 0) return View(); 

     var pastObj = _entities.table2.First(); 
     return View(new Table1ViewModel() 
     { 
      RegNo = pastObj.RegNo, 
      Make = pastObj.Make, 
      Model = pastObj.Model, 
      . 
      . 
      . 
      . 
     }); 
    } 
} 

public class Dummy3Controller : Controller 
{ 
    private MyProj.Data.Entities _entities = new Data.Entities(); 
    private MyProj.Data.Entities2 _coreEntities = new Data.Entities2(); 

    //GET: /Invoices/ 
    public ActionResult Index() 
    { 
     if (_entities.table3.Count() == 0) return View(); 

     var pastObj = _entities.table3.First(); 
     return View(new Table1ViewModel() 
     { 
      InvNo = pastObj.InvNo, 
      Amount = pastObj.Amount, 
      Tax = pastObj.Tax, 
      . 
      . 
      . 
      . 
     }); 
    } 
} 

Vistas: todas las vistas generadas a partir de los contollers funcionan muy bien. Execpt, lo único que cambia son los datos (campos con etiquetas y cuadros de texto). Una vez más, todos hacen el mismo trabajo (pero con diferentes conjuntos de datos).

@model MyProject.Web.ViewModels.Table1ViewModel 

@{ 
    ViewBag.Title = "Index"; 
} 

<link href="@Url.Content("~/Content/CSS/GenericDetailStyles.css")" rel="stylesheet" type="text/css" /> 

<section id="content"> 
    <div id="table"> 
     <div> 
      <h2>Customer</h2> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.Id)</div> 
      <div class="right">@Html.TextBoxFor(model => model.Id)</div> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.FirstName)</div> 
      <div class="right">@Html.TextBoxFor(model => model.FirstName)</div> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.LastName)</div> 
      <div class="right">@Html.TextBoxFor(model => model.LastName)</div> 
     </div> 
     . 
     . 
     . 
     . 
    </div> 
</section> 

@{Html.RenderAction("Index", "FooterPartial");} 


-------------------------------------------------------------------------------------- 


@model MyProject.Web.ViewModels.Table2ViewModel 

@{ 
    ViewBag.Title = "Index"; 
} 

<link href="@Url.Content("~/Content/CSS/GenericDetailStyles.css")" rel="stylesheet" type="text/css" /> 

<section id="content"> 
    <div id="table"> 
     <div> 
      <h2>Vehicle</h2> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.RegNo)</div> 
      <div class="right">@Html.TextBoxFor(model => model.RegNo)</div> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.Make)</div> 
      <div class="right">@Html.TextBoxFor(model => model.Make)</div> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.PatientID)</div> 
      <div class="right">@Html.TextBoxFor(model => model.Model)</div> 
     </div> 
     . 
     . 
     . 
     . 
    </div> 
</section> 

@{Html.RenderAction("Index", "FooterPartial");} 


-------------------------------------------------------------------------------------- 


@model MyProject.Web.ViewModels.Table3ViewModel 

@{ 
    ViewBag.Title = "Index"; 
} 

<link href="@Url.Content("~/Content/CSS/GenericDetailStyles.css")" rel="stylesheet" type="text/css" /> 

<section id="content"> 
    <div id="table"> 
     <div> 
      <h2>Invoice</h2> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.InvNo)</div> 
      <div class="right">@Html.TextBoxFor(model => model.InvNo)</div> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.Amount)</div> 
      <div class="right">@Html.TextBoxFor(model => model.Amount)</div> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.Tax)</div> 
      <div class="right">@Html.TextBoxFor(model => model.Tax)</div> 
     </div> 
     . 
     . 
     . 
     . 
    </div> 
</section> 

@{Html.RenderAction("Index", "FooterPartial");} 

Problema: Quiero hacer un solo controlador, y que sea dinámico. Para que pueda leer datos de diferentes modelos de vista. (¿Por qué tener 9 o 50 controladores esencialmente haciendo el mismo trabajo)

Luego quiero hacer lo mismo con las vistas. Para que diferentes campos puedan generarse dinámicamente. (¿Por qué tener 9 o 50 vistas todas haciendo el mismo trabajo). Si la vista se basa en el controlador, la vista debería poder cambiar en función de sus propiedades.

Esencialmente, todo lo que quiero hacer es encontrar una manera de decirle al controlador que lea desde el modelo de vista X o VM - Y o VM - Z ect y debe poder generar las propiedades, recuperar los datos asociados y pasarlo a la vista, que al recibir, generará los campos con etiquetas y cuadros de texto.

Supongo que quiero saber si hay alguna manera de hacerlo utilizando la reflexión. Como los modelos de vista son clases básicas con propiedades simples. Uno podría crear potencialmente una clase de controlador base que tenga un método para leer en un objeto viewmodel especificado, obtener sus propiedades, leer también una tabla asociada y hacer coincidir los campos en esa tabla con las propiedades de la clase. Finalmente uno puede pasar el registro de la tabla para mostrar. La vista se puede generar automáticamente basándose en esto usando algún tipo de afeitadora, C# o javascript.

Cualquier comentario sobre si es posible o no sería bienvenido.

+0

¿Por qué mezclar el patrón MVC y MVVM? Supongo que lo que estoy preguntando es, ¿para qué necesitas ViewModel y no solo pasas el Modelo? – Brunner

+0

@Brunner - Bueno, en primer lugar, es la forma en que me mostraron y no sé cómo hacerlo directamente (soy relativamente nuevo en MVC). En segundo lugar, mi empleadora quiere que se haga de esta manera. Finalmente, cuando trabajo en mis propios proyectos, lo hago de esta manera porque no me gusta tener el DB real como parte del proyecto, como muestran muchos ejemplos, y tenerlo generado automáticamente o regenerado. Hay muy pocos ejemplos prácticos que le muestran cómo conectarse a una base de datos real en vivo. Si conoces alguno, realmente apreciaría los enlaces a ellos. Gracias por su respuesta. –

+0

No estoy seguro de lo que quiere decir con "dbs reales en vivo", pero es posible que desee ver las POCO en combinación con EF (por ejemplo, Code-First): es más o menos lo que ya tiene, simplemente empacado cuidadosamente. Tampoco estoy seguro de lo que quiere decir con no tener el DB real en el proyecto, dado su ejemplo, podría hacer "devolver nueva Vista (pastObj);" y ni siquiera tiene que editar la Vista (excepto la declaración @model) – Brunner

Respuesta

10

Puede usar AutoMapper para eliminar todo el código de copia de valores entre modelos/entidades.

Además, considere utilizar diseños, atributos de anotación de datos y plantillas (con Html.DisplayFor y Html.EditorFor) para reducir sus vistas.

http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html

Se podría investigar la posibilidad de crear una clase de controlador de base genérica, que tomará el tipo de modelo y de Entidad y contendrá la lógica común para un mantenimiento, pero puede ser un paso demasiado lejos y obstaculizar su desarrollo más adelante.

+0

@Francis Tenga en cuenta que automapper (o valueinjecter) se debe usar solo para el mapeo unidireccional, no se recomienda pasar de un método de publicación a una entidad. Funcionaría en algunos casos, pero se desmorona en otros que tienen que ver con, por ejemplo, el marco de entidades ya que las propiedades no tienen su conjunto de banderas 'modificado', pero el objeto en sí lo hace, por lo que no se producen cambios que regresen al db. Solo un fyi para cualquier otra persona leyendo. –

+0

@AdamTuliper - He usado AutoMapper con EF sin ningún problema. Todo lo que AutoMapper hace es llamar a los establecedores de propiedades, no diferentes de hacer manualmente en el código. ¿Podrías arrojar más luz sobre los problemas que has experimentado? –

+0

Utilizo AutoMapper para el mapeo bidireccional, pero ya no redimensiono ViewModels directamente a las entidades. Nuestras entidades ahora tienen 'conjuntos internos protegidos ', para evitar este tipo de cosas. En cambio, automatizo desde un modelo de vista a un objeto de comando, luego realizo la actualización real de las propiedades de la entidad desde un controlador de comando. Descubrí que la automatización desde viewmodels directamente a las entidades era demasiado complicada y propensa a errores, sin forma de aplicar las reglas comerciales a menos que estuvieran en la capa de UI. – danludwig

5

Creo que un controlador dinámico podría estar un paso demasiado lejos: lo que sucede cuando haces todo genérico y luego una de tus vistas requiere algo más complejo que un simple mapa de la base de datos: ¿extiendes tu 'vista genérica controlador 'para hacer frente a esto? Podría terminar here.

Quizás debería echar un vistazo a Automapper para eliminar parte de la naturaleza repetitiva de lidiar con sus modelos de vista.

http://automapper.codeplex.com/

+0

Estoy echando un vistazo al automapper en este momento. Puede tardar unos días, pero volveré después de haber hecho un poco o volver a buscar para elegir una respuesta aceptada. Mientras tanto, gracias por tu respuesta. –

0

puede generar un plantillas EditFor/modelo para el objeto, o ur clase base, sea lo que sea, y luego simplemente hacer una vista, que aceptará su modelo, sus metadatos (con la ayuda de MVC proveedores de metadatos), y construir los campos de forma dinámica de acuerdo.
this post cubre hwo se puede hacer en MVC2, pero u puede adaptarse fácilmente esta técnica para MVC 3, así ..
Im usando una plantilla de este tipo en un proyecto que trabaja en mi, donde tengo pocas docenas de diferentes entidades , todos compartiendo la misma apariencia. Con la ayuda de mi propio proveedor de metadatos, que también me ayuda a obtener el orden de los campos y si deben ser visibles en cada vista, construí una vista exactamente para mostrar todas mis entidades

+0

Gracias por la respuesta. Estoy en el proceso de leer ese enlace y parece interesante. Aunque Automapper parece aún más. Puedo ver hasta cierto punto por qué generalizar el controlador podría ser un paso demasiado lejos. Pero necesito hacer más investigación. Gracias por tu respuesta. –

+0

tbh, nunca usé Automapper, y esta, por supuesto, podría ser la solución perfecta para ti ... buena suerte :) – YavgenyP

0

Estoy de acuerdo con otros aquí que tienen un solo controlador dinámico para manejar todos los modelos y vistas en su aplicación va demasiado lejos. Hay otras maneras de reducir la cantidad de código en los controladores. Otras respuestas aquí han mencionado AutoMapper, que es una gran herramienta. El inventor de AutoMapper, Jimmy Bogard, también tiene un great video here que incluye otras cosas que puede hacer para reducir la cantidad de código en sus controladores.

En cuanto a las vistas, ya puede tener una vista que maneje todos sus controladores utilizando EditorTemplates. Puede crear un EditorTemplate personalizado que utilizará la reflexión para examinar el modelo y representar los campos de entrada en consecuencia. Todo esto usa "metadatos del modelo", y está a good post on that by Brad Wilson here.

+0

Gracias por la respuesta. Estoy buscando en AutoMapper ahora porque mucha gente lo recomendó junto con el enlace al sitio de Brad Wilson. Aceptaré una respuesta en unos días después de haber completado mi investigación. –

Cuestiones relacionadas