Web API 2.2 を使用した OData v4 の包含
作成者: Jinfu Tan
従来、エンティティはエンティティ セット内にカプセル化されている場合のみアクセスできました。 ただし、OData v4 には、シングルトンと包含という 2 つの追加オプションがあり、どちらも WebAPI 2.2 でサポートされています。
このトピックでは、WebApi 2.2 の OData エンドポイントで包含を定義する方法について説明します。 包含の詳細については、「OData v4 に付属する包含」を参照してください。 Web API で OData V4 エンドポイントを作成するには、「ASP.NET Web API 2.2 を使用して OData v4 エンドポイントを作成する」を参照してください。
まず、次のデータ モデルを使用して、OData サービスに包含ドメイン モデルを作成します。
アカウントには多数の PaymentInstrument (PI) が含まれていますが、PI のエンティティ セットは定義しません。 代わりに、PI にはアカウント経由でのみアクセスできます。
データ モデルの定義
CLR 型を定義します。
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; } }
Contained
属性は包含ナビゲーション プロパティに使用されます。CLR 型に基づいて EDM モデルを生成します。
public static IEdmModel GetModel() { ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<Account>("Accounts"); var paymentInstrumentType = builder.EntityType<PaymentInstrument>(); var functionConfiguration = paymentInstrumentType.Collection.Function("GetCount"); functionConfiguration.Parameter<string>("NameContains"); functionConfiguration.Returns<int>(); builder.Namespace = typeof(Account).Namespace; return builder.GetEdmModel(); }
Contained
属性が対応するナビゲーション プロパティに追加された場合、ODataConventionModelBuilder
は EDM モデルの構築を処理します。 プロパティがコレクション型の場合は、GetCount(string NameContains)
関数も作成されます。生成されたメタデータは次のようになります。
<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(ODataContrainmentSample.PaymentInstrument)" ContainsTarget="true" /> </EntityType>
ContainsTarget
属性は、ナビゲーション プロパティが包含であることを示します。
包含エンティティ セット コントローラーを定義する
包含されるエンティティには独自のコントローラーがありません。アクションは、包含エンティティ セット コントローラーで定義されます。 このサンプルには AccountsController がありますが、PaymentInstrumentsController はありません。
public class AccountsController : ODataController
{
private static IList<Account> _accounts = null;
public AccountsController()
{
if (_accounts == null)
{
_accounts = InitAccounts();
}
}
// PUT ~/Accounts(100)/PayinPIs
[EnableQuery]
public IHttpActionResult GetPayinPIs(int key)
{
var payinPIs = _accounts.Single(a => a.AccountID == key).PayinPIs;
return Ok(payinPIs);
}
[EnableQuery]
[ODataRoute("Accounts({accountId})/PayinPIs({paymentInstrumentId})")]
public IHttpActionResult GetSinglePayinPI(int accountId, int paymentInstrumentId)
{
var payinPIs = _accounts.Single(a => a.AccountID == accountId).PayinPIs;
var payinPI = payinPIs.Single(pi => pi.PaymentInstrumentID == paymentInstrumentId);
return Ok(payinPI);
}
// PUT ~/Accounts(100)/PayinPIs(101)
[ODataRoute("Accounts({accountId})/PayinPIs({paymentInstrumentId})")]
public IHttpActionResult PutToPayinPI(int accountId, int paymentInstrumentId, [FromBody]PaymentInstrument paymentInstrument)
{
var account = _accounts.Single(a => a.AccountID == accountId);
var originalPi = account.PayinPIs.Single(p => p.PaymentInstrumentID == paymentInstrumentId);
originalPi.FriendlyName = paymentInstrument.FriendlyName;
return Ok(paymentInstrument);
}
// DELETE ~/Accounts(100)/PayinPIs(101)
[ODataRoute("Accounts({accountId})/PayinPIs({paymentInstrumentId})")]
public IHttpActionResult DeletePayinPIFromAccount(int accountId, int paymentInstrumentId)
{
var account = _accounts.Single(a => a.AccountID == accountId);
var originalPi = account.PayinPIs.Single(p => p.PaymentInstrumentID == paymentInstrumentId);
if (account.PayinPIs.Remove(originalPi))
{
return StatusCode(HttpStatusCode.NoContent);
}
else
{
return StatusCode(HttpStatusCode.InternalServerError);
}
}
// GET ~/Accounts(100)/PayinPIs/Namespace.GetCount()
[ODataRoute("Accounts({accountId})/PayinPIs/ODataContrainmentSample.GetCount(NameContains={name})")]
public IHttpActionResult GetPayinPIsCountWhoseNameContainsGivenValue(int accountId, [FromODataUri]string name)
{
var account = _accounts.Single(a => a.AccountID == accountId);
var count = account.PayinPIs.Where(pi => pi.FriendlyName.Contains(name)).Count();
return Ok(count);
}
private static IList<Account> InitAccounts()
{
var accounts = new List<Account>()
{
new Account()
{
AccountID = 100,
Name="Name100",
PayinPIs = new List<PaymentInstrument>()
{
new PaymentInstrument()
{
PaymentInstrumentID = 101,
FriendlyName = "101 first PI",
},
new PaymentInstrument()
{
PaymentInstrumentID = 102,
FriendlyName = "102 second PI",
},
},
},
};
return accounts;
}
}
OData パスが 4 つ以上のセグメントの場合、上記のコントローラーの [ODataRoute("Accounts({accountId})/PayinPIs({paymentInstrumentId})")]
など、属性ルーティングのみが機能します。 それ以外の場合は、属性と従来のルーティングの両方が機能します。たとえば、GetPayInPIs(int key)
は GET ~/Accounts(1)/PayinPIs
に一致します。
この記事のオリジナル コンテンツの作成者である Leo Hu に感謝します。