Автономное использование в OData версии 4 с использованием веб-API 2.2
Дзинфу Тан
Обычно доступ к сущности можно было получить только в том случае, если она была инкапсулирована в наборе сущностей. Но OData версии 4 предоставляет два дополнительных варианта: Singleton и Containment, которые поддерживает WebAPI 2.2.
В этом разделе показано, как определить вложенность в конечной точке OData в WebApi 2.2. Дополнительные сведения об автономности см. в статье , посвященной включению в OData версии 4. Сведения о создании конечной точки OData версии 4 в веб-API см. в статье Создание конечной точки OData версии 4 с помощью веб-API ASP.NET 2.2.
Сначала мы создадим модель автономной предметной области в службе OData, используя следующую модель данных:
Учетная запись содержит много объектов PaymentInstruments (PI), но мы не определяем набор сущностей для pi. Вместо этого доступ к api-интерфейсам можно получить только через учетную запись.
Определение модели данных
Определите типы среды 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
используется для свойств навигации в составе.Создайте модель EDM на основе типов СРЕДЫ 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(); }
будет
ODataConventionModelBuilder
обрабатывать построение модели EDM,Contained
если атрибут добавляется в соответствующее свойство навигации. Если свойство является типом коллекции,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
.
Спасибо Лео Ху за оригинальное содержание этой статьи.
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по