【问题标题】:ASP.NET Web Api: Correct way to serve OData-queryable GET requestsASP.NET Web Api:提供 OData 可查询 GET 请求的正确方法
【发布时间】:2013-09-05 22:33:42
【问题描述】:

在 ASP.NET Web Api 中提供 OData 可查询 GET 请求的正确方法是什么?这听起来像是一个“什么更好”的问题,但它应该是一个“什么有效”的问题。

一些假设:

  • 要启用 OData 查询,您必须将 Queryable 属性放在返回 IQueryable<Model> 的操作方法中。因此,您必须公开领域模型?
  • 域模型使用 Entity Framework 5 并具有导航属性。 XML 和 Json 序列化程序不喜欢 EF 代理,所以您必须为 OData 查询禁用它们?
  • 序列化程序获取导航属性并将其提供给用户。

因此,如果我有一个具有父子导航属性的 Category 类型,序列化程序会抱怨我有循环引用,我无法摆脱这个错误。

我已经读到我应该使用 DTO,但是如何使用?如何向将为数据库创建适当 SQL 的用户提供 IQueryable<DTOModel>?记住,我想用$filter之类的。

我只想给用户一个可过滤的模型对象列表,没有序列化的导航属性....但是如何?

【问题讨论】:

    标签: json asp.net-web-api entity-framework-5 odata


    【解决方案1】:

    您不必公开IQueryable<> - 您可以创建一个接受ODataQueryOptions 实例的方法并自己处理它。这是一个代码示例,可以满足您的大部分需求。对于您来说,找出最适合您的解决方案应该绰绰有余。此方法还允许您保留 EF 代理类。

    using System.Web.Http.OData;
    using System.Web.Http.OData.Builder;
    using System.Web.Http.OData.Query;
    [ActionName("Dto")]
    public IList<DtoModel> GetDto(ODataQueryOptions<DtoModel> queryOptions)
    {
        var data2 = DatabaseData();
    
        //Create a set of ODataQueryOptions for the internal class
        ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
        modelBuilder.EntitySet<Model>("Model");
        var queryContext = new ODataQueryContext(
             modelBuilder.GetEdmModel(), typeof(Model));
        var newQueryOptions = new ODataQueryOptions<Model>(queryContext, Request);
    
        var t = new ODataValidationSettings() { MaxTop = 25 };
        var s = new ODataQuerySettings() { PageSize = 25 };
        newQueryOptions.Validate(t);
        IEnumerable<Model> results =
            (IEnumerable<Model>)newQueryOptions.ApplyTo(data2, s);
    
        int skip = newQueryOptions.Skip == null ? 0 : newQueryOptions.Skip.Value;
        int take = newQueryOptions.Top == null ? 25 : newQueryOptions.Top.Value;
    
        IList<Model> internalResults = results.Skip(skip).Take(take).ToList();
    
        // map from Model to Dto here using AutoMapper
        AutoMapper.Mapper.CreateMap<Model, DtoModel>();
        IList<DtoModel> webResults =
            AutoMapper.Mapper.Map<IList<Model>, IList<DtoModel>>(internalResults);
    
        return webResults;
    }
    

    示例中使用的数据是一个简单的Queryable 数据集:

    private IQueryable<Model> DatabaseData()
    {
        return (
            new Model[] { 
            new Model() { id = 1, name = "one", type = "a" },
            new Model() { id = 2, name = "two", type = "b" },
            new Model() { id = 3, name = "three", type = "c" },
            new Model() { id = 4, name = "four", type = "d" },
            new Model() { id = 5, name = "five", type = "e" },
            new Model() { id = 6, name = "six", type = "f" },
            new Model() { id = 7, name = "seven", type = "g" },
            new Model() { id = 8, name = "eight", type = "h" },
            new Model() { id = 9, name = "nine", type = "i" }
        })
        .AsQueryable();
    }
    

    这些是测试类:

    public class Poco
    {
        public int id { get; set; }
        public string name { get; set; }
        public string type { get; set; }
    }
    public class DtoModel
    {
        public int id { get; set; }
        public string name { get; set; }
        public string type { get; set; }
    }
    public class Model
    {
        public int id { get; set; }
        public string name { get; set; }
        public string type { get; set; }
        public virtual ICollection<Poco> Pocos { get; set; }
    }
    

    【讨论】:

    • 您是否找到了一种模拟 ODataQueryOptions 以对控制器进行单元测试的方法?我很难将这种类型作为 PITA。
    猜你喜欢
    • 2020-09-19
    • 2012-06-15
    • 2016-11-26
    • 2013-08-17
    • 2018-10-25
    • 1970-01-01
    • 2021-07-11
    • 2023-03-12
    • 1970-01-01
    相关资源
    最近更新 更多