Addressing the Frustration in EF.net Core
In the realm of .NET application development, Entity Framework Core (EF Core) stands out as a highly regarded Object-Relational Mapping (ORM) tool, streamlining the process of database operations with its robust LINQ queries and automatic change tracking. However, despite its comprehensive features, EF Core often introduces a level of frustration for developers when dealing with complex SQL queries, especially those requiring custom projections or intricate joins that are not straightforward to express using LINQ. This limitation becomes apparent in scenarios where the performance of LINQ queries doesn’t meet expectations or when the SQL generated by EF Core deviates from the optimized paths developers know are possible with hand-written SQL.
One common grievance arises when attempting to map the results of raw SQL queries to Data Transfer Objects (DTOs) or view models that don’t directly align with the database schema. EF Core mandates that queries executed using FromSqlRaw or FromSqlInterpolated methods return entities that EF Core is tracking, thereby complicating scenarios where the desired outcome is a projection into a custom DTO. This constraint often leads to inefficient workarounds, such as fetching more data than necessary and then manually transforming it in-memory, which can detrimentally impact application performance and developer productivity.
Problem
The goal is to aggregate invoice data by ciient, calculating the total cost of invoices per cllient and then project this aggregated data into a DTO (Data Transfer Object) for further use, likely in a user interface or report.
Here is the database scheme:
The aim is to create a query that computes the sum of the spending of each client and map it to the class StatDto
class StatDto{
public int ClientId;
public String ClientName;
public double Amount;
} Solving with EF Core
Given the limitations of EF Core in directly executing raw SQL queries and projecting to non-entity types, a common workaround involves leveraging LINQ to perform similar operations in a way that EF Core supports. This method fully utilizes EF Core’s ability to translate LINQ queries into efficient SQL, ensuring that the operation remains within the ORM’s optimization capabilities.
Here’s how you might achieve the desired outcome using LINQ with EF Core:
var stats = dbContext.Clients
.Select(u => new StatDto
{
ClientId = u.Id,
ClientName = u.Name,
cost = u.Invoices
.SelectMany(i => i.InvoiceDetails)
.Sum(id => id.Quantity * id.UnitPrice) // Aggregate function to calculate cost
}).ToList();
Solving with Dapper
Dapper excels in executing raw SQL and mapping the results directly to a DTO, making it a perfect fit for this scenario. Here’s how you can accomplish the task using Dapper:
using System.Data.SqlClient;
using Dapper;
var connectionString = "your_connection_string"; // Define your connection string
using (var connection = new SqlConnection(connectionString))
{
var query = @"
SELECT u.Id AS ClientId, u.Name AS ClientName, SUM(id.Quantity * id.UnitPrice) AS Amount
FROM Clients u
INNER JOIN Invoice i ON u.Id = i.ClientId
INNER JOIN InvoiceDetails id ON i.Id = id.InvoiceId
GROUP BY u.Id, u.Name";
var statsDto = connection.Query<StatDto>(query).ToList();
}
The complementary strengths of EF Core and Dapper present a compelling case for their combined use in .NET projects. While EF Core excels at managing complex domain models and providing a high-level abstraction over database operations, Dapper offers a streamlined, performance-oriented approach for scenarios where custom SQL queries and mappings are necessary. This synergy allows developers to leverage the full ORM capabilities of EF Core for standard operations while utilizing Dapper for those critical areas where custom SQL queries and projections are essential, thereby mitigating frustration and enhancing the overall efficiency and performance of .NET applications.
Conclusion
In conclusion, the frustration arising from EF Core’s limitations with native custom queries and projections can be effectively addressed by integrating Dapper into the development workflow. This integration ensures that developers can enjoy the best of both worlds: the comprehensive ORM features of EF Core and the raw SQL execution prowess of Dapper. By doing so, developers can navigate the complexities of database operations with greater flexibility, performance, and ease, ultimately leading to more robust and efficient .NET applications.


