Nebula
Nebula

Reputation: 1057

Entity framework single query insert child entity

I am learning about Entity Framework (EF) using code first on version 6+ with C# POCOs. Consider the following example:

public class DatabaseContext : DbContext
{
    public virtual DbSet<TextElement> Textuals { get; set; }
}

public class TextElement
{
    [Key]
    public int                              Number   { get; set; }
    public string                           Content  { get; set; }
    public virtual ICollection<TextElement> Children { get; set; }
}

What I want to do is to add a new child to an existing parent. in pseudo SQL I could do something like:

INSERT INTO Textuals (Content, ParentId) VALUES ("Lorem Ipsum", 42)

Where 42 is the ID of the parent record.

I've been searching the web for the past day to get EF to do the same for me in C# code. The best I could find is:

using (var context = new  DatabaseContext())
{
    var parent = new TextElement { Number = 42, Children = new List<TextElement>() };
    var child  = new TextElement { Content = "I iz kiddo" };
    context.Textuals.Attach(parent);
    parent.Children.Add(child);
    context.SaveChanges();
}

However, the Attach call seems to run a query too. The same issue applies to deleting an entity by id, that needs an Attach as well, running an unnecessary query.

Is there a better performing alternative to do what I want: add a child entity to an existing parent?

Update

Based on cverb's answer I got the following code doing what I want:

public class TextElement
{
    [Key]
    public int                              Number   { get; set; }
    public string                           Content  { get; set; }
    public virtual ICollection<TextElement> Children { get; set; }
    // Added custom parent id reference, MUST be nullable to allow 
    // for entries without parent
    public int?                             ParentId { get; set; }
}

Now, because this is a name that breaks convention (convention dictates TextElementId which is as vague as can be) I needed to change the Context with a little "fluent api" code. This explicitly defines the relations between the properties in the class.

public class DatabaseContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<TextElement>()
            .HasMany((e) => e.Children)        // Indicate the many relation
            .WithOptional()                    // Indicate that the many
                                               // relation is optional.
                                               // Don't require a nav prop
            .HasForeignKey((e) => e.ParentId); // The FK to use with the
                                               // relation.
    }

    public virtual DbSet<TextElement> Textuals { get; set; }
}

Which enables me to successfully apply Migrations as well.

Upvotes: 0

Views: 941

Answers (3)

cverb
cverb

Reputation: 643

You should add the ParentId explicitly on your TextElement entity.

public int? ParentId { get; set; }

If you don't know how to do that, there is some good information on this page under section "Understanding Convention for One to Many Relationships".

And then you can add a new entity by just using the following code.

using (var context = new  DatabaseContext())
{
    var child  = new TextElement { Content = "I iz kiddo", ParentId = 42 };
    context.SaveChanges();
}

No need for attaching or adding the child to the parent's Children-list, because Entity Framework will pick up the change itself.

Upvotes: 2

Yasser Shaikh
Yasser Shaikh

Reputation: 47774

Going by this

What I want to do is to add a new child to an existing parent

and this sql script you used

INSERT INTO Textuals (Content, ParentId) VALUES ("Lorem Ipsum", 42)

here is what should work for you.

using (var context = new  DatabaseContext())
{
    var newTextual  = new Textuals { Number = 42, Content = "I iz kiddo" };
    context.Textuals.Attach(newTextual);
    context.SaveChanges();
}

Upvotes: 0

lem2802
lem2802

Reputation: 1162

the way that you are using is the correct way... here, the EF context do all the job for you: start a transaction do the inserts edits and deletes needed, and finally commit the changes.

Upvotes: 0

Related Questions