Using Automapper to improve performance of Entity Framework
https://docs.automapper.org/en/stable/Queryable-Extensions.html
ProjectTo
must be the last call in the chain. ORMs work with entities, not DTOs. So apply any filtering and sorting on entities and, as the last step, project to DTOs.
Using Automapper to improve performance of Entity Framework
Entity Framework is an ORM technology widely used in the .NET world. It’s very convenient to use and lets you forget about SQL… well, at least until you hit performance issues. Looking at the web applications I worked on, database access usually turned out to be the first thing to improve when optimizing application performance.
Navigation properties
The main goal of Entity Framework is to map an object graph to a relational database. Tables are mapped to classes. Relationships between tables are represented with navigation properties.
The above example will be mapped to the following classes:
|
The highlighted lines declare navigation properties. Thanks to navigation properties, it’s very convenient to access details of Article’s Author. However, it comes at a cost. Imagine the following code in the view:
|
Assuming that ViewBag.Articles is loaded with the below method, this code might turn out to be very slow.
|
Unfortuantely, it will fire a separate SQL query to the database server for each element in the Articles collection. This is highly suboptimal and might result in long loading times.
Lazy and eager loading
The reason behind this behaviour is the default setting of Entity Framework which tells it to load navigation properties on demand. This is called lazy loading. One can easily overcome this problem by enabling eager loading:
|
Eager loading will cause EF to pre-load all Authors for all selected Articles (effectively, performing a join). This might work for simple use cases. But imagine that Author has 50 columns and you are only interested in one of them. Or, Author is a superclass of a huge class hierarchy modelled as table-per-type. Then, the query built by EF would become unncessarly huge and it would result in transfering loads of unnecessary data.
Introducing DTO
One way to handle this situation is to introduce a new type which has all Article’s properties but additionally has some of the related Author’s properites:
|
Now we can perform projection in the query. We will get a much smaller query and much less data transfered over the wire:
|
Automapper
We improved performance, but now the code looks much worse - it involves manual mapping of properties which is in fact trivial to figure out. What’s more, we would need to change this code every time we add or remove a field in the Article class. A library called Automapper comes to rescue. Automapper is a convention-based class mapping tool. Convention-based means that it relies on naming conventions of parameters. For example, Author.FirstName is automatically mapped to AuthorFirstName . Isn’t that cool? You can find it on NuGet. Once you add it to your solution, you need to create Automapper configuration:
|
Here we declare that Article should be mapped to ArticleDto, meaning that every property of Article should be copied to the property of ArticleDto with the same name. Now, we need to replace the huge manual projection with Automapper’s ProjectTo call.
核心是使用连ProjectTo,另外这里的错误用法。按照AutoMapper官方的介绍ProjectTO应该放在where和orderby后面。【另外,有了ProjectTo之后,不需要Include】
|
You need to add one more line:
|
And that’s it. You’ve just improved readability of your code and made it less fragile to changes.
Summary
Automapper is a very flexible tool. You don’t need to rely on naming convensions, you can easily declare your own mappings. Additionally, we have just used just a specific part of Automapper - Queryble Extensions which work with ORMs. You can also use Automapper on regular collections or just on plain objects.
I believe the problem I highlighted here is just a symptom of a much broader issue of incompatibility of relational and object oriented worlds. Although Entity Framework tries to address the issue by allowing to choose between eager and lazy loading, I don’t think it is a good solution. Classes managed by EF being elements of a public API are a big problem. As a user of such interface you never know if a navigation property is loaded and whether accessing it will result in a DB query.
Therefore, I advocate the use of mapped DTOs. This approach reminds me slightly of an idea called Functional Relational Mapping adopted for example by the Slick framework for Scala. I believe it to be a great alternative to classic ORMs. Some references:
- Lazy and eager loading in EF
- Automapper configuration
- Slick: Functional Relational Mapping library for Scala
作者:Chuck Lu GitHub |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2021-08-17 游戏术语
2021-08-17 the user account is not authorized for remote login
2020-08-17 乐刻健身房 间隔天数
2015-08-17 Linq打印
2015-08-17 .net framework client profile
2015-08-17 Resharper中注释代码的快捷键
2015-08-17 What does the number on the visual studio solution icon represent?