Dans mon projet, j'utilise Entity Framework Core 2.0. Le code suivant est légèrement simplifié. Il existe de tels modèles:
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
{
}
J'utilise l'API Fluent pour configurer les relations:
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);
}
}
On dirait que la migration et la base de données sont créées correctement. Mais quand je crée un nouvel objet Site:
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();
la prochaine exception est levée "Impossible de convertir l'objet de type" ApplicationCore.Entities.AudioLink "en" ApplicationCore.Entities.VideoLink "."
Pourriez-vous dire ce que je fais mal?
L'héritage ne modifie pas la règle selon laquelle une propriété de navigation à référence unique ne peut être mappée qu'à une seule propriété de navigation de collection inverse.
En fait, ce qui se passe, c'est que le deuxième HasMany
/ WithOne
remplace le précédent (probablement un bogue, il devrait Link.Site
exception), de sorte que Link.Site
est mappé sur Site.AudioLinks
(le Site
n'est pas une propriété de l'entité de base Site
, donc partagé par AudioLink
et VideoLink
).
Vous devez donc soit supprimer les propriétés Site
et SiteId
de la classe de base et les placer dans les classes dérivées (qui introduiront 2 relations FK), soit utiliser de SiteId
propriété de navigation de collection unique du type de base:
public class Site
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Link> Links { get; set; }
}
et cartographie:
builder.Entity<Site>().HasMany(s => s.Links)
.WithOne(l => l.Site)
.HasForeignKey(l => l.SiteId);
Vous pouvez toujours utiliser l'opérateur OfType()
pour obtenir les AudioLinks
ou VideoLinks
partir des Links
de la requête LINQ to Entities ou LINQ to Objects après la matérialisation du Site
.