Omezování v OData v4 pomocí webového rozhraní API 2.2

podle Jinfu Tan

Tradičně bylo možné k entitě přistupovat pouze v případě, že byla zapouzdřena v sadě entit. OData v4 ale nabízí dvě další možnosti: Singleton a Containment, které webAPI 2.2 podporuje.

Toto téma ukazuje, jak definovat obsah v koncovém bodu OData ve WebApi 2.2. Další informace o omezování najdete v tématu Věnovaném omezování s OData v4. Informace o vytvoření koncového bodu OData V4 ve webovém rozhraní API najdete v tématu Vytvoření koncového bodu OData v4 pomocí webového rozhraní API ASP.NET 2.2.

Nejprve vytvoříme ve službě OData model domény pro uchovávání obsahu pomocí tohoto datového modelu:

Datový model

Účet obsahuje mnoho paymentInstruments (PI), ale pro platební nástroj nedefinujeme sadu entit. Místo toho je možné k rozhraním PI přistupovat pouze prostřednictvím účtu.

Definování datového modelu

  1. Definujte 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; }     
    }
    

    Atribut Contained se používá pro vlastnosti navigace v kontejneru.

  2. Vygenerujte model EDM na základě typů 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();         
    }
    

    Bude ODataConventionModelBuilder zpracovávat sestavení modelu EDM, pokud Contained je atribut přidán do odpovídající navigační vlastnosti. Pokud je vlastnost typu kolekce, GetCount(string NameContains) vytvoří se také funkce.

    Vygenerovaná metadata budou vypadat takto:

    <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>
    

    Atribut ContainsTarget označuje, že vlastnost navigace je obsah.

Definování kontroleru obsahující sady entit

Obsažené entity nemají vlastní kontroler. akce je definována v kontroleru obsahující sady entit. V této ukázce je AccountsController, ale žádný 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;         
    }     
}

Pokud je cesta OData 4 nebo více segmentů, funguje pouze směrování atributů, například [ODataRoute("Accounts({accountId})/PayinPIs({paymentInstrumentId})")] ve výše uvedeném kontroleru. Jinak funguje jak atribut, tak konvenční směrování: například GetPayInPIs(int key) odpovídá GET ~/Accounts(1)/PayinPIs.

Děkujeme Leo Hu za původní obsah tohoto článku.