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


这个问题在这里已有答案:


7870
2018-02-21 17:20


起源



答案:


我在这里找到了答案: Web API Queryable - 如何应用AutoMapper?

而不是使用 [Queryable] 你可以使用类型的参数 ODataQueryOptions<T> 对任何类型或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);
}

这里的 微软页面 解释这种用法的具体细节。 (大约一半)


10
2018-03-05 17:53



我认为这种方法不再适用。除非将参数更改为ODataQueryOptions <PersonDto>,否则不会调用该方法。如果将参数更改为该参数,则会调用该方法,但odataQuery.ApplyTo()会抛出异常,表示该查询适用于PersonDto类型,而不是Person。 - sheamus
@sheamus,你需要申请到Dto类型。喜欢: odataQuery.ApplyTo(ConvertToDtos(uow.Person().GetAll())) - Eric Falsken
@EricFalsken不会导致所有人被拉入内存,所以ConvertToDtos可以对它们进行操作吗? - sheamus
更换 ConvertToDtos 同 Automapper.ConvertTo<PersonDto>() 要么 odataQuery.ApplyTo(db.Person().GetAll().To<PersonDto>()) 并且你会得到一个只会转换必要对象的LINQ查询,并且不会从数据库中获取所有内容,只要你使用双向对象绑定语法。 - Eric Falsken
@EricFalsken但如果在执行查询之前将其转换为DTO,您将如何进行关系?例如,我在尝试扩展时遇到此错误:在实体集“人员”上的实体类型“PersonDto”中找不到导航属性“地址”的NavigationLink工厂 - YesMan85


答案:


我在这里找到了答案: Web API Queryable - 如何应用AutoMapper?

而不是使用 [Queryable] 你可以使用类型的参数 ODataQueryOptions<T> 对任何类型或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);
}

这里的 微软页面 解释这种用法的具体细节。 (大约一半)


10
2018-03-05 17:53



我认为这种方法不再适用。除非将参数更改为ODataQueryOptions <PersonDto>,否则不会调用该方法。如果将参数更改为该参数,则会调用该方法,但odataQuery.ApplyTo()会抛出异常,表示该查询适用于PersonDto类型,而不是Person。 - sheamus
@sheamus,你需要申请到Dto类型。喜欢: odataQuery.ApplyTo(ConvertToDtos(uow.Person().GetAll())) - Eric Falsken
@EricFalsken不会导致所有人被拉入内存,所以ConvertToDtos可以对它们进行操作吗? - sheamus
更换 ConvertToDtos 同 Automapper.ConvertTo<PersonDto>() 要么 odataQuery.ApplyTo(db.Person().GetAll().To<PersonDto>()) 并且你会得到一个只会转换必要对象的LINQ查询,并且不会从数据库中获取所有内容,只要你使用双向对象绑定语法。 - Eric Falsken
@EricFalsken但如果在执行查询之前将其转换为DTO,您将如何进行关系?例如,我在尝试扩展时遇到此错误:在实体集“人员”上的实体类型“PersonDto”中找不到导航属性“地址”的NavigationLink工厂 - YesMan85


我能够使用ViewModel类成功测试它。

public class InvoiceViewModel
{
    public int InvoiceID { get; set; }
    public string InvoiceNumber { get; set; }
}

在Get中,从您的实体中选择您的viewmodel:

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行中使用viewmodel

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

有了这个,你可以使用像这样的网址

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

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


5
2018-03-01 09:29





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

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

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

1
2018-02-21 17:34



它实际上与使用AutoMapper无关。这是EntityFramework LINQ提供程序(至少使用DbContext,有人说ObjectContext不起作用)支持这样一个事实,即你可以在投影上查询结果(选择),它会识别并将其转换为查询原始数据源。 - Rich
LINQ如何允许针对viewmodel的查询转换为原始数据源上的查询?我不明白。我假设如果我们必须使用typeconverter(自定义方法)来映射类,它肯定不会起作用? - Eric Falsken
我明白IQueryable会允许OData查询针对翻译的类执行,但我不知道如果不检索所有可能的话这是如何工作的 Products 并将它们转换为 ProductDto 过滤前的实例。如何将OData查询转换为正确的SQL? - Eric Falsken
Eric,在你的Get中的某个时刻,你必须提供从DTO / ViewModel到EntityFramework类的一些翻译,无论是通过直接映射,如我的例子,还是通过自动映射器或类似的东西。 OData库可以使用它来确定如何将过滤器传递给实体框架,以便将Select转换为适当的SQL。 - Gareth Suarez