An entity object cannot be referenced by multiple instances of IEntityChangeTracker

Este error me apareció en una aplicación en la que tenía el siguiente esquema:

//En uno de mis repositorios
 public class ProductoRepository: IProductoRepository, IDisposable 
 {
    private MiContext context;
    //Constructor
    public ProductoRepository(MiContext context) 
    {
       this.context = context;
    }
    //… otros métodos aquí
 }
 //En otro de mis repositorios
 public class MarcaRepository: IMarcaRepository, IDisposable 
 {
    private MiContext context;
    //Constructor
    public MarcaRepository(MiContext context) 
    {
       this.context = context;
    }
    //… otros métodos aquí
 }
 //Y en mi controller:
 public IProductoRepository repositorioProducto;
 public IMarcaRepository repositorioMarca;
 //Constructor
 public MiController() 
 {
    this.repositorioProducto = new ProductoRepository(new MiContext());
    this.repositorioMarca = new MarcaRepository(new MiContext());
 }

En los repositorios todo está bien, porque en el constructor recibimos el “context” y lo asignamos a la variable local “context”, no hay problema ahí.

Sin embargo, checa el código del “controller”. Primero creo dos variables locales, cada una para cada repositorio, y después en el constructor las asigno a una nueva instancia de la clase de cada repositorio, y paso como parámetro una nueva instancia de “MiContext” a cada una.

Precisamente esto es lo que genera el error:

An entity object cannot be referenced by multiple instances of IEntityChangeTracker

Lo que tienes que hacer es crear una sola instancia de “MiContext” en el “controller” y luego pasar esa misma a los dos constructores de los repositorios, de esta forma:

//En el controller
 private MiContext context = new MiContext();
 public IProductoRepository repositorioProducto;
 public IMarcaRepository repositorioMarca;
 //Constructor
 public MiController()
 {
    this.repositorioProducto = new ProductoRepository(context);
    this.repositorioMarca = new MarcaRepository(context);
 }

De esta forma estarás trabajando con un sólo contexto y desaparecerá el error mencionado.

Cómo duplicar un objeto incluyendo objetos relacionados con Entity Framework

Partiendo del supuesto que tenemos un objeto que tiene objetos relacionados (hijos), lo podemos clonar incluyendo sus objetos relacionados.

Para hacerlo tenemos que usar “AsNoTracking” en “Entity Framework”, de esta manera:

var clon = context.Alumno
           .AsNoTracking()
           .Include(a => a.Asignaciones)
           .Include(a => a.Domicilios)
           .Single(a => a.AlumnoId == "5");

Como puedes ver, estamos encontrando el alumno que tenga un Id igual a 5, y estamos especificando que se incluyan sus objetos relacionados (hijos) de asignaciones y domicilios. Esto va a encontrar al alumno y guardarlo en la variable “clon”, la cual contendrá todos sus descendientes de asignaciones y domicilios.

Si no utilizas “AsNoTracking” entonces todo cambio que hagas en el objeto nuevo va a operar, obviamente, sobre el objeto original.

Al usar “AsNoTracking”, puedes hacer las modificaciones que gustes a “clon” y al final guardarlo. Cuando hagas esto se creará una copia nueva del objeto original, es decir, lo habrás duplicado.

Cómo visualizar el valor de EntityValidationErrors en Visual Studio

Quizás en algún desarrollo te has enfrentado a este error:

Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.

El error nos indica que tenemos que visualizar la colección “EntityValidationErrors” para encontrar los errores que está marcando nuestra aplicación.

La forma más sencilla de hacerlo es correr la aplicación en “Debug mode” en Visual Studio, y en la ventanita “Watch” añadir lo siguiente:

((System.Data.Entity.Validation.DbEntityValidationException) $exception).EntityValidationErrors

Y verás los errores de esta forma:

errorvalidation

Espero que te sirva.

Otro artículo relacionado.

Cómo escribir y leer valores en localStorage utilizando javaScript

“localStorage” es una mejora de las “Cookies”, te permite almacenar y leer información en el navegador, sin tener que enviarse necesariamente al servidor. Es muy útil si necesitas guardar datos que vas a estar utilizando continuamente, por ejemplo, con plugins de jQuery: “data grids”, selectores de opciones, guardar el estado de un menú, etc. El tamaño máximo que puedes guardar utilizando “localStorage” son 10MB.

Es muy fácil de utilizar, para crear una variable hazlo así:

localStorage.setItem("Numero", 11);
localStorage.setItem("Nombre", "Checo Pérez");

Si quieres saber si una variable ya está establecida, utiliza:

if (localStorage.getItem("Numero")) {
   //Haz algo aquí
}

Para obtener el valor de una variable:

var elNombre = localStorage.getItem("Nombre");
var elNumero = parseInt(localStorage.getItem("Numero"));

Para eliminar una variable:

localStorage.removeItem("Nombre");

Ahora bien, si estás usando objetos, entonces necesitas primero guardarlos como Json. Hazlo así:

var miObjeto = { Nombre: "Checo Pérez", Numero: 11, Pais: "México", Activo: true };
//Para colocar el objeto en localStorage
localStorage.setItem("Piloto", JSON.stringify(miObjeto));
//Para leer el objeto Json
var piloto = JSON.parse(localStorage.getItem("Piloto"));
//Para leer las propiedades:
var nombre = piloto.Nombre;
var numero = piloto.Numero;
var pais = piloto.Pais;
var activo = piloto.Activo;

Espero que te sirva.

Cómo enviar un valor temporal de una página a otra utilizando TempData

Tratando de utilizar “ViewBag” dentro de un “Controller”, tenía lo siguiente:

ViewBag.HayErrores = "verdadero";
return RedirectToAction("OtraAccion");

En la página “OtraAccion” el valor de “ViewBag.HayErrores” ya no existe.
Esto se debe a que los “ViewBag” no son persistentes al realizar la redirección.

Para utilizar un valor temporal que sobreviva a la redirección, debes utilizar “TempData”, para nuestro ejemplo quedaría así:

TempData["HayErrores"] = "verdadero";
return RedirectToAction("OtraAccion");

Y después en tu “View” puedes checar el valor utilizando “Razor”. Solamente tienes que checar primero que el valor no sea nulo, de esta forma:

var HayErrores = "";
if (TempData["HayErrores"] != null) 
{
   HayErrores = TempData["HayErrores"].ToString();
}
if (HayErrores == "verdadero") 
{
   <h3> Error </h3>
   Hay un error, por favor vuelve a intentarlo.
}