I have following Classes and interfaces..
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; }
}
For the above model it generates the following table structure
Book --> BookId , Title , Author
Pen --> 开发者_如何学JAVAPenId , Color
Tag --> TagId , Name ,BookBookId , PenPenId
And when i insert the following data
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();
It does not insert the Tag data for the second book
What i want to accomplish is that i want to implement a three table tagging system shown here adhering to my class structure
Yes it will not store the tag info for one of the books because your tag instance can be associated only with single book. That would require your many-to-many relation between Book
and Tag
but your relation is one-to-many. Relation between Pen
and Tag
is also one-to-many. That is clearly visible by foreign keys in Tag
table.
The problem is that ICollection<ITaggable> Items
is skipped by EF code first - code first doesn't work with interfaces. You must define your Tag as:
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; }
}
This will map many-to-many between Book
and Tag
and many-to-many between Pen
and Tag
. If you want also collection of ITaggable
you can expose another property concatenating Books
and Pens
.
If you really want one-to-many relation then you cannot expect that tag will be associated with multiple books and in such case your Tag
entity should look like:
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; }
}
Again you can create Items
as computed property. Any combination of relation should be obvious from these examples.
Edit:
If you don't want to expose navigation properties in Tag you must use 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();
}
}
Further fluent api reference can be found on ADO.NET team blog but it is not up to data (it is for CTP5).
精彩评论