【问题标题】:ASP.NET Web API return queryable DTOs?ASP.NET Web API 返回可查询的 DTO?
【发布时间】:2012-04-11 06:28:10
【问题描述】:

我使用 ASP.NET Web API 构建了一个不错的小 API,但我想从我的上下文(实体框架)AsQueryable 返回实体是不正确的,所以我将所有内容都映射到 DTO 对象。

但是我不太明白:如何保持上下文可查询,但仍然只返回 DTO 而不是实体?或者这不可能?

这是我的代码:

public IQueryable<ItemDto> Get()
{
    using (EfContext context = new EfContext())
    {
        Mapper.CreateMap<Item, ItemDto>()
            .ForMember(itemDto => itemDto.Category, mce => mce.MapFrom(item => item.Category.Name));

        IEnumerable<ItemDto> data = Mapper.Map<IEnumerable<Item>, IEnumerable<ItemDto>>(context.Items
            .OrderByDescending(x => x.PubDate)
            .Take(20));

        return data.AsQueryable();
    }
}

如您所见,我加载了数据,并使那个小的 IEnumerable 集合可查询。问题是为这段代码生成的查询可能效率很低,因为它首先加载所有项目(或至少前 20 个项目),然后过滤输出。

我希望我尽可能地描述我的问题,这有点难以解释。我在 Google 上找不到任何关于它的信息。

【问题讨论】:

  • 根本不要将 Web API 端点公开为 IQueryable...如果您真的需要,请使用 Web API OData。否则,只需坚持使用普通的旧 REST 端点,并在控制器操作上公开任何可能的过滤作为参数。

标签: asp.net linq entity-framework asp.net-web-api


【解决方案1】:

不要先选择内存中的所有内容。做这样的事情:

public IQueryable<ItemDto> Get()
{
    using (EfContext context = new EfContext())
    {
        var query = from item in context.Items
                    select Mapper.Map<Item, ItemDto>(item)

        return query.OrderByDescending(x => x.PubDate).Take(20));
    }
}

顺便说一句,以下代码是您想做一次的事情,例如在静态构造函数或WebApiConfig.cs 文件中。

Mapper.CreateMap<Item, ItemDto>()
    .ForMember(itemDto => itemDto.Category, mce => mce.MapFrom(item => item.Category.Name));

【讨论】:

  • 这在 EF4 中有效吗?我记得最好,EF 不允许您映射到未在 EF 中定义的类型。
  • 所以我只需要在应用程序启动时定义一次映射?不知道那个。感谢您指出:)
  • @Ryan。在 EF 查询中,您正在检索 EF 实体,只有加载的实体才会使用 AutoMapper 转换为 DTO。但是这样你会得到延迟加载,所以订单/过滤器是在数据库中完成的,只有最多 20 条记录从 EF 实体映射到 DTO。
  • @Avalaxy。是的,它只需要执行一次。 Mapper.CreateMap() 是一个相对缓慢和昂贵的调用来加速 Mapper.Map()。
  • 你应该在进入数据库之前执行Take(20)。否则,您将切分效率不高的内存集合。代码:var query = from item in context.Items.Take(20) select Mapper.Map(Item, ItemDto&gt;(item);
【解决方案2】:

如果我们看到的代码中只发生了查询(即 ordering 和 Take ),那么您的代码就可以了。它只会映射结果(最多 20 个)。但是,由于您返回的是 IQueryable,我假设您想进一步查询结果。可能是 OData 样式参数?

最多有 20 个项目,最好不要编写任何代码。其余查询将作为对象查询执行。但是,如果您决定删除该约束(最多 20 个)或在进行进一步查询后将其放置,那么这种方式将是低效的。

基本上,如果您希望所有查询都在 EF 数据库中运行,则需要在查询链的最后移动映射。

你可以做的实际上是返回实际的实体对象

    public IQueryable<ItemDto> Get()
    {
        using (EfContext context = new EfContext())
        {
            return context.items
                       .OrderByDescending(x => x.PubDate)
                       .Take(20));
         }
     }

并告诉 MVC 如何在 MediaTypeFormatter 中序列化它。在这里您可以使用 AutoMapper。

【讨论】:

    【解决方案3】:

    http://dotnetplusplus.wordpress.com/2013/08/30/odata-web-apis-with-automapper-3/

    使用 return _itemRepository .GetItemsQuery() .Project().To();

    【讨论】:

    • 当导航属性可从两端导航时,Project().To() 会引发 StackOverflowException。有什么办法克服吗?
    猜你喜欢
    • 1970-01-01
    • 2012-06-22
    • 1970-01-01
    • 1970-01-01
    • 2013-01-14
    • 2015-01-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多