是否可以在一个实体中具有相同基类(TPH)的实体的多个集合属性?

时间:2018-05-06 20:39:32

标签: c# entity-framework-core

在我的项目中,我使用Entity Framework核心2.0。 下一个代码略有简化。有这样的模型:

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
{
}

我使用Fluent API配置关系:

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);
    }
}

看起来像是正确创建了迁移和数据库。 但是当我创建新的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();

抛出下一个异常“无法将'ApplicationCore.Entities.AudioLink'类型的对象强制转换为'ApplicationCore.Entities.VideoLink'。”

你能说出我做错了什么吗?

1 个答案:

答案 0 :(得分:4)

继承不会更改单个引用导航属性只能映射到单个反向集合导航属性的规则。

实际上发生的事情是第二个HasMany / WithOne覆盖了前一个(可能是一个bug,应该抛出异常),因此Link.Site被映射到{{ 1}}(不是Site.AudioLinks是基本实体Site的属性,因此由SiteAudioLink共享。

因此,您必须从基类中删除VideoLinkSite属性并将它们放在派生类中(这将引入2个FK关系),或者更好地使用单个集合导航属性基本类型:

SiteId

和映射:

public class Site
{
    public int Id { get; set; } 
    public string Name { get; set; }
    public ICollection<Link> Links { get; set; }
}

您可以始终使用builder.Entity<Site>().HasMany(s => s.Links) .WithOne(l => l.Site) .HasForeignKey(l => l.SiteId); 运算符在OfType()之后的LINQ to Entities查询或LINQ to Objects中从AudioLinks获取VideoLinksLinks物化。