Pregunta

Estoy usando el "técnica de stub" a actualizar Mi POCO (utilizado en un contexto separado, ASP.NET MVC).

Este es el código que tengo actualmente en mi controlador (que funciona):

[HttpPost]
public ActionResult Edit(Review review)
{
   Review originalReview = _userContentService.FindById(review.PostId) as Review;
   var ctx = _unitOfWork as MySqlServerObjectContext;
   ctx.ApplyCurrentValues("MyEntities.Posts", review);
   _unitOfWork.Commit();
   // ..snip - MVC stuff..
}

Como puede ver, hay un olor en código en todas partes. :)

Algunos puntos:

  1. Utilizo la inyección de dependencia (basada en la interfaz) básicamente para todo
  2. Utilizo el patrón de la unidad de trabajo para abstracto ObjectContext y proporcione persistencia en múltiples repositorios
  3. Actualmente mi Iunitofwork La interfaz tiene solo 1 método: void Commit();
  4. Controlador tiene IUserContentService y IUnitOfWork inyección por DI
  5. IUserContentService llamadas Find en repositorios, que usan el ObjectContext.

Estas son dos cosas que no me gustan con mi código anterior:

  1. No quiero lanzar el Iunitofwork como MySqlServerObjectContext.
  2. No quiero que el controlador tenga que preocuparse ApplyCurrentValues

Básicamente quiero que mi código se vea así:

[HttpPost]
public ActionResult Edit(Review review)
{
   _userContentService.Update(review);
   _unitOfWork.Commit();
   // ..snip - MVC stuff..
}

¿Alguna idea de cómo puedo hacer eso? (o algo similar).

Ya tengo inteligencia para resolver el nombre del conjunto de entidades basado en el tipo (combinación de genéricos, pluralización), así que no se preocupe demasiado por eso.

Pero Me pregunto dónde el mejor lugar para poner ApplyCurrentValues es? No parece apropiado ponerlo en el IUnitOfWork interfaz, ya que esta es una preocupación de persistencia (EF). Por la misma razón, no pertenece al servicio. Si lo pongo en mi MySqlServerObjectContext clase (tiene sentido), de dónde llamaría a esto, ya que nada directamente tiene acceso a esta clase: se inyecta a través de DI cuando algo solicita IUnitOfWork.

¿Alguna idea?

EDITAR

Tengo una solución a continuación utilizando la técnica Stub, pero el problema es que si hubiera recuperado la entidad que estoy actualizando de antemano, lanza una excepción, indicando una entidad con esa llave ya existe.

¿Qué tiene sentido, aunque no estoy seguro de cómo puede resolver esto?

¿Necesito "comprobar si la entidad ya está adjunta, si no, adjuntarla?"

¿Puede ayudar algún experto de EF4?

EDITAR

No importa: encontré la solución, vea la respuesta a continuación.

¿Fue útil?

Solución

Lo descubrí, no fue fácil, así que intentaré explicar mejor que pueda. (para aquellos a quienes les importa)

Código relevante del controlador:

// _userContentService is IUserContentService
_userContentService.Update(review);

Entonces, mi controlador llama a un método llamado Update en IUserContentService, pasando por el fuertemente tipo Review objeto.

Servicio de contenido de usuario Código relevante

public void Update(Post post)
{
   // _userContentRepository is IPostRepository
   _userContentRepository.UpdateModel(post);
}

Entonces, mi servicio llama a un método llamado UpdateModel en IPostRepository, pasando por el fuertemente tipo Review objeto.

Ahora esta es la parte difícil.

Realmente tengo Sin repositorios específicos. tengo un repositorio genérico llamó GenericRepository<T> : IRepository<T>, que maneja Todos los diferentes repositorios.

Entonces, cuando algo solicita un IPostRepository (que estaba haciendo mi servicio), Di le daría un GenericRepository<Post>.

Pero ahora, le doy un PostRepository:

public class PostRepository : GenericRepository<Post>, IPostRepository
{
   public void UpdateModel(Post post)
   {
      var originalPost = CurrentEntitySet.SingleOrDefault(p => p.PostId == post.PostId);
      Context.ApplyCurrentValues(GetEntityName<Post>(), post);
   }
}

Y porque la clase derivarse de GenicRepository, hereda toda la lógica del repositorio central (encontrar, agregar, etc.).

Al principio, intenté poner eso UpdateModelo código en el GenicRepository clase en sí (y luego no hubiera necesitado este repositorio específico), pero el problema es la lógica para recuperar la entidad existente se basa en una clave de entidad específica, que el GenericRepository<T> no sabría sobre.

Pero el resultado final es el puntadas está oculto en las profundidades de la capa de datos, y termino con un controlador realmente limpio.

EDITAR

Esta "técnica de stub" también funciona:

public void UpdateModel(Post post)
{
   var stub = new Review {PostId = post.PostId};
   CurrentEntitySet.Attach(stub);
   Context.ApplyCurrentValues(GetEntityName<Post>(), post);
}

Pero el problema es porque Correo es abstracto, no puedo instanciar y, por lo tanto, tendría que verificar el tipo de publicación y crear trozos para cada uno tipo derivado. Realmente no es una opción.

Editar 2 (última vez)

De acuerdo, obtuve la "técnica de talón" que funcione con clases abstractas, por lo que ahora se resuelve el problema de concurrencia.

Agregué un parámetro de tipo genérico a mi UpdateModelo método y el especial nueva () restricción.

Implementación:

public void UpdateModel<T>(T post) where T : Post, new()
{
   var stub = new T { PostId = post.PostId };
   CurrentEntitySet.Attach(stub);
   Context.ApplyCurrentValues(GetEntityName<Post>, post);
}

Interfaz:

void UpdateModel<T>(T post) where T : Post, new();

Esto me impide tener que descubrir el tipo de T manualmente, previene problemas de concurrencia y también evita un viaje adicional al DB.

Bastante maravilloso.

Editar 3 (pensé que la última vez fue la última vez)

La "técnica de tallo" anterior funciona, pero si recupero el objeto de antemano, lanza una excepción que indica una entidad con esa llave ya existe en el OSM.

¿Alguien puede aconsejar cómo manejar esto?

Editar 4 (OK, ¡esto es todo!)

Encontré la solución, gracias a esto, así que respuesta: ¿Es posible verificar si un objeto ya está conectado a un contexto de datos en el marco de entidad?

Intenté "verificar si la entidad está adjunta" usando el siguiente código:

ObjectStateEntry entry;
CurrentContext.ObjectStateManager.TryGetObjectStateEntry(entity, out entry);

Pero siempre volvió nulo, incluso a través de cuando exploré el OSM, pude ver mi entidad allí con la misma clave.

Pero este código funciona:

CurrentContext.ObjectStateManager.TryGetObjectStateEntry(CurrentContext.CreateEntityKey(CurrentContext.GetEntityName<T>(), entity), out entry)

Tal vez porque estoy usando puro Poco, la OSM tuvo problemas para descubrir la clave de la entidad, quién sabe.

Ah, y otra cosa que agregué, para que no tenga que agregar un repositorio específico para cada entidad, creé un atributo llamado "EntityKey"(atributo de propiedad pública).

Todos los POCO deben tener 1 propiedad pública decorada con ese atributo, o arrojo una excepción en mi módulo de repositorio.

Entonces, mi repositorio genérico busca esta propiedad para crear/configurar el stub.

Sí, usa la reflexión, pero es una reflexión inteligente (basada en atributos) y ya estoy usando la reflexión para la plularización de los nombres de los conjuntos de entidades de T.

De todos modos, problema resuelto, ¡todo funcionan bien ahora!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top