【问题标题】:ASP.NET Web API 2 CRUD Operation with Entity FrameworkASP.NET Web API 2 CRUD 操作与实体框架
【发布时间】:2018-06-06 20:49:26
【问题描述】:

我正在创建一个 MS Web API 2 项目。我在一个单独的项目中创建了我的实体框架,并在我的 API 中引用它。阅读了一些教程,建议:

“理想情况下,我们不应该从 Web API 返回 EF 实体对象。建议从 Web API 返回 DTO(数据传输对象)”。

因此,我在我的 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; }
  }
}

我的问题是:我是否需要在我的 API 中为每个模型创建一个数据上下文类,还是可以使用 EF 上下文类?如果我确实需要为对于每个模型,如何通过引用 EF 上下文类来实现这一点?以下是我开始的内容:

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

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

我的控制器是:

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

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

上面正确抛出错误,因为它不能直接将EF客户转换为CustomerViewModel!

【问题讨论】:

  • 你展示了两段不相关的代码。请在您尝试将它们一起使用的地方显示代码。
  • @cwharris 编辑了我的帖子
  • “建议从 Web API 返回 DTO(数据传输对象)” - 这根本不是它想要对你说的:你应该有 为您的实体(与实体框架一起使用)和您的 API(您的 DTO)提供两个独立的类,然后将一个对象映射到另一个对象:docs.microsoft.com/en-us/aspnet/web-api/overview/data/…

标签: c# entity-framework asp.net-web-api


【解决方案1】:

理想情况下,我们不应从 Web API 返回 EF 实体对象。它 建议从 Web API 返回 DTO(Data Transfer Object)。

这样做的原因是确保您可以在不更改 API 的情况下更改数据库架构,反之亦然。如果你实现了这个目标,那么你就已经遵守了给定的建议。

您遇到的问题是基本的。类型 A 不能隐式转换为类型 B。就编译器而言,您正在尝试将DbContext 转换为FormControl,但它不知道该怎么做。您需要明确告诉它要做什么。一个例子,虽然不是很好:

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

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

话虽如此,从控制器返回IQueryable&lt;T&gt; 是绝对不行的。您肯定希望允许消费者查询特定记录。您可以这样做以启用分页,例如:

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();
}

【讨论】:

  • “easy”不是我用来描述数据库架构更改的词。你可以习惯它,但这并不容易。 EntityFramework 提供了生成迁移的能力,它每次(一般来说)生成 DbContext 时都会使用它。它检查数据库中的哈希以查看您正在运行的 DbContext 版本,然后在必要时升级数据库。
  • A DbContext 代表一个数据库。每个数据库都有一个。 DbSet&lt;&gt; 代表一个 DB 表。您将在单个 DB / DbContext 中拥有尽可能多的这些,以支持规范化的关系数据模型。但是假设您想向数据库中的表添加一个字段(实体模型上的一个属性),但您不希望该属性显示在您的 API 响应中 - 是原因您需要两个单独的类来表示相同的“事物”。
  • 对于每个给定的数据表示,您应该有一个不同的类。但是您应该只拥有对您有帮助的尽可能多的表示,并且您可能应该有一些“真实来源”表示,所有其他表示都可以转换为/从(即使这是数据库中的多个规范化表)。
  • IQueryable&lt;T&gt; 不受 ASP.NET MVC 控制器支持。 IQueryable 实现了IEnumerable,MVC 支持IEnumerable。当您枚举一个可查询对象时,它会执行其查询并产生结果。您问题中的 API 端点将始终返回数据库中的每条记录。这几乎肯定不是您的想法……考虑到数据库可能包含数百万行。
  • @cwharris 什么? ASP.NET MVC当然支持IQueryable&lt;T&gt;。由于在适当的实现中对上下文的处理,直接返回它们一个糟糕的主意,但是对于例如单例上下文当然可以这样做。此外,此答案中的 ToListAsync 也会返回每个项目,因此您的评论绝对没有意义。
猜你喜欢
  • 2020-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多