使用 EF 和 WebAPI,如何返回 ViewModel 并支持 IQueryable/OData? [复制]

Posted

技术标签:

【中文标题】使用 EF 和 WebAPI,如何返回 ViewModel 并支持 IQueryable/OData? [复制]【英文标题】:Using EF and WebAPI, how can I return a ViewModel AND support IQueryable/OData? [duplicate] 【发布时间】:2013-02-07 03:25:03 【问题描述】:

我有一个 ASP.NET WebAPI 项目。我最近为我的所有数据表创建了 EntityFramework 实体。但我不想将我的数据层和架构暴露给我的用户。如何将我的实体映射到 ViewModel(自动映射器?)提供 IQueryable 返回类型,以便我的 API 支持 OData?

OData 支持查询组合和类似 SQL 的参数。我想我需要为查询组合部分提供某种双向翻译?这是否意味着自定义 LINQ 提供程序?我希望它比那更容易。

或者我应该放弃 IQueryable/OData 吗?

【问题讨论】:

【参考方案1】:

我在这里找到了答案:Web API Queryable - how to apply AutoMapper?

您可以使用ODataQueryOptions<T> 类型的参数而不是使用[Queryable] 来针对您希望的任何类型或LINQ 查询应用OData 操作。这是一个甚至不需要使用 AutoMapper 的好例子:

public virtual IQueryable<PersonDto> Get(ODataQueryOptions<Person> odataQuery)
    odataQuery.Validate(new ODataValidationSettings()
        AllowedFunctions = AllowedFunctions.AllMathFunctions
    );
    var people = odataQuery.ApplyTo(uow.Person().GetAll());
    return ConvertToDtos(people);

the Microsoft page 解释了这种用法的细节。 (大约一半)

【讨论】:

我认为这种方法不再有效。除非参数更改为 ODataQueryOptions,否则不会调用该方法。如果将参数更改为该参数,则确实会调用该方法,但 odataQuery.ApplyTo() 会引发异常,表示查询适用于 PersonDto 类型,而不是 Person。 @sheamus,您需要 ApplyTo 类型的 Dto。喜欢:odataQuery.ApplyTo(ConvertToDtos(uow.Person().GetAll())) @EricFalsken 不会导致所有 Person 都被拉入内存以便 ConvertToDtos 可以对它们进行操作吗? ConvertToDtos 替换为Automapper.ConvertTo&lt;PersonDto&gt;()odataQuery.ApplyTo(db.Person().GetAll().To&lt;PersonDto&gt;()),您将得到一个LINQ 查询,该查询只会转换必要的对象,而不会从数据库中取回所有内容,只要您使用 2-way 对象绑定语法。 @EricFalsken 但是如果您在执行查询之前将其转换为 DTO,您将如何处理关系?例如,我在尝试扩展时收到此错误:No NavigationLink factory was found for the navigation property 'Address' from entity type 'PersonDto' on entity set 'Persons'【参考方案2】:

我能够使用 ViewModel 类成功地对此进行测试。

public class InvoiceViewModel

    public int InvoiceID  get; set; 
    public string InvoiceNumber  get; set; 

在 Get 中,从您的实体中选择到您的视图模型中:

public override IQueryable<InvoiceViewModel> Get()
    
        var ctx = new CreditPointEntities();
        return ctx.Invoices.Select(i => new InvoiceViewModel
            
                InvoiceID = i.InvoiceID,
                InvoiceNumber = i.InvoiceNumber
            ).AsQueryable();
    

确保在 webapiconfig.cs 的模型构建器行中使用视图模型

modelBuilder.EntitySet<InvoiceViewModel>("Invoice");

有了这个,你可以使用像

这样的 url

http://website/odata/Invoice?$filter=InvoiceID eq 1

我通过 sql profiler 确认过滤器正在传递给 SQL。

【讨论】:

【参考方案3】:

如果您使用 Automapper,您可以在其中使用投影。示例:

    public class ProductsController : EntitySetController<Product, int>
    
         private DbProductsContext _db = new DbProductsContext();

         public override IQueryable<ProductDto> Get()
         
            return _db.Products.Project().To<ProductDto>();
         
    ...

【讨论】:

真的不管用AutoMapper。这是因为 EntityFramework LINQ 提供程序(至少对于 DbContext,有人说 ObjectContext 不起作用)支持您可以在投影(选择)上查询结果并且它将识别并将其转换为查询原始数据源。 LINQ 如何允许将针对视图模型的查询转换为对原始数据源的查询?我不明白。我认为如果我们必须使用类型转换器(自定义方法)来映射类,它肯定行不通? 我知道 IQueryable 将允许 OData 查询针对已翻译的类执行,但如果不检索所有可能的 Products 并将它们转换为 ProductDto 实例,我看不出这将如何工作.如何将 OData 查询转换为正确的 SQL? Eric,在您的 Get 中的某个时刻,您必须提供从 DTO/ViewModel 到 EntityFramework 类的一些翻译,无论是通过直接映射(如我的示例中),还是通过自动映射器或类似的东西. OData 库可以使用它来确定如何将过滤器传递给实体框架,以便将您的 Select 转换为正确的 SQL。

以上是关于使用 EF 和 WebAPI,如何返回 ViewModel 并支持 IQueryable/OData? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

使用 Asp.Net WebApi + EF + Odata 深度插入数据

AngularJs + WebApi + EF + SqlServer 一步一步搭建项目

关于在webapi + ef + 视图 + top查询的问题

datagrid与webAPI的数据交互(ef mvc )

WebApi系列详解WebApi如何传递参数

Asp.net WebApi + EF 单元测试架构 DbContext一站到底