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:
- Detect that
customeris new. - Send an
INSERTcommand to the database. - Receive the generated primary-key value from the database.
- Set
customer.CustomerIDto 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
Customeris 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
42and Entity Framework writes42ontocustomer.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.
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.
new Customercreates an object in application memory.CustomerIDinitially has the defaultintvalue:0.Add(customer)tells the context to insert this entity when changes are saved. It still may not exist in the database.- The first
Console.WriteLinetypically prints0. SaveChanges()sends anINSERTstatement to the database.- SQL Server generates identity value
15. - Entity Framework receives that generated value and assigns it to
customer.CustomerID. - The second
Console.WriteLineprints15.
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 useorder.Idwhen creatingOrderItemrecords. - Return an API response: A
POST /customersendpoint 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();
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
| Concept | What it does | Guarantees consecutive values? | Typical use |
|---|---|---|---|
| Identity primary key | Database generates a unique numeric key during insert | No | Internal row identity and relationships |
| Row count | Counts existing records | Not applicable | Reporting and statistics |
| Application-assigned key | Application provides the key | Depends on application logic | Imported data or domain-specific identifiers |
| GUID/UUID key | Generates a globally unique identifier | No | Distributed systems and client-side key generation |
| Business number | A domain-visible number, such as an invoice number | Only if designed to do so | Human-facing workflows |
Add versus SaveChanges
| Operation | Database row inserted immediately? | Generated key available? |
|---|---|---|
context.Customers.Add(customer) | Usually no | Not reliably for database identity keys |
context.SaveChanges() | Yes, for pending additions | Yes, 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.
Cheat Sheet
var entity = new Customer { Name = "John" };
context.Customers.Add(entity);
context.SaveChanges();
int id = entity.CustomerID;
- Call
Addto 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
intidentity column, the value before saving is commonly0. - 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.
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.
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
Best Way to Repeat a Character in C#: Building Repeated Strings Efficiently
Learn the best way to repeat a character in C#, compare StringBuilder, string concatenation, and simpler built-in options.
C# Access Modifiers and static: public, private, protected, and Defaults
Learn how C# public, private, protected, and default access control visibility, and how static differs from instance members.