c# - net - MVC 3-¿Cómo va a funcionar esto?
mvc 5 download (7)
He hecho esta publicación hace más de un año, y creo que tiene sentido actualizarla ya que está obteniendo bastantes puntos de vista.
Me estoy perdiendo algo o Microsoft realmente ha estropeado el MVC. Trabajé en proyectos Java MVC y fueron limpios y simples. Sin embargo, esto es un completo desastre IMO. Los ejemplos en línea como NerdDinner y los proyectos discutidos en ASP.Net son demasiado básicos, de ahí que funcionen "simplemente". Disculpe si esto suena negativo, pero esta es mi experiencia hasta el momento.
Tengo un repositorio y un servicio que habla al repositorio. Los controladores llaman al servicio.
Mi capa de datos NO es independiente de la persistencia, ya que las clases fueron generadas por SQL metal. Debido a esto, tengo mucha funcionalidad innecesaria. Idealmente, me gustaría tener POCO, pero no encontré una buena manera de lograrlo todavía.
* Actualización: por supuesto, Microsoft no ha estropeado nada, lo hice. No entendí completamente las herramientas que estaban a mi disposición. El mayor defecto en lo que hice fue que he elegido una tecnología incorrecta para persistir en mis entidades. LINQ to SQL funciona bien en aplicaciones con estado ya que el contexto de datos se puede rastrear fácilmente. Sin embargo, este no es un caso en un contexto sin estado. ¿Cuál sería la elección correcta? El código o el código de Entity Framework solo funciona bastante bien, pero lo que es más importante, es que no debería importar. MVC, o aplicaciones de front-end no deben saber cómo persisten los datos. *
Al crear entidades, puedo usar el enlace de objetos:
[HttpPost]
public ActionResult Create(Customer c)
{
// Persistance logic and return view
}
Esto funciona muy bien, MVC hace algunos enlaces detrás de la escena y todo está "muy bien".
No fue "Jolly Good". El cliente era un modelo de dominio, y lo que era peor, dependía del medio de persistencia, ya que fue generado por SQL metal. Lo que haría ahora es diseñar mi modelo de dominio, que sería independiente del almacenamiento de datos o las capas de presentación. Luego, crearía el modelo de vista de mi modelo de dominio y lo usaría en su lugar.
Tan pronto como me gustaría hacer algo más complejo, por ejemplo, guardar Pedido que está vinculado al cliente, todo parece romperse:
[HttpPost]
public ActionResult Create(Order o)
{
// Persistance logic and return view
}
Para conservar un pedido, necesito el Cliente o al menos CustomerId. CustomerId estuvo presente en la vista, pero en el momento en que ha llegado al método Create, ha perdido CustomerId. No me gusta sentarme a depurar el código MVC, ya que no podré cambiarlo en un entorno de hosting de ninguna manera.
Ok, un poco de gemido aquí, lo siento. Lo que haría ahora, es crear un modelo de vista llamado NewOrder, SaveOrder o EditOrder, dependiendo de lo que estoy tratando de lograr. Este modelo de vista contendría todas las propiedades que me interesan. La vinculación automática lista para usar, como su nombre lo indica, vinculará los valores enviados y no se perderá nada. Si quiero un comportamiento personalizado, entonces puedo implementar mi propio "enlace" y hará el trabajo.
La alternativa es usar FormCollection:
[HttpPost]
public ActionResult Create(FormCollection collection)
{
// Here I use the "magic" UpdateModel method which sometimes works and sometimes doesn''t, at least for LINQ Entities.
}
Esto se usa en libros y tutoriales, pero no veo un punto en un método que tenga una alternativa: TryUpdateModel: si esto falla o el modelo no es válido, intenta actualizarlo de cualquier manera. ¿Cómo puede estar seguro de que esto va a funcionar?
Autobinding con modelos de vista funcionará la mayor parte del tiempo. Si no es así, entonces puede anularlo. ¿Cómo sabes que siempre funcionará? Lo pruebas en una unidad y duermes bien.
Otro enfoque que he probado es usar ViewModel - objetos envoltorios con reglas de validación. Esto parece una buena idea, excepto que no quiero agregar anotaciones a las clases de Entity. Este enfoque es ideal para mostrar los datos, pero ¿qué haces cuando se trata de escribir datos?
[HttpPost]
public ActionResult Create(CustomViewWrapper submittedObject)
{
// Here I''d have to manually iterate through fields in submittedObject, map it to my Entities, and then, eventually, submit it to the service/repository.
}
** Ver el modelo es una buena forma de avanzar. Tendría que haber algún código de mapeo del modelo de vista al modelo de dominio, que luego se puede pasar al servicio relevante. Esta no es una forma correcta, pero es una forma de hacerlo. Las herramientas de auto mapping son tus mejores amigos y debes encontrar la que se adapte a tus necesidades, de lo contrario estarás escribiendo toneladas de código repetitivo **.
¿Me estoy perdiendo algo o es así como debería funcionar Microsoft MVC3? No veo cómo esto simplifica las cosas, especialmente en comparación con Java MVC.
Lo siento si esto suena negativo, pero esta ha sido mi experiencia hasta ahora. Aprecio el hecho de que el marco se mejora constantemente, se introducen métodos como UpdateModel, pero ¿dónde está la documentación? Tal vez es hora de parar y pensar un poco? Prefiero que mi código sea consistente en todo momento, pero con lo que he visto hasta ahora, no tengo ninguna confianza en que este sea un buen camino a seguir.
Amo el marco. Hay mucho que aprender y no es mucho más emocionante que nunca. Probablemente debería hacer otra publicación con respecto a los formularios web. Espero que esto sea útil.
1) Para el caso de guardar una orden y no tener CustomerId presente. Si Order
tiene una propiedad CustomerId en ella, y usted tiene una vista muy tipada, entonces puede persistir de nuevo a la acción de su controlador agregando
@Html.HiddenFor(model => model.CustomerId)
Al hacer esto, la carpeta de modelo predeterminada rellenará las cosas por usted.
2) Con respecto al uso de un modelo de vista, recomendaría ese enfoque. Si utilizas algo como AutoMapper puedes sacar algo de dolor de los escenarios de mapeo redundante. Si usa algo así como Validación fluida, entonces puede separar las inquietudes de validación muy bien.
Aquí hay un buen enlace sobre un enfoque general de implementación ASP.NET MVC.
Creo que la tabla de pedidos incluiría un campo CustomerID, de ser así, el único problema es que quizás no incluyas ningún control en la vista para mantener ese valor, y luego se pierde.
Intenta seguir este ejemplo.
1) OBTENGA acción antes de enviarla a la Vista, digamos que asigna el IdCliente en este punto.
public ActionResult Create()
{
var o = new Order();
o.CustomerID = User.Identity.Name; // or any other wher you store the customerID
return View(o);
}
2) The View, si no usa ningún control para el CustomerID, como el cuadro de texto, el combobox, etc., debe usar un campo oculto para mantener el valor.
@using (Html.BeginForm())
{
@Html.HiddenFor(m => m.CustomerID)
<label>Requested Date:</label>
@Html.TextBoxFor(m => m.DateRequested)
...
}
3) Finalmente, la acción POST para obtener y mantener el orden. Aquí, como CustomerID se mantuvo en el valor oculto, Model Binder colocará automáticamente todos los valores de Formulario en el objeto Order o, luego solo tendrá que usar los métodos CRUD y persistir.
[HttpPost]
public ActionResult Create(Order o)
{
return View();
}
Puede haber dos enfoques para esto, uno para guardar implícitamente todos los valores del Modelo, incluso si no se usa en la Vista, y el otro es para mantener solo esos valores utilizados. Creo que MVC está haciendo lo correcto para seguir el último, evitar innecesariamente mantener una gran cantidad de basura para modelos más grandes, cuando lo único que se piensa es nombrar uno, un CustomerName, de alguna manera puede darle control sobre qué datos conservar a través del ciclo completo acción-vista-acción y guardar memoria.
Para escenarios más complejos, donde no todos los campos están en el mismo modelo, necesita usar ViewModels. Por ejemplo, para escenarios de detalles de materia, crearía un modelo OrderView que tiene dos propiedades: Order o e IEnumerable <OrderDetail> od, pero nuevamente, necesitará usar explícitamente los valores en la Vista, o usar campos ocultos.
En versiones recientes, ahora puede usar las clases POCO y Code-First, que lo hace todo más limpio y fácil. Puede probar EF4 + CTP5.
Debo extrañar el problema.
Usted tiene un Orden de control con una Acción de Crear tal como lo dijo:
public class OrderController()
{
[HttpGet]
public ViewResult Create()
{
var vm = new OrderCreateViewModel {
Customers = _customersService.All(),
//An option, not the only solution; for simplicities sake
CustomerId = *some value which you might already know*;
//If you know it set it, if you don''t use another scheme.
}
return View(vm);
}
[HttpPost]
public ActionResult Create(OrderCreateViewModel model)
{
// Persistance logic and return view
}
}
El mensaje Crear acciones muestra un modelo de vista de tipo OrderCreateViewModel que se parece a ese.
public class OrderCreateViewModel
{
// a whole bunch of order properties....
public Cart OrderItems { get; set; }
public int CustomerId { get; set; }
// Different options
public List<Customer> Customers { get; set; } // An option
public string CustomerName { get; set; } // An option to use as a client side search
}
Su vista tiene una lista desplegable de clientes que puede agregar como propiedad al modelo de vista o un cuadro de texto que conecta hasta la búsqueda en el lado del servidor a través de JQuery donde puede establecer un campo oculto de CustomerId cuando se realiza una coincidencia, sin embargo tu decides hacerlo Y si ya conoce al cliente Id con anticipación (lo que parece implicar algunas de las otras publicaciones), simplemente configúrelo en el modelo de vista y omita todo lo anterior.
Tienes todos tus datos de pedido. Tiene el Id del cliente del cliente adjunto a este pedido. Eres bueno para ir.
"Para persistir un pedido, necesito el Cliente o, al menos, el Id. De cliente. CustomerId estuvo presente en la vista, pero cuando se creó el método, ha perdido CustomerId".
¿Qué? ¿Por qué? Si CustomerId estaba en la vista, configurado y enviado de vuelta, está en el modelo para el método HttpPost Create que está exactamente donde lo necesita. ¿Qué quieres decir con que se está perdiendo?
ViewModel se asigna a un objeto Model de orden de tipo. Como se sugirió, usar AutoMapper es útil ...
[HttpPost]
public ActionResult Create(OrderCreateViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
// Persistance logic and return view
var orderToCreate = new Order();
//Build an AutoMapper map
Mapper.CreateMap<OrderCreateViewModel, Order>();
//Map View Model to object(s)
Mapper.Map(model, orderToCreate);
//Other specialized mapping and logic
_orderService.Create(orderToCreate);
//Handle outcome. return view, spit out error, etc.
}
No es una necesidad, puedes mapearlo manualmente, pero simplemente hace las cosas más fáciles.
Y estás listo. Si no desea utilizar las anotaciones de datos para la validación, bien, hágalo en la capa de servicio, use la biblioteca de validación con fluidez mencionada, lo que elija. Una vez que llame al método Create () de su capa de servicio con todos los datos, estará listo. ¿Dónde está la desconexión? ¿Qué nos estamos perdiendo?
La respuesta de ataddeini es correcta, solo intento mostrar un poco más de código. Upvote ataddeini
No creo que su problema sea con asp.net MVC, sino con todas las piezas que elija usar juntas.
¿Lo quieres crudo y simple?
Use POCO por todas partes e implemente el repositorio donde lo necesite.
No he usado Java MVC, pero haría que toda la pregunta pareciera menos una diatriba si incluyes cómo resolviste el problema en particular allí.
Vamos a aclarar algunos conceptos erróneos o tal vez una falta de comunicación:
- Puede pasar objetos complejos a través de una publicación a la vista. Pero solo quiere hacerlo si tiene sentido, vea la siguiente viñeta
- La muestra que elegiste allí suena algunas alarmas. Aceptar los datos del Cliente o CustomerID para un pedido y no verificar la autorización puede ser un gran agujero de seguridad. Lo mismo podría decirse de una Orden en función de lo que está aceptando / permitiendo. Este es un caso enorme para el uso de ViewModels, independientemente de POCOs, LINQ, Asp.net MVC o Java MVC.
- Puede pasar valores simples que no se muestran a través de una publicación en la vista. Se hace con campos ocultos (que asp.net MVC admite de manera muy simple para usar el valor del modelo), y en algunos escenarios genera los campos ocultos para usted.
- De ninguna manera está obligado a usar linq2sql con Asp.net MVC. Si le parece que no tiene la intención de usarlo, aléjese de él. Tenga en cuenta que me encanta linq2sql, pero la forma en que se relaciona con su visión de lo que puede hacer con asp.net mvc es extraño.
- "Trabajé en proyectos de Java MVC y fueron limpios y simples". Trabajar en un proyecto no es lo mismo que diseñar el proyecto tú mismo. Las habilidades de diseño afectan lo que obtienes de cualquier cosa. No digo que sea su caso, pero solo quería señalarlo, dada la falta de detalles sobre lo que le falta a Java MVC.
- "Mi capa de datos NO es independiente de la persistencia, ya que las clases fueron generadas por SQL metal. Debido a esto, tengo muchas funcionalidades innecesarias. Idealmente, me gustaría tener POCO, pero no encontré una buena manera de lograr esto. todavía". Escogiste la tecnología equivocada, linq2sql no está diseñado para ajustarse a ese requisito. No ha sido un problema en los proyectos en los que lo he usado, pero todo está diseñado de tal manera que está menos vinculado a sus detalles de lo que parece. Dicho eso, solo muévete a otra cosa. Por cierto, deberías haber compartido lo que usaste con Java MVC.
- "CustomerId estuvo presente en la vista, pero cuando se creó el método Create, ha perdido CustomerId". Si la propiedad está en orden, puede apostar que su código tiene el error. Ahora, esa habría sido una pregunta Real totalmente diferente, por qué no está usando el CustomerId / tal pregunta vendría con: su clase de Cliente, la Vista, lo que está pasando a la Vista ... las respuestas incluirían, pero no estar limitado a: inspeccionar la fuente HTML en el navegador para ver qué valor está publicando realmente con la fuente (alternativamente, use el violín para ver lo mismo), asegúrese de que CustomerId realmente tenga el valor cuando lo pase a la Vista.
- Usted dijo: "método mágico" UpdateModel que a veces funciona y otras veces no ". No es magia, puedes ver lo que hace y ciertamente encontrar información sobre ella. Algo está mal en la información que está publicando, mi apuesta es campos no opcionales o valores incorrectos para la información que se analiza ... las vistas admiten la adición de validaciones para eso. Sin las validaciones, esto puede faltar.
- Usted dijo en un comentario: "Después de que se llama a UpdateModel, no puedo establecer explícitamente CustomerId, tendré que recuperar un objeto del cliente y luego asignarlo al pedido, lo que parece una sobrecarga, ya que todo lo que necesito es CustomerId "... está aceptando un ID de cliente que es una entrada de usuario (incluso si es un campo oculto), realmente desea Validar esa entrada. Además, se está contradiciendo a sí mismo, afirma que solo necesita CustomerId, pero luego dice que necesita el objeto de cliente completo relacionado con el pedido. De lo que se trata, si solo está vinculando al CustomerId, debe obtenerlo y asignarlo a la propiedad. No hay magia además de las escenas ...
- También en un comentario: "El modelo de actualización es algo que estoy evitando completamente ahora, ya que no sé cómo se comportará con las entidades LINQ. En la clase de modelo de vista, he creado un constructor que convierte la entidad LINQ en mi modelo de vista. cantidad de código en el controlador, pero todavía no se siente bien ". La razón para usar ViewModel (o EditModel) no es porque sea linq2sql ... es porque, entre muchas otras razones, está exponiendo un modelo que permite manipular mucho más allá de lo que realmente desea permitir que el usuario modifique. Exponer el modelo en bruto, si tiene campos que el usuario no debería poder modificar, es el verdadero problema.
Si el ID del cliente ya está en el modelo de Pedido (en este ejemplo), debería estar disponible sin extender la firma del método. Si ve el origen en la vista representada, ¿la identificación del cliente se emite correctamente en un campo oculto dentro del formulario? ¿Está utilizando el atributo [Bind] en la clase de modelo de pedido e inadvertidamente impide que se llene el ID del cliente?
Si su vista está definida correctamente, puede hacerlo fácilmente>
[HttpPost]
public ActionResult Create(Order o, int CustomerId)
{
//you got the id, life back to jolly good (hopefully)
// Persistance logic and return view
}
EDITAR:
como se mencionó attadieni, con la vista correcta quise decir que tienes algo como esto dentro de la etiqueta del formulario>
@Html.HiddenFor(model => model.CustomerId)
ASP.NET MVC se vinculará automáticamente a los parámetros respectivos.
si está utilizando servicios (es decir, capa de servicio, fachada comercial), para procesar digamos el modelo de pedido, puede extraer una interfaz y obtener su ViewModel / DTO para implementarla, de modo que pueda devolver el ViewModel / DTO al Servicio.
Si está utilizando Repositorios para administrar directamente los datos (sin una capa de servicio) en el controlador, puede hacerlo de la manera antigua de cargar el objeto desde un repositorio y luego hacer un modelo de actualización en él.
[HttpPost]
public ActionResult Create(string customerCode, int customerId, Order order)
{
var cust = _customerRepository.Get(customerId);
cust.AddOrder(order);//this should carry the customerId to the order.CustomerId
}
Además, las URL pueden ayudar un poco donde tiene sentido, quiero decir que puede agregar el identificador del cliente en la url para crear el pedido.
UpdateModel debería funcionar, si su FormCollection tiene valores para propiedades que no admiten nulos y están vacíos / nulos en FormCollection, entonces UpdateModel debería fallar.