在ASP.NET Web API中使用OData的Action和Function

 

本篇体验OData的Action和Function功能。上下文信息参考"ASP.NET Web API基于OData的增删改查,以及处理实体间关系"。在本文之前,我存在的疑惑包括:

 

● 为什么需要OData的Action和Function功能?
● Action和Function之间有什么区别?
● 如何设置OData的的Action和Function功能?这中间有哪些惯例呢?

 

为某个Product添加Action

 

如果我们希望通过http://localhost:54714/odata/Products(1)/SomeActionName来实现在某个Product基础上,为Product的关联表ProductRating添加一条数据,该如何做到呢?

首先创建产品评论的一个模型:

 

public class ProductRating
{
    public int ID { get; set; }
    public int Rating { get; set; }
    public int ProductID { get; set; }
    public virtual Product Product { get; set; }
}

 

把模型添加到上下文中:

 

public class ProductsContext : DbContext
{
    public ProductsContext()
           : base("name=ProductsContext")
    {
    }
    public DbSet<Product> Products { get; set; }
    public DbSet<Supplier> Suppliers { get; set; }
    public DbSet<ProductRating> Ratings { get; set; }
}

 

添加迁移并更新数据库:

Add-Migration "AddProductRating" -StartUpProjectName ProductService -ProjectName ProductService
Update-Database  -StartUpProjectName ProductService -ProjectName ProductService  

现在,需要在WebApiConfig.cs中的Register方法中,为Product的EDM添加一个Action。

 

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "ProductService";

builder.EntitySet<Product>("Products");//创建EntityDataModel(EDM)
builder.EntitySet<Supplier>("Suppliers");


//http://localhost/Products(1)/ProductService.Rate 注意需要在Web.config中添加配置,因为在IIS上不允许带点,否则会返回404
builder.EntityType<Product>()
    .Action("Rate")//给EDM添加一个Action
    .Parameter<int>("Rating"); //Rating作为参数在前后端传递



config.MapODataServiceRoute(
    routeName: "ODataRoute",
    routePrefix: "odata", 
    model:builder.GetEdmModel());

 

以上,

● 通过builder.Namespace定义了Action的命名空间为ProductService
● 通过Action方法给Product这个EDM定义了Rate这个Action
● 通过Parameter<T>方法定义了参数



这意味着:

● 我们发出的请求格式大致是:http://localhost:54714/odata/Products(1)/ProductService.Rate
● API的action方法中,action名称是Rate,可以从前端接受一个名称为Rating的键值



可问题还有:

● 前端如何把Rating这个参数传递出去呢?
● 后端又如何接受这个Rating参数呢?

来看后端控制器部分的action,在ProductsController中添加如下:

 

//这里的action名称Rate必须和EDM定义的时候保持一致
[HttpPost]
public async Task<IHttpActionResult> Rate([FromODataUri] int key, ODataActionParameters parameters)
{
    //先验证
    if(!ModelState.IsValid)
    {
        return BadRequest();
    }

    //再取值
    int rating = (int)parameters["Rating"];

    //实施操作
    db.Ratings.Add(new ProductRating
    {
        ProductID = key,
        Rating = rating
    });

    //捕获异常
    try
    {
        await db.SaveChangesAsync();
    }
    catch (DbUpdateException ex)
    {
        if (!ProductExists(key))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return StatusCode(HttpStatusCode.NoContent);

}

 

可见,后端是通过ODataActionParameters来接受前端传来的变量Rating。

现在前端可以试着发出请求了:

POST http://localhost:54714/odata/Products(1)/ProductService.Rate
Body {"Rating":5}

我们把Rating变量放在了Body中,以json传递给后端。

可是,返回结果是: 404 Not Found

这是因为,IIS还不接受类似ProductService.Rate这样的写法,在Web.config添加如下配置:

 

<system.webServer>
<handlers>
  ...
  <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="/odata/*" verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>

 

重新请求,返回:204 No Content

 

为Product集合添加Action

 

如果想为Products集合添加如下方法发出如下请求:http://localhost:54714/odata/Products/ProductService.MostExpensive

首先还是配置EDM:

 

builder.EntityType<Product>().Collection
        .Function("MostExpensive")
        .Returns<double>();
        

 

在ProductsController中添加如下Action:

 

[HttpGet]
public IHttpActionResult MostExpensive()
{
    var product = db.Products.Max(x => x.Price);
    return Ok(product);
}

 

前端请求:

GET http://localhost:54714/odata/Products/ProductService.MostExpensive
返回:
{
  "@odata.context": "http://localhost:54714/odata/$metadata#Edm.Decimal",
  "value": 18.2
}

和EDM模型无关,添加Function

当我们需要添加一个与EDM 模型无关的方法时候,就使用Function。

首先在WebApi.config中配置如下:

 builder.Function("GetSalesTaxRate")
            .Returns<double>()
            .Parameter<int>("PostalCode");

 

在某个Controller中添加如下:

 

[HttpGet]
[ODataRoute("GetSalesTaxRate(PostalCode={postalCode})")]
public IHttpActionResult GetSalesTaxRate([FromODataUri] int postalCode)
{
    double rate = 5.8;
    return Ok(rate);
}

 

以上,ODataRoute设定路由规则。前端发出如下请求:

GET http://localhost:54714/odata/GetSalesTaxRate(PostalCode=10)
返回:

{
  "@odata.context": "http://localhost:54714/odata/$metadata#Edm.Double",
  "value": 5.8
}


总结:当需要针对某个EDM模型或EDM模型集合进行CRUD以外的操作,甚至涉及多个模型的操作使用Action,但添加和EDM模型无关的操作,使用Function。

 

posted @ 2015-11-08 12:00  Darren Ji  阅读(1838)  评论(2编辑  收藏  举报

我的公众号:新语新世界,欢迎关注。