Zawieranie w usłudze OData w wersji 4 przy użyciu internetowego interfejsu API 2.2
Autor: Jinfu Tan
Tradycyjnie można uzyskać dostęp do jednostki tylko wtedy, gdy została hermetyzowana wewnątrz zestawu jednostek. Jednak protokół OData w wersji 4 udostępnia dwie dodatkowe opcje: Singleton i Containment, z których oba obsługują interfejs WebAPI 2.2.
W tym temacie pokazano, jak zdefiniować zawieranie w punkcie końcowym OData w interfejsie WebApi 2.2. Aby uzyskać więcej informacji na temat zawierania, zobacz Zawieranie pochodzi z OData w wersji 4. Aby utworzyć punkt końcowy OData w wersji 4 w internetowym interfejsie API, zobacz Create an OData v4 Endpoint Using ASP.NET Web API 2.2 (Tworzenie punktu końcowego OData w wersji 4 przy użyciu interfejsu API sieci Web 2.2).
Najpierw utworzymy model domeny zawierającej w usłudze OData przy użyciu tego modelu danych:
Konto zawiera wiele jednostek PaymentInstruments (PI), ale nie definiujemy zestawu jednostek dla pi. Zamiast tego dostęp do tych identyfikatorów można uzyskać tylko za pośrednictwem konta.
Definiowanie modelu danych
Zdefiniuj typy 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; } }
Atrybut
Contained
jest używany do właściwości nawigacji zawierania.Wygeneruj model EDM na podstawie typów CLR.
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(); }
Obiekt
ODataConventionModelBuilder
będzie obsługiwać kompilowanie modelu EDM, jeśliContained
atrybut zostanie dodany do odpowiedniej właściwości nawigacji. Jeśli właściwość jest typem kolekcji, zostanie również utworzonaGetCount(string NameContains)
funkcja.Wygenerowane metadane będą wyglądać następująco:
<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>
Atrybut
ContainsTarget
wskazuje, że właściwość nawigacji jest zawieraniem.
Definiowanie zawierającego kontrolera zestawu jednostek
Zawarte jednostki nie mają własnego kontrolera; akcja jest zdefiniowana w kontrolerze zawierającym zestaw jednostek. W tym przykładzie istnieje kontroler AccountsController, ale nie 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;
}
}
Jeśli ścieżka OData ma co najmniej 4 segmenty, działa tylko routing atrybutów, na przykład [ODataRoute("Accounts({accountId})/PayinPIs({paymentInstrumentId})")]
w powyższym kontrolerze. W przeciwnym razie zarówno atrybut, jak i konwencjonalny routing działają: na przykład GetPayInPIs(int key)
pasuje do GET ~/Accounts(1)/PayinPIs
.
Dzięki Leo Hu za oryginalną zawartość tego artykułu.