c# - Se produjo un error al guardar entidades que no exponen propiedades de claves foráneas para sus relaciones
entity-framework ef-code-first (14)
Primero tengo un código simple en el código de Entity Framework 4.1
:
PasmISOContext db = new PasmISOContext();
var user = new User();
user.CreationDate = DateTime.Now;
user.LastActivityDate = DateTime.Now;
user.LastLoginDate = DateTime.Now;
db.Users.Add(user);
db.SaveChanges();
user.Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") };
db.SaveChanges();
db.Users.Add(new User() { Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") } });
db.SaveChanges();
El problema es que me sale un error
Se produjo un error al guardar entidades que no exponen propiedades de claves foráneas para sus relaciones. La propiedad EntityEntries devolverá null porque no se puede identificar una sola entidad como el origen de la excepción. El manejo de excepciones al guardar puede ser más fácil al exponer propiedades de clave foránea en sus tipos de entidad. Vea InnerException para más detalles.
a
db.Users.Add(new User() { Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") } });
db.SaveChanges();
No entiendo por qué funciona la operación similar. ¿Hay algún problema con mi modelo o con ef-code-first?
public class Avatar
{
[Key]
public int Id { get; set; }
[Required]
public string LinkInString { get; set; }
[NotMapped]
public Uri Link
{
get { return new Uri(LinkInString); }
set { LinkInString = value.AbsoluteUri; }
}
}
public class User
{
[Key]
public int Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public Avatar Avatar { get; set; }
public virtual ICollection<Question> Questions { get; set; }
public virtual ICollection<Achievement> Achievements { get; set; }
public DateTime CreationDate { get; set; }
public DateTime LastLoginDate { get; set; }
public DateTime LastActivityDate { get; set; }
}
El problema se resolvió agregando una propiedad FK.
En mi caso, el problema fue que cambié el nombre de una columna de forma incorrecta, por lo que la migración formó dos columnas, una llamada "ID de equipo" y otra denominada "ID de equipo". C # se preocupa, SQL no.
En mi caso, la excepción fue lanzada porque EF había creado una migración incorrectamente. Se olvidó de establecer la identidad: verdadero en la segunda mesa. Así que entre en las migraciones que crearon las tablas relevantes y verifique si se perdió para agregar identidad.
CreateTable(
"dbo.LogEmailAddressStats",
c => new
{
Id = c.Int(nullable: false, identity: true),
EmailAddress = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.LogEmailAddressStatsFails",
c => new
{
Id = c.Int(nullable: false), // EF missed to set identity: true!!
Timestamp = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.LogEmailAddressStats", t => t.Id)
.Index(t => t.Id);
Una columna Id debe tener identidad (es decir, autoincrementar!) Por lo que debe ser un error EF.
Puede agregar identidad manualmente con SQL directamente a la base de datos, pero prefiero usar Entity Framework.
Si te encuentras con el mismo problema, veo dos soluciones fáciles :
Alt 1
revertir la migración creada incorrectamente con
update-database -target:{insert the name of the previous migration}
A continuación, agregue la identidad: true manualmente al código de migración y luego actualice la base de datos nuevamente.
Alt 2
usted crea una nueva migración que agrega identidad. Si no tienes cambios en los modelos y corres
add-migration identity_fix
creará una migración vacía. Entonces solo agrega esto
public partial class identity_fix : DbMigration
{
public override void Up()
{
AlterColumn("dbo.LogEmailAddressStatsFails", "Id", c => c.Int(nullable: false, identity: true));
}
public override void Down()
{
AlterColumn("dbo.LogEmailAddressStatsFails", "Id", c => c.Int(nullable: false));
}
}
En mi caso, la siguiente situación me estaba dando la misma excepción:
Imagina un primer modelo de código de EF donde tienes una entidad de Garage
que tiene una colección de entidades de Car
. Necesitaba sacar un auto del garaje, así que terminé con un código que se veía así:
garageEntity.Cars.Remove(carEntity);
En cambio, debería haberse visto así:
context.Cars.Remove(carEntity);
Este mensaje de error puede ser lanzado por cualquier tipo de razón. La propiedad ''InnerException'' (o su InnerException, o InnerException de eso, etc.) contiene la causa principal real del problema.
Por supuesto, sería útil saber algo sobre dónde ocurrió el problema: ¿qué objeto (s) en la unidad de trabajo está causando el problema? El mensaje de excepción normalmente le indicaría en la propiedad ''EntityEntries'', pero en este caso, por alguna razón, eso no se puede hacer. Esta complicación diagnóstica, de que la propiedad ''EntityEntries'' está vacía, es aparentemente porque algunas Entidades ''no exponen propiedades de claves foráneas para sus relaciones''.
Incluso si el OP obtiene el error porque no inicializó DateTime
para la segunda instancia de User
, obtiene la complicación de diagnóstico: ''EntityEntries'' está vacío y un confuso mensaje de alto nivel ... porque uno de sus Entity no funciona. t ''exponer propiedades de clave externa''. Para solucionar esto, Avatar
debería tener una public virtual ICollection<User> Users { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<User> Users { get; set; }
propiedad definida.
Este problema también puede surgir de declaraciones clave invertidas. Si usa fluidez para configurar la relación, asegúrese de que las teclas izquierda y derecha estén asignadas a la entidad correcta.
Hoy me enfrenté a este problema y probé las posibles soluciones publicadas anteriormente, pero ninguno de ellos me ayudó. Tuve el patrón UnitOfWork
implementado y el sistema estaba UnitOfWork
los datos después de agregar todos los registros.
En mi caso, el sistema combinaba los dos modelos y consultaba el DB
Nombre de objeto inválido ''dbo.RoleModelUserModel''.
donde estos eran dos modelos diferentes en realidad.
Lo arreglé reordenando las instrucciones de inserción y agregando primero la entidad padre. En este caso, se agregó al usuario primero y se resuelve el problema.
No estoy del todo seguro de que sea útil en su caso porque estoy configurando mis tablas con la API Fluent, sin embargo, en lo que puedo decir, el problema surge independientemente de si el esquema está configurado usando anotaciones de datos (atributos) o Fluent API (configuración).
Parece que hay un error en EF (v. 6.1.3) ya que omite ciertos cambios al esquema al actualizar el DB a la siguiente migración. La ruta más rápida a su alrededor es (durante la etapa de desarrollo) para eliminar de nuevo todas las tablas de las migraciones DB y Runt de la etapa init.
Si ya está en producción, la solución más rápida que he encontrado es cambiar manualmente el esquema en la base de datos o, si desea tener control de versiones de los cambios, manipular manualmente los métodos Arriba () y Abajo () en su migración.
Otra respuesta:
Usé esto:
public List<EdiSegment> EdiSegments { get; set; }
en lugar de esto:
public virtual ICollection<EdiSegment> EdiSegments { get; set; }
y obtuve el mensaje de error mencionado anteriormente.
Otro caso diferente aquí. Se lanzó una consulta a una lista y, al hacerlo, creó entidades por su constructor para su comparación en la expresión linq justo después de ToList (). Esto creó entidades que pasaron al estado eliminado después de que la expresión linq finalizó.
¡Sin embargo! Hubo un pequeño ajuste que creó otra entidad en el constructor, de modo que esta nueva entidad se vinculó a una entidad que se marcó como Eliminada.
Algunos códigos para ilustrar:
query.Except(_context.MyEntitySetSet()
.Include(b => b.SomeEntity)
.Where(p => Condition)
.ToList() // This right here calls the constructor for the remaining entities after the where
.Where(p => p.Collection.First(b => Condition).Value == 0)
.ToList();
El constructor de MyEntity:
public partial class MyEntity
{
protected MyEntity()
{
// This makes the entities connected though, this instance of MyEntity will be deleted afterwards, the instance of MyEntityResult will not.
MyEntityResult = new MyEntityResult(this);
}
}
Mi solución fue asegurarme de que toda la expresión se realizara dentro de IQueryable para que no se creen objetos.
Para aquellos de ustedes que todavía tendrían este error con todas las claves correctamente definidas, eche un vistazo a sus entidades y asegúrese de no dejar un campo de fecha y hora con un valor nulo.
Solo para otros que puedan tener problemas similares. Tuve el mismo error, pero por una razón diferente. En uno de los objetos secundarios definí la [Clave] como un valor que era el mismo para diferentes guardados. Un error estúpido de mi parte, pero el mensaje de error no te lleva instantáneamente al problema.
Tuve el mismo error y en mi caso el problema fue que agregué un objeto de relación que ya había sido cargado como "AsNoTracking". Tuve que volver a cargar la propiedad de relación.
Por cierto, algunos sugieren usar "Adjuntar" para las relaciones que ya existen en db, aunque no he probado esa opción.
Tuve el mismo problema. en mi caso, fue debido al campo de fecha y hora con un valor nulo. Tuve que pasar un valor a datetime y evrythings fue bien