Web Api 2 + EF6 - Create DTO

For the project, we are using EF and Web Api 2. To take a break from our database models, we are using DTOs. We have a factory to build these DTOs:

public class FooFactory:IModelConverter<FooDTO, Foo>
{

    public FooDTO Create(Foo data)
    {
        return new FooDTO()
        {
           //Some fields
        };
    }
}

      

In our Api call, we can do:

public async Task<IHttpActionResult> GetFoo()
    {
        var foos = db.Foos

        //DO STUFF

         var dtos = (await foos.ToListAsync()).Select(m => _converter.Create(m)); //Converter is an instance of FooFactory)

        return Ok(dtos);
    }

      

This works, but it implies that after executing the query, we have to iterate over all of our results and convert each model to a DTO.

Alternatively, we could do:

public async Task<IHttpActionResult> GetFoo()
    {
        var foos = db.Foos

        //DO STUFF

        return Ok(await foos.Select(m => new FooDTO() { 
            //Assign fields
        }).ToListAsync());
    }

      

To include this projection in a query executed by EF. But this exposes all the internal details of FooDTO, and we have to repeat all this creation code.

Is there a way to do something like:

public async Task<IHttpActionResult> GetFoo()
    {
        var foos = db.Foos

        //DO STUFF

        return Ok(await foos.Select(m => _converter.Create(m)).ToListAsync());
    }

      

which doesn't work as Linq to Entities can't work with create function.

I'm also open to alternative ways of working with DTOs, is there a better way to do this, or is there no way to avoid extra traversal of all query results?

Oh trick, I want to do this without the automapper.

+3


source to share


1 answer


First, it's more like a code review question. I suggest you don't use entity framework (db in your code) directly in your controller. Controllers need to be thin and the query logic can be very complex. There are many situations where you need to query data from your database that cannot be mapped to entities. Because of this, you can create repository classes that directly return DTOs:

class FooRepository
{
   public async Task<List<FooDTO>> FindAsync()
   {
       using(var context = new DbContext())
       {
           return await context.Foos
               .Select(m => new FooDTO
               {
                   Id = m.Id,
                   ...
               })
               .ToListAsync();
       } 
   }
}

      



Another benefit of this approach is that you only ask for the data you really need, and now the whole entity.

Note: waiting is necessary - it is not possible to return a task directly here.

+5


source







All Articles