Question

Retrieve an Inserted Entity ID with Entity Framework in C#

csharpasp.netentity-framework

Question

In an ASP.NET application using Entity Framework, how can I retrieve the CustomerID generated when I add a new Customer to the database?

using (var context = new EntityContext())
{
    var customer = new Customer
    {
        Name = "John"
    };

    context.Customers.Add(customer);
    context.SaveChanges();

    int id = customer.CustomerID;
}

After SaveChanges(), customer.CustomerID has a value. Does this value represent the database table's identity value? If a record is deleted and identity values contain gaps, why might the generated ID not match the number of rows or an expected sequence?

Short Answer

Entity Framework tracks an entity you add to a DbContext. When SaveChanges() inserts that entity, Entity Framework obtains its database-generated primary key and writes it back to the same C# object. You can then read customer.CustomerID. Identity values are unique identifiers, not guaranteed row numbers, so deleted rows and failed or rolled-back inserts can leave gaps.

Concept

A primary key identifies one database row. In many SQL Server tables, an integer primary key is configured as an IDENTITY column, which means SQL Server generates the next key value during an insert.

Before the insert, a new Customer may have the default integer value, usually 0. Calling SaveChanges() causes Entity Framework to:

  1. Detect that customer is new.
  2. Send an INSERT command to the database.
  3. Receive the generated primary-key value from the database.
  4. Set customer.CustomerID to that value.

Therefore, after a successful SaveChanges(), this is the normal way to get the inserted row's ID:

int id = customer.CustomerID;

The value is the database-generated key when the property is correctly configured as database-generated. It is not merely a separate, arbitrary Entity Framework ID.

An identity column does not promise consecutive values. For example, if IDs 1, 2, and 3 exist and row 2 is deleted, the table can contain 1 and 3. The next inserted row may receive 4. This behavior preserves uniqueness and avoids unsafe renumbering of existing rows.

Mental Model

Think of the database as a ticket dispenser.

  • A new Customer is a person waiting without a ticket number.
  • context.Customers.Add(customer) puts that person into Entity Framework's waiting list; it does not yet create a database row.
  • SaveChanges() sends the person to the ticket dispenser and database.
  • The database assigns ticket 42 and Entity Framework writes 42 onto customer.CustomerID.

If someone throws away ticket 41, the dispenser does not reuse it. The next ticket is still 42. Ticket numbers identify entries; they are not a count of people currently in the room.

Take Quiz

Syntax and Examples

Create the entity, add it to the context, save changes, and read the key after saving.

using (var context = new EntityContext())
{
    var customer = new Customer
    {
        Name = "John"
    };

    context.Customers.Add(customer);

    // The INSERT happens here.
    context.SaveChanges();

    int customerId = customer.CustomerID;
    Console.WriteLine(customerId);
}

Add marks the object as new. SaveChanges sends the insert to the database. Once it succeeds, Entity Framework updates the tracked customer object with generated values such as an identity primary key.

If your model uses a different key name or type, read that property instead:

var order = new Order { Description = "Notebook" };
context.Orders.Add(order);
context.SaveChanges();

long orderId = order.Id;

For an integer identity key, the property commonly has a default value of 0 before the insert. Do not treat that default as a real database ID.

Step by Step Execution

Consider this code:

var customer = new Customer { Name = "Ava" };

context.Customers.Add(customer);
Console.WriteLine(customer.CustomerID);

context.SaveChanges();
Console.WriteLine(customer.CustomerID);

Assume CustomerID is an int identity column and the next database identity value is 15.

  1. new Customer creates an object in application memory.
  2. CustomerID initially has the default int value: 0.
  3. Add(customer) tells the context to insert this entity when changes are saved. It still may not exist in the database.
  4. The first Console.WriteLine typically prints 0.
  5. SaveChanges() sends an INSERT statement to the database.
  6. SQL Server generates identity value 15.
  7. Entity Framework receives that generated value and assigns it to customer.CustomerID.
  8. The second Console.WriteLine prints 15.

If SaveChanges() throws an exception, the insert did not complete successfully from the application's point of view. Do not use the key as though the row was saved.

Real World Use Cases

Retrieving the generated key is useful whenever a newly inserted record must be used immediately.

  • Create related records: Save an Order, then use order.Id when creating OrderItem records.
  • Return an API response: A POST /customers endpoint can return the new customer's ID.
  • Redirect after form submission: After creating a blog post, redirect to /posts/{id}.
  • Write audit records: Insert an entity, then store its ID in an audit log.
  • Attach uploaded files: Save a product first, then associate image metadata with its product ID.

Example with a related entity:

var order = new Order { CustomerName = "Ava" };
context.Orders.Add(order);
context.SaveChanges();

var item = new OrderItem
{
    OrderId = order.Id,
    ProductName = "Keyboard",
    Quantity = 1
};

context.OrderItems.Add(item);
context.SaveChanges();
Take Quiz

Real Codebase Usage

In real applications, developers usually avoid relying on identity values as row counts or business-visible sequence numbers. They use the generated key strictly to identify and relate records.

Save a parent and children together

When navigation properties are configured, Entity Framework can insert a parent and its children in one SaveChanges() call and fix up the foreign keys.

var order = new Order
{
    CustomerName = "Ava",
    Items = new List<OrderItem>
    {
        new OrderItem { ProductName = "Keyboard", Quantity = 1 },
        new OrderItem { ProductName = "Mouse", Quantity = 2 }
    }
};

context.Orders.Add(order);
context.SaveChanges();

int orderId = order.Id;

Validate before saving

Validate input before adding data. This prevents unnecessary database work and produces clearer errors.

if (string.IsNullOrWhiteSpace(customer.Name))
{
    throw new ArgumentException("A customer name is required.");
}

context.Customers.Add(customer);
context.SaveChanges();

Use a transaction for multi-step work

When several database operations must either all succeed or all fail, use a transaction. Identity values can still have gaps after failed or rolled-back work; that is normal.

Return a DTO from an API

Do not normally return your tracked entity directly from a web endpoint. Return the ID and fields the client needs.

return new CreateCustomerResult
{
    Id = customer.CustomerID,
    Name = customer.Name
};

Common Mistakes

Reading the ID before saving

var customer = new Customer { Name = "John" };
context.Customers.Add(customer);

int id = customer.CustomerID; // Usually 0 for an int identity key

Avoid it: Read the database-generated key after a successful SaveChanges().

Assuming identity values are row numbers

int numberOfCustomers = customer.CustomerID; // Incorrect assumption

A customer ID identifies one customer. It does not tell you how many customers exist. Use context.Customers.Count() when you need a count.

Expecting deleted IDs to be reused

Deleting customer 5 does not mean the next customer will receive ID 5. Identity columns normally continue forward. Never build logic that depends on gap-free IDs.

Manually setting an identity key

var customer = new Customer
{
    CustomerID = 999,
    Name = "John"
};

If CustomerID is generated by the database, setting it yourself can cause an insert error or configuration problems. Let the database assign it unless your schema intentionally uses application-assigned keys.

Calling SaveChanges() but ignoring failures

context.Customers.Add(customer);
context.SaveChanges();
SendWelcomeEmail(customer.CustomerID);

This is only safe if SaveChanges() succeeds. Handle exceptions appropriately, and do not continue as if the record was inserted after a database error.

Using an ID as a display sequence

If invoices need legally meaningful, gap-free, or specially formatted numbers, an identity primary key is usually the wrong feature. Use a separate, explicitly designed business-numbering process.

Comparisons

ConceptWhat it doesGuarantees consecutive values?Typical use
Identity primary keyDatabase generates a unique numeric key during insertNoInternal row identity and relationships
Row countCounts existing recordsNot applicableReporting and statistics
Application-assigned keyApplication provides the keyDepends on application logicImported data or domain-specific identifiers
GUID/UUID keyGenerates a globally unique identifierNoDistributed systems and client-side key generation
Business numberA domain-visible number, such as an invoice numberOnly if designed to do soHuman-facing workflows

Add versus SaveChanges

OperationDatabase row inserted immediately?Generated key available?
context.Customers.Add(customer)Usually noNot reliably for database identity keys
context.SaveChanges()Yes, for pending additionsYes, after success

Identity key versus business identifier

A CustomerID such as 1042 is a technical identifier. An account code such as CUST-2025-0042 is a business identifier. Keep them separate unless they truly have the same requirements.

Take Quiz

Cheat Sheet

var entity = new Customer { Name = "John" };
context.Customers.Add(entity);
context.SaveChanges();

int id = entity.CustomerID;
  • Call Add to mark a new entity for insertion.
  • Call SaveChanges() to execute the insert.
  • Read a database-generated key only after a successful save.
  • The original tracked object receives the generated key automatically.
  • For an int identity column, the value before saving is commonly 0.
  • Identity values must be unique; they do not need to be consecutive.
  • Deleted rows, failed inserts, and rollbacks can leave identity gaps.
  • Use a separate field or process for human-facing sequence numbers.
  • Use Count() to count rows, not an identity value.

FAQ

Does customer.CustomerID contain the database identity value after SaveChanges()?

Yes. When CustomerID is configured as a database-generated identity key, Entity Framework retrieves the generated value after a successful insert and assigns it to the tracked customer object.

Why is CustomerID zero before SaveChanges()?

0 is the default value for an int. The database has not inserted the row or generated its identity value yet.

Does Entity Framework create a different ID from the database ID?

Not in this normal identity-key scenario. After saving, the entity property is updated with the key generated by the database.

Why are there gaps in SQL Server identity values?

Identity values are designed for uniqueness, not uninterrupted numbering. Deletions, failed inserts, rollbacks, and database behavior can create gaps.

Can I use the generated ID to create child records?

Yes. Save the parent first, then use its generated ID as the child's foreign key. Alternatively, use configured navigation properties and save the object graph together.

Should I reset an identity value after deleting rows?

Usually no. Resetting or reseeding identities can create collisions or confusion, especially in production. Gaps are expected and harmless for primary keys.

What if the key is a GUID instead of an integer identity?

The overall idea is the same: the entity needs a unique key. Depending on configuration, the GUID may be generated by the application or by the database, so when it becomes available can differ.

How do I know whether my key is database-generated?

Check your entity configuration and database schema. In SQL Server, an IDENTITY column is database-generated. Entity Framework mappings can also specify whether a property is generated on add.

Take Quiz

Mini Project

Description

Build a small customer-and-order creation workflow. It demonstrates how Entity Framework obtains a database-generated customer ID after saving, then uses that ID to create a related order.

Goal

Create a customer, save it, display the generated ID, and create an order linked to that customer.

Requirements

Create Customer and Order entity classes with integer key properties. Add a new customer to the Entity Framework context. Call SaveChanges() before reading the customer's generated ID. Create an order whose CustomerID matches the saved customer's ID. Save the order and print both generated IDs.

Take Quiz

Keep learning

Related questions

View more