简体   繁体   中英

Web Api 2 + EF6 - Building a DTO

For a project we are making use of EF and Web Api 2. To abstract away from our database models we are making use of DTO's. We have a factory to construct these DTO's:

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 implies that, after we execute the query, we have to loop over all our results and convert each model to a DTO.

On the other hand, 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());
    }

Which would integrate this projection into the query executed by EF. But this exposes all the inner details of the 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 does not work, because Linq to Entities can't deal with the create function.

I'm also open to alternative ways to work with DTO's, is there a better way to do this, or is there no way to avoid the extra pass through all query results?

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

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

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 advantage of this approach is that you query only data you really need, and now whole entity.

Note: await is necessary - cannot return Task directly here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM