0

What's the most preferred way to work with Entity Framework and DTOs?

Let's say that after mapping I have objects like:

Author
    int id
    sting name
    List<Book> books

Book
    int id
    string name
    Author author
    int authorID

My DTOs

AuthorDTO
    int id
    sting name

BookDTO
    int id
    string name
    int authorID

Since author can have a lot of books I don't want to retrieve all of them, when for example I'm only interested in authors.

But sometimes I might want to get few authors and filtered books or all books. I could go with multiple queries AuthorDTO GetAuthor(int id) List<BookDTO> GetBooks(int authorID). But that means several accesses to database.

The ways I see it:

  1. If I had in AuthorDTO field List<BookDTO> books the job could be done. But sometimes I would keep this list empty, if for example I listed only authors. And that means some unconsistency, mess and a lot of details to remember.

  2. Return Tuple<AuthorDTO, List<BookDTO>> it might be a bit confusing.

  3. Define new DTO.

    AuthorAndBooksDTO
        AuthorDTO author
        List<BookDTO> books
    
1
  • In my project I've end up with DTOs like this: BookDTO; int id; string name; int authorID; string authorName; Last one just for visual information and it saves me from another DB roundtrip. As a conclusion I just adding up string Name for every navigation property model has. Commented May 27, 2016 at 12:25

4 Answers 4

2

The problem with sticking to a sinlge AuthorDTO and selectively filling the List is that you are now forced to keep track of where that DTO came from. Is the list of Books not hydrated, or does this Author simply have no books? Do I have to go back to my controller and call a different method to get a different state of the same DTO? This lacks clarity from the consumer's standpoint.

In my experience, I've leaned the way of more DTOs instead of trying to re-use a set of basic DTOs to represent multiple different sets of data. It requires a bit more "boilerplate", having to set up a whole bunch of similar DTOs and mappings between DTO and Entity, but in the end the specificity and clarity makes the codebase easier to read and manage.

Sign up to request clarification or add additional context in comments.

Comments

2

I think some clarification of the issues involved will actually solve your confusion here.

First and most importantly, your entity classes are DTOs. In fact, that's all they are. They're classes that represent a table structure in your database so that data from queries Entity Framework makes can be mapped on to them. In other words, they are literally objects that transfer data. The failing of Microsoft and subsequently far too many MVC developers is to conflate them with big-M Models described by the MVC pattern.

As a result, it makes absolutely zero sense to use Entity Framework to return one or more instances of an entity and then map that to yet another DTO class before finally utilizing it in your code. All you're doing is creating a pointless level of abstraction that adds nothing to your application but yet another thing to maintain.

As far as relationships go, that's where Entity Framework's lazy/eager loading comes in. In order to take advantage of it, though, the property representing the relationship must follow a very specific convention:

public virtual ICollection<Book> Books { get; set; }

If you type it as something like List<Book>, Entity Framework will not touch the relationship at all. It will not ever load the related entities and it will not persist changes made to that property when saving the entity back to the database. The virtual keyword allows Entity Framework to dynamically subclass your entity and override the collection property to add the logic for lazy-loading. Without that, the related entities will only ever be loaded if you explicitly use Load from the EF API.

Assuming your property is defined in that way, then you gain a whole world of abilities. If you want all books belonging to the author you can just interact with author.Books directly (iterate, query, whatever). No queries are made until you do something that requires evaluation of the queryset. EF issues just-in-time queries depending on the information you're requesting from the database. If you do want to load all the related books at the same time you retrieve the author, you can just use Include with your query:

var author = db.Authors.Include(m => m.Books).SingleOrDefault(m => m.Id == id);

1 Comment

How do this square with the fact that the new Entity Framework Core eliminated lazy loading? Also, exipecting that the manner in which your data is stored in the database to be the way that your view will want to read it via the ViewModel is probably going to cause problems down the road. You're probably going to end up with other custom DTOs, and now you'll have both entities and DTOs side-by-side in your ViewModel. Not the end of the world, but not really clean, either.
1

My first question would be to ask why you are creating DTO's in the first place? Is there a consumer on the other end that is using this data? Is it a screen? Are you building DTO's just to build DTO's?

Since you tagged the question as MVC i'm going to assume you are sending data to a view. You probably want a ViewModel. This ViewModel should contain all the data that is shown on the View that uses it. Then use entity framework to populate the view model. This may be done with a single query using projections or something complex.

So after all that blathering. I would say you want option 3.

Comments

0

Just like the others said, for clarity reasons, you should avoid creating "generic" DTO's for specific cases.

When you want to sometimes have authors and some of their books then model a DTO for that. When you need only the authors then create another DTO that is more suited for that. Or maybe you don't need DTOs, maybe a List containing their names is enough. Or maybe you could in fact use an anonymous type, like new { AuthorId = author.Id, AuthorName = author.Name }. It depends on the situation.

If you're using ASP.NET MVC the DTO you'll want is in fact a ViewModel that best represents your page.

Based on what you've described, you're view model could be something like this

public class BookViewModel{
    public int Id {get;set;}
    public string Name {get;set;}
}
public class AuthorViewModel{
    public int Id {get;set;}
    public string Name {get;set;}
    public List<BookViewModel> Books {get;set;} = new List<BookViewModel>();
}

public class AuthorsViewModel
{
    public List<AuthorViewModel> Authors {get;set;} = new List<AuthorViewModel>();

    //add in this class other properties, like the filters used on the page...

    public void Load(){
        //here you can retrieve the data from your database. 
        //you could do like this:
        //step 1: retrieve data from DB via EF
        //step 2: fill in the Authors view models from the data at step 1
    }

}

//and in your controller you're calling the Load method to fill you're viewmodel with data from db. 
public class AuthorsController{
    public ActionResult Index(){
        AuthorsViewModel model = new AuthorsViewModel();
        model.Load();
        return View(model);
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.