I'm developing my first entity-framework app. I'm using EF vesion 6 (from Nuget) and .net 4.0. However, I'm having some difficulty with something that, to me, seems like it should be extraordinarily simple. I've found lots of conflicting advice and solutions on the internet, but after spending several days trying to work things out, I'm really confused and am to the point of questioning some basic understandings I have of the Entity Framework. What I want to do is this: create a simple collection of related entities, and automatically delete them when they're removed from the parent.
Here's how I would model this in vanilla C#. In keeping with Microsoft samples, assume we have two classes, Post and Tag, like so:
public class Post
{
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public string Text { get; set; }
// Possibly other properties here
}
Then, adding a tag is as simple as myPost.Tags.Add(myTag)
and removing a tag is as simple as myPost.Tags.Remove(myTag)
.
Now to the Entity Framework side of things: I looked at that and thought "Foreign Key, of course!" but I had a host of issues adding a FK: Tags wouldn't delete from the database when they were removed from the post, myPost.Tags
would have 0 elements when loaded from the db, despite the SQL explorer showing that the PostId value was correct, etc. I faffled around with a bunch of tricks, like marking the Tag.PostId
as a Key, manually removing Tags, actually adding Tag to the context as a DbSet, manually setting myTag.Post = null;
(I tried with Lazy loading both enabled and disabled, for what it's worth - though I'd like to keep it off if possible)
Now (thanks in no small part to seemingly conflicted and overly-complicated examples), I'm quite confused and lost. Can someone tell me exactly how I should go about setting this relationship up in EF? (I'm using Code First, by the way)
Thanks to Moho, I've come up with this structure, which does exactly what I want:
public class Post
{
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public Post()
{
Tags = new HashSet<Tag>();
}
}
public class Tag
{
[Key, Column(Order=1), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Text { get; set; }
// Possibly other properties here
public virtual Post Post { get; set; }
[Key, Column(Order=2)]
public virtual int PostId { get; set; }
}
public class TestContext : DbContext
{
public DbSet<Post> Posts { get; set; }
}
When a Tag
is removed from a Post
's Tags collection, Entity Framework will issue a DELETE for the tag, as outlined here (#2): http://www.kianryan.co.uk/2013/03/orphaned-child/
Likewise, adding a tag to a post will automatically issue an INSERT and set the FK relationship.
One thing to note: Make sure you use virtual
! I think that was the root of a lot of my frustration as well.
EF5 is built into the core of . NET 4.5, whereas EF6 has been shifted out, and is open source. This means that you must add the new EF6 assemblies to all of the relevant projects in the solution, in particular the entry project. This means that you must remove assembly System.
EF Core now supports: The creation of temporal tables using Migrations. Transformation of existing tables into temporal tables, again using Migrations. Querying historical data.
There are three approaches to model your entities in Entity Framework: Code First, Model First, and Database First. This article discusses all these three approaches and their pros and cons.
You can create such a relationship by defining a third table, called a junction table, whose primary key consists of the foreign keys from both table A and table B.
Try also defining the relationship from the Tag
side as well, specifying each Tag
relates to a single Post
and is required.
Add a required navigation property to Post
in Tag
:
public class Tag
{
// you need an ID
public int Id { get; set; }
public string Text { get; set; }
[Required]
public virtual Post Post { get; set; }
}
Alternatively, if you really don't want to add the navigation property, you can use the Fluent API:
modelBuilder.Entity<Post>().HasMany( p => p.Tags ).WithRequired();
This is probably thread necromancy, but I don't have the rep to just comment. Wouldn't the following do what you need?
public class Tag
{
[Key, Column(Order=1), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Text { get; set; }
// Possibly other properties here
public int PostId { get; set; }
[ForeignKey("PostId")]
public virtual Post Post { get; set; }
}
PostId is loaded from the database, then used as the Foreign Key (via the annotation) into the Post class. Post is virtual, nothing else is. You could probably just go with the basic [Key] annotation too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With