The key methods for adding entities via the DbSet
are
Add<TEntity>(TEntity entity)
AddRange<TEntity>(IEnumerable<TEntity> entities)
AddRange<TEntity>(params TEntity[] entities)
Most often, you will see examples of the generic version of Add
but with the type, parameter omitted because the compiler will infer it. The following two examples are identical:
// with the type parameter
var author = new Author{ FirstName = "William", LastName = "Shakespeare" };
context.Authors.Add<Author>(author);
// without a type parameter
var author = new Author{ FirstName = "William", LastName = "Shakespeare" };
context.Authors.Add(author);
The context begins tracking the entity that was passed into the Add
method and applies an EntityState
value of Added
to it. The context also applies the same EntityState
value of Added
to all other objects in the object graph that aren't already being tracked by the context. In the next example, the Added
state is also applied to the books:
var context = new SampleContext();
var author = new Author {
FirstName = "William",
LastName = "Shakespeare",
Books = new List<Book>
{
new Book { Title = "Hamlet"},
new Book { Title = "Othello" },
new Book { Title = "Macbeth" }
}
};
context.Authors.Add(author);
The books are added because they are referenced through the Books
property of the author. In the next example, the books will not be added:
var author = new Author { FirstName = "William", LastName = "Shakespeare" };
var hamlet = new Book { Title = "Hamlet", Author = author };
var othello = new Book { Title = "Othello", Author = author };
var macbeth = new Book { Title = "Macbeth", Author = author };
context.Authors.Add(author);
context.SaveChanges();
Although the author has been assigned to the Author
property of each of the books that have been instantiated, the author
entity is unaware of these relationships. Its Books
property is still null
and the books are not added to the context.
Adding Multiple Records
The AddRange
method is used for adding multiple objects to the ChangeTracker
in one method call. The code in the next example is very similar to the previous example, but the AddRange
method is used to add all the books and the author to the ChangeTracker
in one go:
var context = new SampleContext();
var author = new Author { FirstName = "Stephen", LastName = "King" };
var books = new List<Book> {
new Book { Title = "It", Author = author },
new Book { Title = "Carrie", Author = author },
new Book { Title = "Misery", Author = author }
};
context.Books.AddRange(books);
context.SaveChanges();
The AddRange
method takes an IEnumerable<object>
. The author forms part of the object graph for at least one of the books, so it is added too.
The other version of the AddRange
method takes a params array of entities, providing the facility to add a variable number of entities to the database without the need to create a collection for them:
var context = new SampleContext();
var author = new Author { FirstName = "William", LastName = "Shakespeare" };
var hamlet = new Book { Title = "Hamlet", Author = author };
var othello = new Book { Title = "Othello", Author = author };
var macbeth = new Book { Title = "Macbeth", Author = author };
context.Books.AddRange(hamlet, othello, macbeth);
context.SaveChanges();
When the SaveChanges
method is called on the DbContext
, all entities with an EntityState
of Added
will be inserted into the database. The ordering of the SQL to insert objects is managed in such a way to ensure that principals are inserted first and their primary key value is then available to be applied to the foreign key of dependent objects.
⚠️ Common Misconception: AddRange
is NOT a Bulk Insert
It’s important to understand that AddRange
is not the same as a bulk insert.
AddRange
only adds multiple entities into the Change Tracker.When you call
SaveChanges()
, EF Core will insert them into the database.- In earlier versions, this happened strictly one by one.
- In EF Core 7 and later, inserts are batched together to reduce round-trips — but they’re still executed as multiple individual
INSERT
statements.
This means it’s much better than before, but still not designed for true high-performance bulk operations.
If you need to insert thousands or millions of rows efficiently, you should use the BulkInsert method from Entity Framework Extensions.
// Using AddRange (EF Core 7+, batched inserts but still individual INSERT statements)
context.Books.AddRange(books);
context.SaveChanges();
// @nuget: Z.EntityFramework.Extensions.EFCore
using Z.EntityFramework.Extensions;
// Using BulkInsert (true bulk operation, single optimized command under the hood)
context.BulkInsert(books);