0

I am creating a MS Web API 2 project. I have created my Entity Framework in a separate project and am referencing it in my API. Reading over a few tutorials, it is suggested that:

"ideally, we should not return EF entity objects from the Web API. It is recommended to return DTO (Data Transfer Object) from Web API".

Hence, I have created my model in my API:

namespace MyAPI.Models
{
  [Table("Customer")]
  public class CustomerViewModel
  {
    [Key]
    public int CustomerID { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
  }
}

My question is: Do I need to create a data context class for each model in my API or is it fine to use EF context class? And if I do need to create a separate context for each model, how can I achieve this by a reference to the EF context class? Below is what I have started with:

namespace MyAPI.Models
{
   public class CustomerDbContext : DbContext
   {
       public CustomerDbContext() : base("name=CusetomerDbContext")
       {
       }

       public DbSet<MyEFDataAccess.Customer> CustomerViewModel { get; set; }
   }
}

And my Controller is:

namespace MyAPI.Controllers
{
   public class CustomersController : ApiController
   {
    private readonly CustomerDbContext _context = new CustomerDbContext();

    // GET: api/Customer
    public IQueryable<CustomerViewModel> GetCustomerViewModels()
    {
        return _context.CustomerViewModel;
    }
}

The above correctly throws an error because it cannot convert EF customer to CustomerViewModel directly!

3
  • you show two unrelated pieces of code. Please show the code where you're attempting to use them together. Commented Jun 6, 2018 at 20:53
  • @cwharris Edited my post Commented Jun 6, 2018 at 20:55
  • "It is recommended to return DTO (Data Transfer Object) from Web API" - that's not at all what it is trying to say to you: you should have two separated classes for your entity (used with entity framework) and for your API (your DTO), and then map one object to the other: learn.microsoft.com/en-us/aspnet/web-api/overview/data/… Commented Jun 6, 2018 at 20:59

1 Answer 1

3

ideally, we should not return EF entity objects from the Web API. It is recommended to return DTO (Data Transfer Object) from Web API.

The reason for this is to make sure you can change your DB schema without changing API, and vice versa. If you accomplish that goal, then you've adhered to that given advice.

The problem you're having is basic. Type A cannot be implicitly converted to Type B. As far as the compiler is concerned, you're trying to convert a DbContext to a FormControl, and it has no idea how to do that. You need to tell it explicitly what to do. One example, albeit not great:

public DbSet<MyEFDataAccess.Customer> Customer { get; set; }

and

public IQueryable<CustomerViewModel> GetCustomerViewModels()
{
    return _context.Customer.Select(
        customer => new CustomerViewModel
        {
            // <assign properties here>
        }
    );
}

That being said, returning an IQueryable<T> from your Controller is a definite no-no. You definitely want to allow for the consumer to query specific records. You could do this to enable pagination, for instance:

public async Task<List<CustomerViewModel>> GetCustomerViewModels(
    int skip = 0,
    int take = 100
)
{
    return await _context.Customer
        .Skip(skip)
        .Take(take)
        .Select(
            customer => new CustomerViewModel
            {
                // <assign properties here>
            }
        )
        .ToListAsync();
}
Sign up to request clarification or add additional context in comments.

16 Comments

"easy" is not a word I would use to describe DB schema changes. You can get used to it, but that doesn't make it inherently easy. EntityFramework provides the ability to generate Migrations, which it uses each time (generally speaking) the DbContext is generated. It checks a hash in the DB to see what version of the DbContext you're running, and then upgrades the DB if necessary.
A DbContext represents a DB. Have one for each database. A DbSet<> represents a DB Table. You'll have as many of these as you need in a single DB / DbContext to support a normalized relational data model. But lets say you want to add a field to a table in your DB (a property on your Entity model), but you don't want that property to show up in your API responses - that is why you need two separate classes to represent the same "thing".
For each given representation of the data, you should have a different class. But you should only have as many representations as is helpful for you, and you should probably have some "source of truth" representation that all of the others convert to/from (even if that's multiple normalized tables in a database).
IQueryable<T> is not supported by ASP.NET MVC Controllers. IQueryable implements IEnumerable, and MVC supports IEnumerable. When you enumerate through a queryable, it executes its query and yields the results. The API endpoint in your question would have always returned every record from the database. Which is almost certainly not what you had in mind... considering databases could contain millions of rows.
@cwharris What? ASP.NET MVC certainly supports IQueryable<T>. It is a terrible idea to return them directly due to the disposal of the context in a proper implementation, but it is certainly possible to do so with for example a singleton context. Also, the ToListAsync in this answer returns every single item as well, so your comment makes absolutely no sense.
|

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.