When building a simple API based on Entity Framework, It can be tempting to keep it simple and bind persistent entities directly to WebAPI output.
❌ Figure: Bad example - A naive WebAPI implementation
Although this code is very simple to write, there can be a number of potential problems:
Update operations can be even more problematic:
❌ Figure: Bad example - A naive update operation
Consider the Product object that is received as a FromBody parameter by the action.
At the start of the action this is not a persistent entity that has been loaded from the database and attached to a DBContext. Instead it is an entirely new object that has been created by the MVC databinding system.
The next call to the DbContext will take that object – exactly as received and de-serialized from the network – and attach it as-is to the DBContext in the “Modified” state, to be later saved by the call to SaveChangesAsync()
Any fields that did not survive the "round-trip" from the server -> client-> server will be overwritten / lost. The mapping from "Object received from the web" to "Object saved into the database" is entirely implicit here.
For all these reasons, the use of DTOs or View Models is highly recommended:


✅ Figure: Good Example - Update an Entity from a submitted View Model
This approach requires a bit more boiler-plate code as the fields to be updated are applied manually, but there is far less risk of unintended side effects. As the complexity of the code increases, it will be much easier for developers to keep a clear distinction between ViewModel objects that were received from web requests, and persistent entities that came from Entity Framework.


✅ Figure: Good example - A Read Operation that selects directly into a view model
For the above read, Entity Framework will execute an SQL select statement containing only the fields that have been projected via .Select() This will also prevent change tracking on the source entity.
The above example also demonstrates how a projection / mapping from an entity to a view model can be reused by creating an Expression <Func<EntityType, ViewModelType>>.