En mi proyecto utilizo Entity Framework Core 2.0. El siguiente código está ligeramente simplificado. Hay tales modelos:
public class Site
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<AudioLink> AudioLinks { get; set; }
public ICollection<VideoLink> VideoLinks { get; set; }
}
public abstract class Link
{
public int Id { get; set; }
public string Href { get; set; }
public int SiteId { get; set; }
public Site Site { get; set; }
}
public class AudioLink : Link
{
}
public class VideoLink : Link
{
}
Utilizo Fluent API para configurar relaciones:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Site> Sites { get; set; }
public DbSet<AudioLink> AudioLinks { get; set; }
public DbSet<VideoLink> VideoLinks { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Link>()
.HasDiscriminator<byte>("LinkType")
.HasValue<AudioLink>(1)
.HasValue<VideoLink>(2);
builder.Entity<Site >().HasMany(s => s.VideoLinks)
.WithOne(l => l.Site)
.HasForeignKey(l => l.SiteId);
builder.Entity<Site >().HasMany(s => s.AudioLinks)
.WithOne(l => l.Site)
.HasForeignKey(l => l.SiteId);
}
}
Parece que la migración y la base de datos se crean correctamente. Pero cuando creo un nuevo objeto de sitio:
var site = new Site(){Id = 1, Name = "SiteA"}
var audioLink = new AudioLink(){Id = 1, Href = "abc", Site = site};
context.Sites.Add(site);
context.SaveChanges();
context.AudioLinks.Add(audioLink);// exception occurs
context.SaveChanges();
se lanza la siguiente excepción "No se puede convertir el objeto de tipo 'ApplicationCore.Entities.AudioLink' para escribir 'ApplicationCore.Entities.VideoLink'".
¿Podrías decir qué estoy haciendo mal?
La herencia no cambia la regla de que una propiedad de navegación de referencia única se puede asignar solo a una propiedad de navegación de colección inversa única.
De hecho, lo que está sucediendo es que el segundo HasMany
/ WithOne
reemplaza al anterior (probablemente un error, debería lanzar una excepción), por lo que el sitio Link.Site
se asigna al Site.AudioLinks
. Site.AudioLinks
(no es que el Site
sea propiedad de la entidad base Site
, por lo tanto es compartido por AudioLink
y VideoLink
).
Por lo tanto, debe eliminar las propiedades Site
y SiteId
de la clase base y colocarlas en las clases derivadas (que introducirán 2 relaciones FK), o mejor usar una propiedad de navegación de colección única del tipo base:
public class Site
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Link> Links { get; set; }
}
y mapeo:
builder.Entity<Site>().HasMany(s => s.Links)
.WithOne(l => l.Site)
.HasForeignKey(l => l.SiteId);
Siempre puede usar el operador OfType()
para obtener los AudioLinks
o VideoLinks
de VideoLinks
de los Links
en la consulta de LINQ a entidades o LINQ a objetos después de la materialización del Site
.