Shaping Data Without Extra Classes
Anonymous types in C# allow developers to create lightweight, on-the-fly data structures without defining separate model or DTO classes. When combined with Entity Framework (EF), they provide a powerful mechanism to project data into custom shapes – ideal for reports, dashboards, and lightweight data displays.
Instead of fetching full entities (which can be memory-heavy and slower), anonymous types let you select only the properties you need, keeping queries lean, efficient, and type-safe.
πΉ What Are Anonymous Types in EF?
Anonymous types are compiler-generated objects created with the new { } syntax. They are immutable and type-safe, but have no explicit class name. This makes them perfect for temporary data shaping inside LINQ to Entities queries.
Example:
var result = new { Id = 1, Name = "Alice", Score = 95 };
Console.WriteLine($"{result.Name} scored {result.Score}");
The compiler automatically infers the property names and types, creating a read-only class behind the scenes. In EF, this same principle applies β you can project data directly into anonymous types when querying entities.
πΉ Why Use Anonymous Types in EF Queries?
When working with EF, each entity typically maps to a database table. However, you rarely need every column from a table. Anonymous types let you:
- Retrieve only the columns you need, improving performance.
- Combine data from multiple entities into one compact result.
- Create ad hoc projections for UI binding or JSON output.
- Avoid defining temporary model classes for every custom shape.
This approach minimises memory footprint, reduces SQL payload, and speeds up query execution.
π§± Example: Projecting Specific Columns
Instead of fetching the entire Student entity, you can select just the necessary data:
using (var context = new SchoolContext())
{
var studentSummaries = context.Students
.Where(s => s.Grade > 85)
.Select(s => new
{
s.FirstName,
s.LastName,
s.Grade
})
.ToList();
foreach (var s in studentSummaries)
Console.WriteLine($"{s.FirstName} {s.LastName} - {s.Grade}");
}
β Explanation:
.Select()shapes each entity into a new anonymous type.- EF translates the query into SQL that retrieves only
FirstName,LastName, andGrade. - The anonymous object is materialised into memory without creating full entity objects.
This reduces database load and network transfer dramatically.
πΉ Combining Multiple Entities
Anonymous types shine when you need to combine related data without defining a custom class.
Example:
var orderSummaries = context.Orders
.Where(o => o.Total > 100)
.Select(o => new
{
o.OrderId,
o.Customer.Name,
o.OrderDate,
TotalItems = o.OrderDetails.Count,
o.Total
})
.ToList();
β Explanation:
- EF automatically performs the join between
OrdersandCustomers. - The projection combines fields from both entities into a single anonymous type.
- This avoids the need for a separate βOrderSummaryβ class.
πΉ Joining Tables with Anonymous Types
You can also create combined results using explicit joins:
var customerOrders = from c in context.Customers
join o in context.Orders on c.CustomerId equals o.CustomerId
select new
{
CustomerName = c.Name,
o.OrderId,
o.Total
};
β
Result:
EF translates the join into a single SQL statement returning customer names and their order totals, all projected into an anonymous structure.
πΉ Nested Anonymous Types
Anonymous types can contain other anonymous types β useful for hierarchical projections such as JSON-like structures:
var customerProfiles = context.Customers
.Select(c => new
{
c.Name,
Contact = new { c.Email, c.Phone },
Orders = c.Orders.Select(o => new { o.OrderId, o.Total }).ToList()
})
.ToList();
β
Explanation:
This creates a nested structure like:
[
{
"Name": "Alice",
"Contact": { "Email": "a@example.com", "Phone": "12345" },
"Orders": [
{ "OrderId": 1, "Total": 200 },
{ "OrderId": 2, "Total": 350 }
]
}
]
Perfect for generating structured output for APIs or front-end apps.
πΉ Anonymous Types vs DTOs
| Feature | Anonymous Types | DTO (Data Transfer Object) |
|---|---|---|
| Definition | Inline, compiler-generated | Explicit class |
| Reusability | Limited to local scope | Reusable across layers |
| Immutability | Read-only | Customisable |
| Use Case | Quick projections, ad hoc queries | API models, external data exchange |
β
Guideline:
Use anonymous types for internal, read-only projections within a single method or short pipeline.
Use DTOs when passing data across layers, returning from APIs, or storing long-term results.
πΉ Deferred Execution and Anonymous Types
Like all LINQ to Entities queries, projections into anonymous types use deferred execution β the SQL runs only when you enumerate the query.
Example:
var query = context.Products
.Select(p => new { p.Name, p.Price }); // No SQL yet
var list = query.ToList(); // SQL executes here
β
Benefit:
You can compose and refine your query before it runs β improving performance and clarity.
π Performance and Best Practices
| Strategy | Description | Example |
|---|---|---|
| Project Early | Select only required fields | .Select(x => new { x.Name }) |
| Avoid Client Evaluation | Ensure EF translates logic to SQL | Avoid methods unsupported by SQL |
| Reuse Query Logic | Store reusable expressions in helper methods | Expression<Func<T,bool>> |
| Debug SQL | Use EF logging or Profiler to inspect generated SQL | context.Database.Log |
Anonymous projections are fast because EF executes them as direct SQL SELECT statements, fetching only the needed columns.
π Summary
| Concept | Description | Example |
|---|---|---|
| Anonymous Types | Temporary, compiler-generated objects | new { Name = "Alice" } |
| EF Projections | Map entity data to custom shapes | .Select(x => new { x.Id, x.Name }) |
| Nested Types | Anonymous types inside others | new { Contact = new { Email } } |
| Deferred Execution | Query executes when enumerated | .ToList() |
| Efficiency | Retrieve minimal, precise data | Project early and filter early |
β Best Practices
- Use anonymous types for quick, focused data shaping.
- Keep them local to a method or query pipeline.
- Prefer explicit DTOs for reusable or API-facing data.
- Profile SQL output to confirm projections are efficient.
π‘ Conclusion
Using anonymous types within Entity Framework queries allows developers to streamline data access, improve performance, and avoid unnecessary complexity.
They enable fine-grained control over what data is fetched, offering a clean, expressive syntax for shaping query results β all while maintaining the safety and efficiency of strongly-typed LINQ queries.
Mastering this technique is key to writing modern, optimised, and elegant EF queries that focus only on the data that truly matters.