5 Entity Framework Performance Tips
I've heard mention of some EF instances receiving a bad reputation regarding performance. Today, I want to show some simple tips on speeding up Entity Framework.
When Entity Framework 1 came out, it was absolutely horrible to use (sorry guys), but a lot of developers stuck with it and gave feedback regarding the ORM.
As it grew into EF 4 and 5, some developers felt there was a stigma following Entity Framework's performance and every once in a while, I would read about someone explaining how bad EF's performance was when making a simple LINQ call.
When I read those types of posts, I ask myself, "Is it the database, is it the LINQ statement, or umm...is the user a new dev?"
It could be a number of factors.
While ORMs (Object-Relational Mapper) should be performant, each one has their own way of optimization through specialization.
For example, out of the box, Entity Framework works quite well, but some people don't pursue how to optimize it. They say "It works...I'm done!"
No one truly optimizes until you hear someone scream about it regardless of whether it's a user or a developer.
This is also true for other ORMs. Like any third-party library, you first integrate it into your application and get it working, then you learn about optimization.
In today's post, I discuss some real-world techniques demonstrating how to speed up Entity Framework.
Tip #1 - Use AsNoTracking()
When building your web application, some developers take the approach of partitioning their code into read and write segments.
For example, this section of data access is for reading data and another server is meant for write the data.
If you are only performing reads on data like viewing detail records or generating reports, it makes sense to add the AsNoTracking() to your fluent LINQ statement.
var products = dbContext.Products.Where(e => e.UnitPrice > 5).ToList();
Since we just want to view this data and not edit, AsNoTracking() does not track changes to the entities.
var products = dbContext.Products.Where(e => e.UnitPrice > 5).AsNoTracking().ToList();
Obviously, since we don't track changes, it doesn't work well with CUD (Create, Update, Delete).
Tip #2 - Minimize Calls To The Database
The amount of calls performed in an Entity Framework call can blow your mind.
From my experience with DanylkoWeb, I learned from a number of mistakes.
First mistake was writing all kinds of code to get my data.
Once I had my entire web page ready to go and I looked over my code for the home page.
It had around 100 lines of code calling close to 15 repositories to grab data.
That's an expensive method.
Second mistake was looping through records to populate additional entities.
For each loop item, I was making a call to the database.
I'm not going to lie...it was hammering the database pretty bad.
To solve this issue, I decided to use multiple result sets to return the data.
One call, one set of data. Done!
While this technique is a lot to cover in this post, I'll refer you to Use Entity Framework to Return Multiple Result Sets From a Generic Repository.
Update
If you are using ASP.NET Core, check out the more recent post titled ASP.NET Core with Entity Framework Core: Returning Multiple Resultsets
A word of warning: If you pursue this particular avenue of retrieving data, there is a drawback. If you have relationships between your entities in your database, some relationships will not come over when you are materializing your code (turning your code into entities).
Tip #3 - Don't Fight Your Database
Since we're on the topic of relationships, sometimes it's better to build your database properly and let Entity Framework build the navigational properties between the entities.
For example, when you are designing your table in SQL Server, right-clicking on a field row will display a context menu with an option to define Relationships. This is where you define your foreign keys between tables.
When you have these relationships between tables, this provides insight to Entity Framework on how to map your entities to the tables. These relationships will convert over to navigational properties in your entities.
Choose the option "Code First from Database..." when adding a new ADO Data Model to your project. Entity Framework will take over from there. It will read your database and make all of the necessary arrangements in your code.
If you've already had a DBA go through and create everything to make it a fast database, Entity Framework will adhere to the database's rules and create optimized code for your EF project.
Tip #4 - Know your Deferred Calls
I imagine there are times when you are wondering why your web application slows down when you are trying to retrieve 5 records in a 50,000-record table.
What could be taking so long?
You examine the statement and it's just one line in the method.
public IEnumerable<Product> GetTopProduct() { return dbContext.Products.ToList().FirstOrDefault(); }
In this statement, Entity Framework is creating the SQL statement and the ToList() is executed...immediately.
If the above example is used with the Products table, it will return all 50,000 records before it grabs the first one in the list.
A better statement would be:
public IEnumerable<Product> GetTopProduct() { return dbContext.Products.FirstOrDefault(); }
This creates a SQL statement to return a TOP 1 record instead of retrieving all 50,000 records and then giving you the first record.
Definitely a quicker query.
Tip #5 - Don't bring over the kitchen sink
If you don't need all of the fields, don't ask for them.
Let's say you have this Entity Framework call.
var products = dbContext.Products.ToList();
If you have a products table of 50 fields and you call this line of code, you will receive all 50 fields dragging down your database, network, PC...and your DBA. :-p
It's better to request a smaller subset of fields to pass across the wire.
var products = dbContext.Products.Select(r => new Product { r.ProductId, r.Title }).ToList();
It hopefully will make your DBA a little happier. Maybe even smile?
Conclusion
I'm pretty sure I'm forgetting some performance tips, but these were the usual suspects I fall back on when I'm not getting the speed I expect.
With Entity Framework Core out, these tips only make EF Core better than before since they improved performance again.
While these tips were from real-world experience, additional tips are available at Microsoft's Performance Considerations for EF 4, 5, and 6.
Did I miss some tips? How do you optimize Entity Framework? Do you use EF? Post your comments below and let's discuss.