【问题标题】:Entity Framework Code first generating weird database schema when using interfaces实体框架代码在使用接口时首先生成奇怪的数据库模式
【发布时间】:2025-12-04 22:55:02
【问题描述】:

我有以下类和接口..

 public interface ITaggable
    {
         ICollection<Tag> Tags { get; set; }
    }

 public class Book :ITaggable
    {
        [DatabaseGenerated(DatabaseGenerationOption.Identity)] 
        public Guid BookId { get;set; }
        public string Title { get; set; }
        public string Author { get; set; }
        public virtual ICollection<Tag> Tags {get; set;}
    }

public class Pen:ITaggable
    {
        [DatabaseGenerated(DatabaseGenerationOption.Identity)]
        public Guid PenId { get; set; }
        public string Color { get; set; }
        public virtual ICollection<Tag> Tags {get; set;}
    }

public class Tag
    {
        [DatabaseGenerated(DatabaseGenerationOption.Identity)] 
        public Guid TagId { get; set; }
        public string Name { get; set; }
        public virtual ICollection<ITaggable> Items { get; set; }
    }

对于上述模型,它会生成如下表结构

书 --> BookId , 书名 , 作者

笔 --> 笔号,颜色

标签 --> TagId , 姓名 ,BookBookId , PenPenId

当我插入以下数据时

            Tag tag = new Tag();
            tag.Name = "Stationary";

            Book b1 = new Book();
            b1.Title = "Head first c#";
            b1.Author = "Betty";
            b1.Tags = new List<Tag>() { tag };

            Book b2 = new Book();
            b2.Title = "Head first Java";
            b2.Author = "Katty";
            b2.Tags = new List<Tag>() { tag };

            Pen p = new Pen();
            p.Color = "Red";
            p.Tags = new List<Tag>() { tag };

            context.Books.Add(b1);
            context.Books.Add(b2);
            context.Pens.Add(p);
            context.SaveChanges();

它不插入第二本书的标签数据

我想要实现的是我想要实现一个三表标记系统 shown here坚持我的班级结构

【问题讨论】:

    标签: interface mapping associations ef-code-first entity-framework-4.1


    【解决方案1】:

    是的,它不会存储其中一本书的标签信息,因为您的标签实例只能与一本书相关联。这需要BookTag 之间的多对多关系,但你的关系是一对多的。 PenTag 之间的关系也是一对多的。 Tag 表中的外键可以清楚地看到这一点。

    问题是 ICollection&lt;ITaggable&gt; Items 被 EF 代码优先跳过 - 代码优先不适用于接口。您必须将您的标签定义为:

    public class Tag
    {
        [DatabaseGenerated(DatabaseGenerationOption.Identity)] 
        public Guid TagId { get; set; }
        public string Name { get; set; }
        public virtual ICollection<Book> Books { get; set; }
        public virtual ICollection<Pen> Pens { get; set; }
    }
    

    这将映射BookTag 之间的多对多以及PenTag 之间的多对多。如果您还想收集ITaggable,您可以公开连接BooksPens 的另一个属性。

    如果您真的想要一对多的关系,那么您不能期望该标签会与多本书相关联,在这种情况下,您的 Tag 实体应该如下所示:

    public class Tag
    {
        [DatabaseGenerated(DatabaseGenerationOption.Identity)] 
        public Guid TagId { get; set; }
        public string Name { get; set; }
        public virtual Book Book { get; set; }
        public virtual Pen Pen { get; set; }
    }
    

    您可以再次创建Items 作为计算属性。从这些示例中可以看出任何关系的组合。

    编辑:

    如果您不想在 Tag 中公开导航属性,则必须使用 fluent-api:

    public class Context : DbContext
    {
        public DbSet<Book> Books { get; set; }
        public DbSet<Pen> Pens { get; set; }
        public DbSet<Tag> Tags { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
            modelBuilder.Entity<Book>()
                        .HasMany(b => b.Tags)
                        .WithMany();
    
            modelBuilder.Entity<Pen>()
                        .HasMany(p => p.Tags)
                        .WithMany();
        }
    }
    

    更多流利的api参考可以在ADO.NET team blog找到,但它不取决于数据(它是针对CTP5的)。

    【讨论】:

    • 所以对于我在系统中添加的每个类(需要标记),我必须在我的标记类中添加 (public virtual ICollection NewClasses {get;set;})..i认为这将是一个不太好的设计....还有其他解决方法
    • 我可以使用基类而不是接口
    • 如果您想从Tag 访问标记类,则没有其他解决方案。您可以完全忽略Tag 中的这些导航属性,但您必须使用 fluent api 来映射您的关系,并且您将无法从Tag 访问相关实体。基类将向您的模型引入继承映射,从而导致其他后果。
    • 好的..你能给我任何关于 flent api 链接的参考,我可以在其中找到类似的解决方案...
    • 你的表现在是如何定义的?