OData按ID和名称获取资源

时间:2016-03-13 08:19:27

标签: asp.net-web-api odata

我有这个代码示例,我试图让第三个Get方法工作,并且$expand也可以按照下面的说明工作。

如果我只使用方法Get()Get([FromODataUri] int key),我可以使用这些相应的路线/odata/Products/odata/Products(1)?$expand=Vendor来调用它们。 但是,当我做出以下更改时,我会遇到以下问题。

  1. 如果我只将参数名称key更改为id或方法Get([FromODataUri] int key)中的任何其他内容,则不再调用该方法。 /odata/Products(1)的路由呼叫转到Get()方法,该方法始终返回所有集合。

  2. 如果我将参数名称放回key,但将方法更改为其他内容,例如GetByKey([FromODataUri] int key),然后再次出现同样的问题。

  3. 此外,如果我使用/odata/Products?key=1?$expand=Vendor拨打电话,则会拨打GetByKey([FromODataUri] int key),但我不会扩展供应商。

  4. 并在GetByName([FromODataUri] string name)下面添加第3种方法,我得到"找不到错误404"使用此路线调用/odata/Products('Product 1')时。如果使用/odata/Products?name='Product 1'/odata/Products?name='Product 1'$expand=Vendor进行调用,则会触发Get()方法,并且供应商不会再次展开。

  5. 感谢您对此的意见。

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
    
            // Web API routes
            config.MapHttpAttributeRoutes();
    
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
    
            ODataModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<Product    ("Products");
            config.MapODataServiceRoute("ODataRoute", "odata", builder.GetEdmModel());
    
        }
    }
    
    public class ProductsController : ODataController
    {
        #region 
    
        ProductsContext db = new ProductsContext();
        private bool ProductExists(int key)
        {
            return db.Products.Any(p =     p.Id == key);
        }
        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    
        #endregion
    
        [HttpGet]
        [EnableQuery]
        public IQueryable<Product     Get()
        {
            return db.Products.AsQueryable();
        }
    
        [HttpGet]
        [EnableQuery]
        public SingleResult<Product     Get([FromODataUri] int key)
        {
            IQueryable<Product     result = db.Products.Where(p =     p.Id == key).AsQueryable();
            return SingleResult.Create(result);
        }
    
        [HttpGet]
        [EnableQuery]
        public SingleResult<Product     GetByName([FromODataUri] string name)
        {
            IQueryable<Product     result = db.Products.Where(p =     p.Name == name).AsQueryable();
            return SingleResult.Create(result);
        }
    }
    
    public class Product
    {
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        public decimal Price { get; set; }
    
        public string Category { get; set; }
    
        public Vendor Vendor { get; set; }
    
    }
    
    public class Vendor
    {
        public int Id { get; set; }
    
        public string Name { get; set; }
    
    }
    
    public class ProductsContext : IDisposable
    {
        public ICollection<Product     Products = new List<Product    ()
        {
            new Product()
            {
                Category = "A",
                Id = 1,
                Name = "Product 1",
                Price = 10,
                Vendor = new Vendor()
                {
                    Id = 1,
                    Name = "Vendor 1"
                }
            },
    
            new Product()
            {
                Category = "A",
                Id = 2,
                Name = "Product 2",
                Price = 15,
                Vendor = new Vendor()
                {
                    Id = 1,
                    Name = "Vendor 1"
                }
            },
    
            new Product()
            {
                Category = "B",
                Id = 3,
                Name = "Product 3",
                Price = 10,
                Vendor = new Vendor()
                {
                    Id = 2,
                    Name = "Vendor 2"
                }
            },
        };
    
        public void Dispose()
        {
    
        }
    }
    

2 个答案:

答案 0 :(得分:2)

因为,#1,#2,#4不起作用,因为操作名称或参数不遵循Web API OData路由约定。约定是一组默认的rules,开发人员应该跟进这些默认值以确保Web API OData可以将请求路由到控制器中的正确方法。

对于#3,因为它使用Web API路由,那是

  

API / {控制器} / {行动} / {ID}

不使用Web API OData路由。

希望材料here可以帮助您理解Web API OData中的路由。感谢。

答案 1 :(得分:2)

您的代码正在使用OData的ASP.NET实现的built-in routing conventions,而您遇到的问题是这些约定的结果。

  1. 在URI路径/odata/Products(1)中,(1)部分称为关键细分。按照惯例,key是用于保存键段值的参数的名称。不幸的是,这与将模型的关键属性命名为Id的惯例不符。您可以使用attribute routing覆盖此路由约定,但我认为这不值得。

  2. 路由惯例是expecting一个名为GetProduct或仅Get的方法。同样,您可以使用ODataRouteAttribute

  3. 覆盖传统行为
  4. 检索Products实体集成员的正确URI是/odata/Products(id)。在我的测试环境中,GET /odata/Products?key=1&$expand=Vendor未路由到GetByKey。相反,它被路由到无参数Get方法,因为它应该根据路由约定(因为请求URI中没有关键段)。

  5. 如果Product实体的Name属性是备用键,则可以利用ASP.NET OData中的alternate key support。您可以按名称检索产品,如下所示:GET /odata/Products(Name='name')。有关如何在控制器中实现备用密钥的说明,请参阅上一个问题的my answer