Entity Framework 6 and collections

c# entity-framework

Question

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)

SOLUTION:

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.

1
5
12/17/2013 11:44:58 PM

Accepted Answer

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();
2
12/17/2013 5:15:51 AM

Popular Answer

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.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow