Adjuntar una entidad EF4 a un contexto más de una vez

Así que estoy trabajando en una aplicación existente que almacena una entidad en una variable de sesión y cada devolución de datos intenta volver a adjuntarla a un nuevo contexto. No me importa especialmente esta implementación, pero volver a escribirla está fuera de discusión por ahora. El código se parece a esto:

public partial class ReviewDetail
{
    DBContext context = new DBContext();

    public void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            int id = Convert.ToInt32(Request.QueryString["id"]);
            Session["Review"] = context.Reviews.FirstOrDefault(x => x.ReviewID = id);
        }
        else
        {
            Review review = Session["Review"] as Review;
            context.Attach(review);
        }
    }
}

Esto funciona tanto en la carga de la página inicial como en la PRIMERA devolución de datos. Sin embargo, en las devoluciones posteriores, falla con el siguiente error:

Un objeto con un valor EntityKey temporal no se puede adjuntar a un contexto de objeto.

Lo extraño (para mí, de todos modos) es que cuando paso por el código en el depurador de VS y examino las propiedades de review.EntityKey, el campo IsTemporary es falso.

¿Alguna sugerencia sobre cómo debo abordar una solución para esto? Intenté llamar a .AttachTo("Reseñas", revisión) pero termino con el mismo error.

Lo que creo que sé hasta ahora es que cuando realizo la primera llamada .Adjuntar, adjunta el objeto "revisar", pero dado que es un objeto de referencia, también modifica el objeto en la Sesión y le agrega una EntityKey (puede que ser incorrecto en mi terminología o mi comprensión aquí; esta es un área un poco débil en EF para mí). Así que ahora, después de la primera devolución de datos, mi objeto de sesión se ha "adjuntado". La segunda vez (cuando falla), ya se adjuntó, ¿entonces está fallando?

Creo que estoy en el camino correcto, solo que no estoy seguro de cómo abordar una solución desde aquí. Idealmente, no lo haríamos de esta manera, pero reescribirlo para usar un enfoque más correcto probablemente no sucederá aquí a menos que no haya otras alternativas.

Cualquier sugerencia es apreciada

EDIT: Debería haber mencionado esto antes de que alguien respondiera con él, pero no puedo volver a la base de datos en cada devolución de datos para obtener la revisión. El objeto en sesión se modifica más adelante y sus datos pueden ser diferentes a los de la base de datos.

preguntado el 11 de junio de 12 a las 19:06

2 Respuestas

Tomaría la entidad de su variable de sesión y su propiedad de ID para volver a consultar el contexto, como:

public partial class ReviewDetail
{
    DBContext context = new DBContext();

    public void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            int id = Convert.ToInt32(Request.QueryString["id"]);
            Session["Review"] = context.Reviews.FirstOrDefault(x => x.ReviewID = id);
        }
        else
        {
            Review review = Session["Review"] as Review;

            Review updateReviewFromContext = context.ApplyCurrentValues<Review>("Reviews", review);         
        }
    }
}

Es un cambio simple y hace que sea un poco irrelevante que esté almacenando todo el objeto en la sesión, pero si no puede cambiar eso, esto debería ser suficiente.

ACTUALIZACIÓN

Ver: http://msdn.microsoft.com/en-us/library/dd487246

La ApplyCurrentValues tomará su actualización Review de la sesión y aplica sus propiedades actualizadas a la entidad que se devuelve desde el contexto. El único truco es que tienes que saber cuál es el EntitySetName es, en mi ejemplo "Reseñas", pero siempre que eso no cambie, puede codificarse de forma rígida.

Respondido el 11 de junio de 12 a las 19:06

Debería haber mencionado en mi publicación original que no puedo hacer eso. Hay una razón específica por la que lo almacenamos en sesión y el objeto en sesión difiere del objeto en la base de datos. De hecho, lo intenté antes de darme cuenta de que había una razón por la que lo hacían de esta manera. :) - Scott

Entonces, ¿es este el caso donde hay cambios en la versión de la sesión, y si los obtiene de la base de datos, perderá esos cambios? - CodificaciónGorilla

Correcto. Los datos modificados se mantienen en el objeto de la sesión hasta que se hace clic en el botón "Guardar". Podría haber varias devoluciones de datos entre Page_Load y el evento de clic de botón. La aplicación es mucho más compleja de lo que estoy mostrando a través de este ejemplo, he escrito lo suficiente para dar una idea del problema. - Scott

Estos problemas suelen ser el resultado de cómo funciona Entity Framework al adjuntar objetos. Cuando adjunta una entidad, está adjuntando recursivamente cada entidad a la que hace referencia. Las excepciones son engañosas porque, naturalmente, asumimos que el error está en la entidad que estamos adjuntando explícitamente, cuando en muchos casos el problema está en una entidad que se está adjuntando implícitamente.

Por lo tanto, no estoy seguro de cuán abreviado es su fragmento de código, pero parece que puede estar intentando adjuntar una entidad que hace referencia a una entidad recién creada.

Respondido el 11 de junio de 12 a las 20:06

No creo que ese sea el caso aquí. Solo porque puedo cargar una nueva página nueva, causar una devolución de datos simple (que no crea ninguna entidad nueva), luego hacer una devolución de datos más del mismo tipo (nuevamente, sin entidades nuevas) y arroja el error. - Scott

Que pasa si cambias context.Reviews.FirstOrDefault(x => x.ReviewID = id); a context.Reviews.Find(id);? - w.brian

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.