So say I have a controller Method

    // GET: TimeEntries
    [HttpGet("[action]")]
    public IEnumerable<TimeEntryDTO> GetLast(int value)
    {
        return dal.GetLast(value);
    }

And a data access layer method of (using AutoMapper to convert TimeEntry to TimeEntryDTO)

    public IEnumerable<TimeEntryDTO> GetLast(int value)
    {
        return db.TimeEntries.TakeLast(value).ProjectTo<TimeEntryDTO>();
    }

My Question is should I do this, and have the DAL (Data Access Layer) return the DTO (Data Transfer Object) or should I have the DAL return an IQuerable of the EF Model, and then convert it to a DTO in the controller method?

EDIT:

After doing some more reading, isn't the DBsets exposed by an EF context, in fact, the repository surface? So you wouldn't really ever see the actual DAL right, because it is a layer inside EF? So I should not call a layer that is on top of the EF Context, a DAL?

up vote 3 down vote accepted

...should I have the DAL return an IQuerable of the EF Model...

Please don't do this. As Mark Seemann explains in IQueryable is Tight Coupling, this interface suffers from a number of problems.

  1. It's what's often referred to as a "header interface". This term refers to the bad practice in languages like C whereby a .h file simply details all the public functions found in the corresponding .c file. No consideration is given to the needs of the consumer, they simply get everything in one big lump. This means that IQuerable is a huge interface that is incredibly difficult to mock when writing unit tests around methods that return it. Have a look at the 16 part series on how to write an implementation of IQuerable to get an idea of how hard this can be.

  2. It's a leaky abstraction. It's effectively an interface written to reflect the design of the Entity Framework ORM. As such, it exposes much of the functional design of that ORM in the interface, ie those implementation details "leak out" through the interface. So by exposing IQuerable in your DAL API, you couple your DAL to it and you couple consumers of that API to it.

The whole purpose of abstractions is to decouple parts of your code. Using an interface that actually couples things together defeats the whole purpose of an abstraction. So don't do this.

Instead, stick to your other suggestion: convert those implementation details of IQuerable into DTOs that are focused on supplying just what the caller needs. This requires a bit more work as you have to write the DTOs and the conversion code, but it pays dividends in keeping things decoupled and easier to understand and test.

You should do the mapping to DTOs in the repository and return the DTO from there.

This is particularly beneficial when you use a separated API Controllers project, a separated Data Layer project, a separated Repositories project, and a separated DTOs project.

This way the Repositories project depends on the Data Layer project and the DTOs project while the project containing the API Controllers depends only on the Interfaces (utilising dependency injection) and DTOs project and has no knowledge of the Data Layer or Repositories projects.

It creates a much simpler and cleaner dependency graph and greater separation of concerns.

  • Isn't a DBset exposed by an EF context a repository? Isn't that the whole point of EF that it handles the DAL and the Repo Layer for you. Maybe I should not have called the next layer on top of EF the DAL layer. Not sure what I should call it. Maybe I don't need it? Thoughts? – TheColonel26 Nov 28 at 15:10
  • 1
    As I understand it, EF/ORM is merely a code-based representation of the database, making it much easier to work with the database in code. A repository is an abstraction layer over the top of the ORM, sitting between the DB context and the code that wants to consume it. There are numerous good reasons to use the repository pattern, one of the most practical being that instead of accessing the data in different ways all over your solution, you use a consistent approach, and then when changes to the DB context happen, code that queries those parts can be changed in one place rather than [...] – Dom Nov 28 at 15:42
  • [...] searching for every place in your code that has been affected. See the Microsoft repository example and this SO discussion to gain a better understanding of it. – Dom Nov 28 at 15:44
  • Oooh ok, Can you update your answer with that information and I will mark it as accepted? – TheColonel26 Nov 28 at 16:30
  • Have you read this? thereformedprogrammer.net/… – TheColonel26 Nov 28 at 18:46

I agree with @Dom and would like to expand on the importance of having a separate project for your DTO's. In one of our applications we recently decided to stop using EF and use Dapper instead. The only thing we needed to change is the DAL implementation (DAL.EF and now we have DAL.Dapper) which is returning the exact same DTO's as the EF project. We are changing code and implementations where it hurts the most on an on-demand basis.

If at the beginning we decided to simply inject DbContext through the application we'd have to write everything from scratch again.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.