你的位置:首页 > ASP.net教程

[ASP.net教程]在ASP.NET Web API中使用OData的Containment


 

通常情况下,一个OData的EDM(Entity Data Model)在配置的时候定义了,才可以被查询或执行各种操作。比如如下:

 

builder.EntitySet<SomeModel>("SomeModels");

 

可能会这样查询:http://localhost:8888/odata/SomeModels

 

如果SomeModel中有一个集合导航属性,该如何获取呢?比如:

 

public class SomeModel{  public int Id{get;set;}    public IList<AnotherModel> AnotherModels{get;set;}}public class AnotherModel{ 

 

我们是否可以直接在SomeModel中获取所有的AnotherModel, 而不是通过如下方式获取:

 

builder.EntitySet<AnotherModel>("AnotherModels");http://localhost:8888/odata/AnotherModels

 

OData为我们提供了Containment,只要为某个集合导航属性加上[Contained]特性,就可以按如下方式获取某个EDM模型下的集合导航属性,比如:

 

http://localhost:8888/odata/SomeModels(1)/AnotherModels

 

好先定义模型。

 

public class Account{  public int AccountID { get; set; }  public string Name { get; set; }  [Contained]  public IList<PaymentInstrument> PayinPIs { get; set; }}public class PaymentInstrument{  public int PaymentInstrumentID { get; set; }  public string FriendlyName { get; set; }}

 

以上,一旦在PayinPIs这个集合导航属性上加上[Contained]特性,只要在controller中再提供获取集合导航属性的方法,我们就可以按如下方式,通过Account获取PaymentInstrument集合。如下:

 

http://localhost:8888/odata/Accounts(100)/PayinPIs

 

在WebApiConfig类中定义如下:

 

public static class WebApiConfig{  public static void Register(HttpConfiguration config)  {    ...    config.MapODataServiceRoute(      routeName: "ODataRoute",      routePrefix: "odata",      model: GetModel());  }  private static IEdmModel GetModel()  {    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();    builder.EntityType<PaymentInstrument>();    builder.EntitySet<Account>("Accounts");    return builder.GetEdmModel();  }}

 


在API端定义如下:

 

public class AccountsController : ODataController{  private static IList<Account> _accounts = null;  public AccountsController()  {    if(_accounts==null)    {      _accounts = InitAccounts();    }  }  [EnableQuery]  public IHttpActionResult Get()  {    return Ok(_accounts.AsQueryable());  }  [EnableQuery]  public IHttpActionResult GetById([FromODataUri] int key)  {    var account = _accounts.Single(a => a.AccountID == key);    return Ok(account);  }  //获取Account所有的PaymentInstrument集合  [EnableQuery]  public IHttpActionResult GetPayinPIs([FromODataUri]int key)  {    var payinPIs = _accounts.Single(a => a.AccountID == key).PayinPIs;    return Ok(payinPIs);  }  private static IList<Account> InitAccounts()  {    var accounts = new List<Account>()    {      new Account()      {        AccountID = 100,        Name="Name100",        PayoutPI = new PaymentInstrument()        {          PaymentInstrumentID = 100,          FriendlyName = "Payout PI: Paypal",        },        PayinPIs = new List<PaymentInstrument>()        {          new PaymentInstrument()          {            PaymentInstrumentID = 101,            FriendlyName = "101 first PI",          },          new PaymentInstrument()          {            PaymentInstrumentID = 102,            FriendlyName = "102 second PI",          },        },      },    };    return accounts;  }}

 

以上的GetPayinPIs方法可以让我们根据Account获取其集合导航属性PayinPIs。


好,现在PayinPIs加上了[Contained]特性,也配备了具体的Action,现在开始查询:

 

http://localhost:64696/odata/Accounts(100)/PayinPIs

 

能查询到所有的PaymentInstrument。

 

此时,PayinPIs集合导航属性在元数据中是如何呈现的呢?查询如下:

 

http://localhost:64696/odata/$metadata

 

相关部分为:

 

<EntityType Name="Account">  <Key>    <PropertyRef Name="AccountID" />  </Key>  <Property Name="AccountID" Type="Edm.Int32" Nullable="false" />  <Property Name="Name" Type="Edm.String" />  <NavigationProperty Name="PayinPIs" Type="Collection(MyODataContainmentSample.PaymentInstrument)" ContainsTarget="true" /></EntityType>

 

如果把PayinPIs上的[Contained]特性去掉呢?去掉后再次查询如下:

 

http://localhost:64696/odata/Accounts(100)/PayinPIs

 

返回404 NOT FOUND

 

再来看去掉[Contained]特性后相关的元数据:

<NavigationProperty Name="PayinPIs" Type="Collection(MyODataContainmentSample.PaymentInstrument)" />


没去掉[Contained]特性之前是:
<NavigationProperty Name="PayinPIs" Type="Collection(MyODataContainmentSample.PaymentInstrument)" ContainsTarget="true" />

 

原来,在一个集合导航属性上添加[Contained]特性,实际是让ContainsTarget="true",而默认状况下,ContainsTarget="false"。

 

 ^_^