ASP.NET MVC / EF4 / POCO / Repository – ¿Cómo actualizar las relaciones?

Tengo una relación 1 … * entre Revisión y Recomendaciones .

La parte relevante de mi modelo (que también es el POCO mapeado por EF4):

public class Review { public ICollection Recommendations { get; set; } } 

En una vista de edición , represento las Recomendaciones como un conjunto de casillas de verificación.

Cuando trato de agregar una nueva Recomendación como parte de la edición de la Revisión (por ejemplo, marcar otra casilla), no ocurre nada, y sé por qué …

Utilizo la técnica “stub” para actualizar mis entidades, por ejemplo, creo una entidad con la misma clave, la adjunto al gráfico y luego hago ApplyCurrentValues . Pero esto solo funciona para propiedades escalares, no para propiedades de navegación.

Encontré esta pregunta de StackOverflow que se ve bien, pero estoy tratando de encontrar la manera de hacer que funcione con POCO’s / Repository (y ASP.NET MVC – context separado).

Como estoy utilizando POCO, review.Recommendations es una ICollection , por lo que no puedo hacer una review.Recommendations.Attach . Tampoco estoy usando entidades de seguimiento automático, así que tengo que trabajar manualmente con el seguimiento de gráficos / cambios, que hasta ahora no ha sido un problema.

Para que pueda visualizar el escenario:

Revisión:

  • Recomendaciones ( ICollection ):
    • Recommendation One ( Recommendation )
    • Recomendación dos ( Recommendation )

Si estoy en la vista de edición, dos de las casillas de verificación ya están marcadas. El tercero (que representa RecommendationThree) está desmarcado .

Pero si marqué esa casilla, el modelo anterior se convierte en:

Revisión:

  • Recomendaciones ( ICollection ):
    • Recommendation One ( Recommendation )
    • Recomendación dos ( Recommendation )
    • Recomendación tres ( Recommendation )

Y entonces necesito adjuntar RecommendationThree al gráfico como una nueva entidad .

¿Necesito campos ocultos para comparar los datos publicados con la entidad existente? ¿O debería almacenar la entidad en TempData y compararla con la entidad publicada?

EDITAR

Para evitar confusiones, aquí está la llamada completa a la stack de aplicaciones:

ReviewController

 [HttpPost] public ActionResult Edit(Review review) { _service.Update(review); // UserContentService _unitOfWork.Commit(); } 

UserContentService

 public void Update(TPost post) where TPost : Post, new() { _repository.Update(post); // GenericRepository } 

GenericRepository: utilizado como GenericRepository

 public void Update(T2 entity) where T2 : class, new() { // create stub entity based on entity key, attach to graph. // override scalar values CurrentContext.ApplyCurrentValues(CurrentEntitySet, entity); } 

Por lo tanto, es necesario invocar los métodos de Update (o Add o Delete ) del Repositorio para cada recomendación, dependiendo de si es nueva / modificada / eliminada.

Tal vez necesito más contexto, pero ¿qué está mal con:

 recommendations.Add(newRecomendation) 

?

En respuesta al comentario:

Bien, entonces ¿qué pasa con

 SomeServiceOrRepository.AddNewRecommendation( newRecommendation ) 

o

 SomeServiceOrRepository.AddNewRecommendation( int parentId, newRecommendation ) 

¿Última frase? ¿Te refieres a las dos preguntas?

Esto no debería ser difícil en absoluto.

Para resumir mi respuesta, creo que estás haciendo las cosas “de la manera difícil” y realmente debes enfocarte en publicar los valores de los formularios que corresponden a la acción CRUD que intentas lograr.

Si una nueva entidad pudiera ingresar al mismo tiempo que las entidades editadas, realmente debería prefijarlas de forma diferente para que la carpeta del modelo pueda retomarla. Incluso si tiene varios elementos nuevos, puede usar la misma syntax [0] simplemente prefija el campo “nombre” con Nuevo o algo.

Muchas veces en este escenario no puede confiar en las características del gráfico de Entity Frameworks porque eliminar una entidad de una colección nunca significa que deba establecerse para su eliminación.

Si el formulario es inmutable, también podría intentar usar la función de adjunción generizada fuera de ObjectSet:

 theContect.ObjectSet().Attach( review ) 

Un montón de formas de salir de esto. ¿Tal vez podría publicar su controlador y ver el código?

He aceptado la respuesta de @ jfar porque me puso en el camino correcto, pero pensé que agregaría una respuesta aquí para beneficio de otras personas.

La razón por la cual las relaciones no se actualizaban es por las siguientes razones:

1) Escenario completamente desconectado. ASP.NET = sin estado, el nuevo contexto creó cada solicitud HTTP.

2) entidad editada creada por MVC (vinculación del modelo), pero no existente en el gráfico.

3) Cuando se utiliza POCO sin seguimiento de cambios, la realización de .Attach en una entidad se agregará al gráfico, pero la entidad y las relaciones secundarias quedarán sin cambios.

4) Utilizo el truco de la entidad stub y ApplyCurrentValues para actualizar la entidad, pero esto solo funciona para propiedades escalares, no de navegación.

Entonces, para que todo lo anterior funcione, tendré que configurar explicitamente EntityState para el objeto (que ocurre automáticamente debido a ApplyCurrentValues ), y también las propiedades de navegación.

Y existe el problema: ¿cómo si se agregó / modificó / eliminó la propiedad de navegación? No tengo ningún objeto para comparar, solo una entidad que sé que fue “editada”, pero no sé qué fue editada.

Entonces la solución al final fue esta:

 [HttpPost] public ActionResult Edit(Review review) { var existingReview = _service.FindById(review.Id); // review is now in graph. TryUpdateModel(existingReview); // MVC equivalent of "ApplyCurrentValues" - but works for ALL properties - including navigationals _unitOfWork.Commit(); // save changed } 

Eso es. Ni siquiera necesito mi método _service.Update , ya que ya no necesito el truco stub porque la revisión está en el gráfico con la recuperación, y ApplyCurrentValues es reemplazado por TryUpdateModel .

Ahora, por supuesto, esta no es una solución a prueba de concurrencia .

Si cargo la vista de revisión de revisión y antes de hacer clic en “Enviar” alguien más cambia la revisión, mis cambios podrían perderse.

Afortunadamente tengo un modo de concurrencia “último en ganar”, así que no es un problema para mí.

Me encanta el POCO, pero ¿son un dolor cuando tienes la combinación de un entorno sin estado (MVC) y sin seguimiento de cambios?

Trabajar con gráficos de objetos separados es mi desventaja favorita de EF. Simplemente dolor en el culo. Primero debes lidiar con eso por tu cuenta. EF no te ayudará con eso. Significa que, además de la Review , también debe enviar información sobre los cambios realizados. Cuando adjunta Review al contexto, establece Review todas las Recommendation y todas las relaciones a Unchanged . ApplyCurrentValues funciona solo para valores escalares como ya has encontrado. Por lo tanto, debe usar su información adicional sobre los cambios realizados y establecer el estado de las relaciones a Added utilizando ObjectContext.ObjectStateManager.ChangeRelationshipState .

Yo personalmente me di por vencido con este enfoque y estoy cargando un gráfico de objeto de DB primero fusionando mis cambios en el gráfico adjunto y lo guardo.

Respondí una pregunta similar más profundamente aquí .