OData Client for Objective-C User Guide

 

 

 

 


 

Contents

Introduction. 4

Package Content 4

Installation and Configuration of OData Client

Creating a Proxy Class. 9

Command-line Parameters. 9

Using the Proxy Class. 12

Data Service Query Expressions. 13

Running Query Expressions. 13

DataServiceQuery Class. 13

QueryOperationResponse Class. 15

OData Exception Classes. 17

DataServiceRequestException Class. 17

ODataServiceException Class. 17

ACSUtilException Class. 18

Query Options in Detail 19

Expand. 19

Filter 20

OrderBy. 20

Skip. 21

Top. 22

Inlinecount 22

Count 23

Select 23

Server Driven Paging. 25

Loading the Related Entities Using the Expand Option and the LoadProperty API 26

Creating a Instance in a Data Service. 28

Updating an Instance in Data Service. 29

Deleting an Instance from a Data Service. 30

Creating a Link between Instances Using AddLink. 31

Deleting the Link between Instances Using DeleteLink. 32

Creating and Deleting the Link between Instances Using SetLink. 32

Batch and Non-Batch Modes of the SaveChanges Operation. 34

Enhanced BLOB Support 34

Associating a Stream with the MLE entity Using GetReadStream.. 35

Creating an MLE Entity and Associating it with a Stream Using setSaveStream.. 36

Working with the Azure Storage Tables. 37

Creating an Azure Storage Table. 38

Inserting a Blob Entity into an Azure Table. 39

Updating a Table Entry by Adding a New Property. 40

Deleting a Table Entry and Table. 42

FunctionImport 43

BaseType/Inheritance. 45

Using the Call Back Feature. 46

Authentication in the Client Library. 49

Custom Authentication Support 50

Reference Section. 50

 

Introduction

The OData Client for Objective-C helps you to build applications that consume data services, which are created using the OData Services framework. The OData Client for Objective-C includes support to access and modify the data that the OData Services exposes.

 

OData is a Web protocol, which is used to query and update data and provides a way to unlock data and free it from silos that exist in applications. OData promotes an interoperable ecosystem for data consumers (clients) and producers (services). OData uses Web technologies such as HTTP, Atom Publishing Protocol (AtomPub), and JavaScript Object Notation (JSON) to provide access to information from various applications, services, and stores.

 

Objective-C is an object oriented programming language which is widely used in Mac operating system and in iPhone operating systems.

 

For more information on protocol documentation for OData and the list of publicly available OData services, refer to the OData Web site.

Package Content

The contents of the OData Client are:

Folder Path

Contents

\Doc

OData Client for Objective-C User Guide

\Sample\iPhoneApplications

Netflix Catalog sample application

\framework\src\odatalib

Source code of odatalib

\framework\src\odatagen

Source code of odatagen

\framework\bin\odatalib\include

Include files of odatalib

\framework\bin\odatalib\lib

odatalib libraries for both simulator and devices

\framework\bin\odatagenBinary

Odatagen binary files

 

Installation and Configuration of OData Toolkit

Before installing the OData Client for Objective-C, ensure that Xcode Projects is installed in the Mac machine.

The installation and configuration of OData Client for Objective-C toolkit consists of the following steps:

·         Addthe odatalib header path in XCode Projects.

·         Add the odatalib library path in XCode Projects.

·         Add the LibMSOdatalib.a library path in XCode Projects.

 

To add the odatalib header path in XCode Project, perform the following steps:               

1.       Select Menu Project->Edit Project Settings.

Description: Description: Description: Description: Description: 1_a.png         

2.       Click Build tab.

Description: Description: Description: Description: Description: 1_b.png

3.       Click the Search Path settings.

Description: Description: Description: Description: Description: 1_c.png              

4.       Add the odatalib include path in Header Search Paths.

 Description: Description: Description: Description: C:\Users\claudioc\AppData\Local\Microsoft\Windows\Temporary Internet Files\Content.Word\HeaderSearchPath.png

       

5.       Select the Recursive check box.

6.        Click OK.

To add the odatalib library path in XCode Project, perform the following steps: 

1.       Select Menu Project->Edit Project Settings.

2.       Go to Build tab.

3.       Find the Search Path settings.

4.       Add the odatalib include path in Library Search Paths.

Description: Description: Description: LibrarySearchPath 

5.       Select the Recursive check box.

6.       Click OK.

To add the LibMSOdatalib.a in XCode Project, perform the following steps:

1.       Right-click the Frameworks group in XCode and go to Add->Existing Framework.

Description: Description: Description: Description: Description: 3_a.png

2.       Navigate to the required LibMSOdatalib.a and click Add. LibMSOdatalib.a is built for both simulator and device SDKs.

Description: Description: Description: Description: Description: 3_b.png

3.       Click Add to add it to the selected targets.


 

Creating a Proxy Class

To connect to the OData Service, you need to create a proxy class. The OData Client for Objective-C provides the odatagen binary which creates the proxy class.

 

To create a proxy class, use the following command:

 

odatagen /uri=<data service Uri> | /metadata=<service metadata file>  [/out=<output file path>] [/auth=windows|acs /u=username /p=password [/sn=servicenamespace /at=applies_to] ] [/ups=yes|no] ]

 

Command-line Parameters

 The following table lists the parameters that you can use while creating the proxy class:

 

Option

Definition

Description

/uri

OData Service URI

The odatagen binary file uses the Uniform Resource Identifier (URI) to retrieve the service metadata. For example: http://localhost/catalog.svc/$metadata

/metadata

The path of the service meta data file.

Specifies the path to the metadata file.  You can use this option if you have saved the service metadata file on your local machine.

/out

The output directory or file path.

Specifies the output directory where the generated proxy file is saved. If the file name is not specified an error is returned.

/auth

The type of authentication. The possible values are:

·         windows

·         acs

You can use this parameter if access to the service requires authentication. odatagen supports the following types of authentication:

·         Windows authentication

·         Azure Access Control Service (ACS) authentication

/u

The Windows username or ACS scope.

If the /auth parameter is of Windows authentication type, the /u parameter is the Windows user name in the domain/username format. If /auth is of ACS authentication type, /u is the ACS scope name.

/p

The Windows password or ACS issuer key.

If /auth is of Windows authentication type, then /p will be the Windows password. If /auth is of ACS authentication type, then /p will be the ACS issuer key.

/sn

The ACS service namespace.

If /auth is of ACS authentication type, then /sn will be the ACS service namespace.

/at

ACS applies to

If /auth is of ACS authentication type, then /at will be the ACS applies to value.

/ups

The proxy settings that are used for service request. The possible values are:

·         yes

·         no

By default, the user-specified proxy settings are used to request metadata from OData Service. If you use the ACS authentication, and the access to the OData Service does not require any proxy settings, such as a locally running service, set this flag to no.

For example:

 /ups=no

  

 The following examples show how to use the command-line parameters:

·         To generate a proxy class for a service running locally and is configured for anonymous access using /uri option, you can use the following sample command:

 

macuser-imac:Desktop macuser$ odatagen /uri=http://localhost:13986/NorthWindDataServices.svc /out=/Users/macuser/Desktop/odatagenapp

 

This command enables ODatagen to:

1.       Connect to  http://localhost:13986/NorthWindDataServices.svc.

2.       Retrieve the metadata.

3.       Generate the proxy file in /Users/macuser/Desktop/odatagenapp  directory with the same name as that of the container name in the metadata.

·          To generate a proxy class from the local metadata file using the /metadata option, you can use the following sample command:

 

macuser-imac:Desktop macuser$odatagen /metadata=/Users/macuser/Desktop/odatagenapp/NorthWindMeta.xml /out=/Users/macuser/Desktop/odatagenapp

 

 This command enables ODatagen to use the metadata from the local disk and generate the proxy file in /Users/macuser/Desktop/odatagenapp directory with the same name as that of the container name in the metadata. For example, if the container name is NorthWind, the file name is NorthWind.h and NorthWind.m.

·         To generate a proxy class for a service that exists in the same domain and is configured to require Windows authentication using the /auth, /u and /p options, you can use the following sample command:

 

macuser-imac:Desktop macuser$odatagen /uri=http://MyServer/_VTI_BIN/listdata.svc /out=/Users/macuser/Desktop/odatagenapp /auth=windows /u=macuser1 /p=passw0rd!

 

This command enables ODatagen to:

 

1.       Use the Windows credential provided through the /u and /p options to connect to the service.

2.       Generate the proxy file in the folder /Users/macuser/Desktop/odatagenapp with the same name as that of the container in the metadata.

 

·         To generate a proxy class for a service that runs locally and is configured to require the ACS authentication using the /auth, /u, /p, /sn and /at options, you can use the following sample command:

 

macuser-imac:Desktop macuser$odatagen /uri= http://localhost:13985/ACSNorthWindDataService.svc /out=/Users/macuser/Desktop/odatagenapp /auth=acs /u=infocorp /p=abcdefghijklmnopqrstuvwxyz1234567890 /sn=odataservice /at=http://localhost/NorthWindService/ /ups=no

 

This command enables ODatagen to:

1.       Use the acs credential provided through the /u, /p, /sn and /at options to connect to the service.

2.       Generate the proxy file in /Users/macuser/Desktop/odatagenapp with the same name as that of the container name in the metadata.


 

Using the Proxy Class

 

After you create the proxy class for the OData Service, you can use the proxy class in the application to access the data on the server. To use the proxy class, do the following:

·         Include the proxy class files in the project.

·         Import the header file in the class in which you are going to use the service.

 The following code snippet shows how to import the header file:

#import “MyProxyClass.h”


The MyProxyClass.h file contains the declaration, and the MyProxyClass.m file contains the definition of a class that you can be use to execute various queries using the Execute method.

The following code snippet shows how to use a proxy class to access a customer record for a service that exposes the NorthWind database. The code snippet helps to retrieve the record for a costomer where the value of CustomerID is ALFKI.

NorthWindEntites *proxy = [[NorthWindEntites alloc] initWithUri:@"http://localhost:13985/ACSNorthWindDataService.svc/" credential:nil];

QueryOperationResponse *response = [proxy execute:@" Customers(‘ALFKI’)"];];

NSMutableArray *customersArray  = [response getResult];

Customer* customer = [customersArray objectAtIndex:0];

NSLog(@"Company Name = %@", [customer getCompanyName] );


The above code snippet does the following:

1.       Uses a proxy class to connect to the OData Service.

2.       Retrieves a customer.

3.       Prints the Company Name field using the customers class defined in the MyProxyClass file.

Note:

To select an entity instance from the data service, you can use the EntitySet(<key>) syntax . You can map Entity Set to the database table, <key> to the primary key and the entity instance to the table record.

 

Data Service Query Expressions

The data service query expressions are conditions that determine the data that is retrieved from the OData Service. You can perform traditional query operations such as filtering, sorting, and paging, against resources (entity sets) by using the query expressions to . The query expressions are comprise of query options, query operators, and query functions. OData Client for Objective-C supports the following  nine data service query options :

·         expand

·         filter

·         orderby

·         skip

·         top

·         inlinecount

·         count

·         skiptoken

·         select

For more information on the data service query expressions, refer to the corresponding MSDN site.

Running Query Expressions

 

OData Client for Objective-C exposes the following two APIs that you can be use to run query expressions against the OData Service servers:

·         ObjectContext::Execute

·         DataServiceQuery::Execute

ObjectContext: :Execute method accepts the query expression URI as a parameter. This method does the following:

1.       Uses the HTTP GET request to retrieve the entity set (in Atom format) addressed by the query expression URI.

2.       Creates a collection of entity objects by parsing the Atom response.

3.       Returns the DataServiceResponse object which includes this collection.

An instance of the DataServiceQuery class represents a single query request to a data service. The DataServiceQuery  class exposes a few methods that help toreduce the difficulty in building a query expression URI. 

DataServiceQuery Class

 

OData Client for Objective-C includes a class that represents a single query request to a data service. The DataServiceQuery class exposes the following member functions:

 

API Prototype

Parameters

Description

addQueryOption:(NSString*)anOption query:(NSString*)aQuery

anOption: The string value that contains the name of the query string option that you want to add.

aQuery: The string that contains the value of the query string option.

The query options are added to the resultant query expression URI using the  ?name=value&name2=value2… syntax. Here, the name maps directly to the anOption parameter and the value maps to the aQuery parameter.

expand:(NSString*)anExpression

anExpression: The related entities separated by a comma. These entities  are embedded in the result.­­­­

This API adds the expand query string option in the resultant query expression URI.

filter:(NSString*)anExpression

anExpression: The expression that is used to restrict the entities which are embedded in the result.

This API adds the filter option in the resultant query expression URI.

select:(NSString*)anExpression

anExpression: Properties of the entity separated by a comma.

This API adds the select option in the resultant query expression URI.

top:(NSInteger)count

count: The maximum number of entities to be embedded in the result.

This API adds the top option in the resultant query expression URI.

skip:(NSInteger)count

count: The number of rows to be skipped while returning results.

This API adds the skip option in the resultant query expression URI.

includeTotalCount:(NSString*)anExpression

None

This API includes the number of entities that the query expression URI returns along with the query result.

(NSString*) count

None

This API returns the count of entities, and satisfies the resultant query URI expression.

(NSString*) requestUri

None

This API returns the resultant query URI expression.

(QueryOperationResponse*) execute

None

This API executes the query expression and returns the QueryOperationResponse object that holds the results as a collection.

 

If an error occurs while building the query expression URI (for example: you cannot use IncludeTotalCount() and Count() together) or running the query  (for example: Unauthorized), the  DataServiceQuery class throws an exception of the DataServiceRequestException type.

 

QueryOperationResponse Class

 

OData Client for Objective-C includes a class that represents the type of return for the ObjectContext::Execute and DataServiceQuery::Execute APIs.

The QueryOperationResponse class exposes the following member functions.

API Prototype

Parameters

Description

- (NSInteger) totalCount

None

This API returns the total number of entities in the entity set.

You can use the following options in the query expression:

·         DataServiceQuery::IncludeInlineCount

·         inlinecount=allpages option

- (NSString*)getQuery

None

This API returns the query expression URI that generates the QueryOperationResponse item.

- (NSInteger) getStatusCode

None

This API returns the HTTP response code that is associated with the HTTP response for the query expression URI.

-(NSDictionary*) getHeaders

None

This API returns an associative array which contains the HTTP response headers that are associated with the HTTP response for the query expression URI.

getContinuation:(NSArray*)collection

collection: The collection of related objects that are loaded.

This API returns the DataServiceQueryContinuation object. This object contains the URI that is used to retrieve the next page of the related entities in the collection. For more information on server side paging, refer to the Server Driven Paging section.

 

The QueryOperationResponse class includes the following member variable:

Member variable

Description

- (NSMutableArray*) getResult

The result of the execution of query expression URI as a collection.

 

OData Exception Classes

OData Client for Objective-C supports the following types of exception classes:

DataServiceRequestException Class

The  DataServiceRequestException exception class represents any exception that can occur while building the query expression URI using DataServiceQuery member APIs or while running the query expression.

The DataServiceRequestException exception class includes the following member variable:

Member variable

Description

Response

Type: QueryOperationResponse

You can use the following variables to obtain the error details:

·         Response::getError()

·         Response::getStatusCode()

·         Response::getHeaders

 

This exception class includes one member variable of the QueryOperationResponse type,. For more information on how to handle this exception, refer to the samples in Query Options in Detail section.

ODataServiceException Class

The ODataServiceException exception class represents any error that the OData Services return, while processing all the requests except the query request from the client.

The ODataServiceException class exposes following member functions: 

API Prototype

Parameters

Description

getError

None

This API returns the short description of the error.

getDetailedError

None

This API returns the detailed description of the error.

getStatusCode

None

This API returns the HTTP response code associated with the HTTP response representing the error.

getHeaders

None

This API returns an associative array which contains the HTTP response headers. These headers are associated with the HTTP response that represents the error.

ACSUtilException Class

The ACSUtilException  exception class represents any error that occurs while trying to retrieve the ACS token from Azure. This error can occur while using the ACSUtil or ACSCredential classes. 

API Prototype

Parameters

Description

getError

None

This API returns the description of the error from ACS.

getStatusCode

None

This API returns the HTTP response code associated with the HTTP response that represents the error.

getHeaders()

None

This API returns an associative array that contains the HTTP response headers. These headers are associated with the HTTP response that represents th error.

Query Options in Detail

This section explains the query options and samples that are used in the DataServiceQuery class build and execute the query expressions.

Expand

The Expand query option allows you to embed sets of related entities in the results. For example, if you want to display a customer and the sales orders of the customers, you can execute two requests. You can use the Expand option to return the related entities in-line with the response of the parent entity in a single HTTP request.

You can specify entities like multiple navigation properties that you want to expand by separating them with commas. You can traverse many relationships using a dot to go to the next navigation property. For example, if an entity is linked to multiple entities, there will be multiple navigation properties.  You can use a comma delimiter to query the multiple navigation properties. The Expand option helps you to retrieve data with only one server invocation.

For example:

try

{

   //customer ALFKI with related sales orders

   NorthWindEntites *proxy = [[NorthWindEntites alloc]  

   initWithUri:@"http://localhost:13985/ACSNorthWindDataService.svc/" credential:nil];

   DataServiceQuery* query = [proxy  customers];

   [query filter:@“CustomerID eq ‘ALFKI’”];

   [query expand:@”Orders”];

   QueryOperationResponse *response  =[query execute];

   Customer* customer = = [customersArray objectAtIndex:0];

   NSLog(@"count = %@", [customer orders]);

 

   //In the proxy class, for each entity set, there will be one function (with name same as name of entity set) that returns DataServiceQuery object for that particular entity set.


 Instead of using DataServiceQuery class, you can also use the Execute() API of ObjectContext class. For example: 

You can replace the following sample code:

   DataServiceQuery* query = [proxy  customers];

   [query filter:@“CustomerID eq ‘ALFKI’”];

   [query expand:@”Orders”];

   QueryOperationResponse *response  =[query execute];

 

using ObjectContext::Execute API. The correct query strings need to be built and the following sample code can be used:

QueryOperationResponse *response  =[proxy execute:@” Customers(‘ALFKI’)?/$expand=Orders”];

Filter

The Filter query option restricts the entities returned from a query by applying the expression specified in this operator to the entity set identified by the last segment of the URI path.

For example:

@try

{

   //all customers in London

   DataServiceQuery* query = [proxy  customers];

   [query filter:@“ City eq ‘London’”];

   QueryOperationResponse *response  =[query execute];

}

@catch(DataServiceRequestException *exception)

{

    NSLog(@”Exception = %@”, [exception reason]); 

}


You can use the following conditions in the Filter query option:

Condition

Description

eq

Equals

ne

Not equals

lt

Less than

le

Less than or equal

gt

Greater than

ge

Greater than or equal

 

OrderBy

The OrderBy query option sorts the results using the criteria given in the parameter. You can provide multiple properties separated by a comma. To control the sort order, you can use the asc (default) and desc modifiers.

For example:

@try

{

    DataServiceQuery* query = [proxy  customers];

    [query orderBy:@“City”];

    QueryOperationResponse *response  =[query execute];

 

    query = [proxy  customers];

    [query orderBy:@“ City desc”];

    response  =[query execute];

 

    query = [proxy  customers];

    [query orderBy:@“ City desc,CompanyName asc”];

     response  =[query execute];

}

@catch(DataServiceRequestException *exception)

{

   NSLog(@”Exception = %@”, [exception reason]); 

 

Skip

The Skip query option skips the number of rows while returning results. To implement paging, you can use the skip option with the Top option. For example, If there are 40 entities and 10 entity pages, you can use the expression $skip=30&top=$10 to return the fourth page.


Note:  If you include the OrderBy option, the skip option skips entities in the order that the OrderBy option provides. If you do not include the orderby option, the skip option sorts the entities by the primary key, and then performs the skip operation.

 

For example:

 

@try

{

   //return all customers except the first 10

    DataServiceQuery* query = [proxy  customers];

    [query skip:@“10”];

   QueryOperationResponse *response  =[query execute];

 

   //return the 4th page, in 10-row pages

    query = [proxy  customers];

    [query skip:@“10”];

    [query top:@”10”];

    response  =[query execute];

}

@catch(DataServiceRequestException *exception)

{

    NSLog(@”Exception = %@”, [exception reason]); 

}

Top

The top query option restricts the maximum number of entities that are returned. To implement paging, you can use this option with the Skip option. 

@try

{

   //top 5 sales orders

    DataServiceQuery* query = [proxy  orders];

    [query top:@“5”];

   QueryOperationResponse *response  =[query execute];

 

   //top 5 sales orders with the highest TotalDue

    query = [proxy  orders];

    [query orderby:@“TotalDue”];

    [query Top:@“5”];

    response  =[query execute];

}

@catch(DataServiceRequestException *exception)

{

   NSLog(@”Exception = %@”, [exception reason]);

}

Inlinecount

The Inlinecount query option directs the service to include the count of all the entities along with the entities that a URI returns. This query option is executed as a separate query, and returns a value that is independent of any page size limits.For more information on page size limit, refer to the Server Driven Paging section.

 For example: 

@try

{

  //all customers in London

    DataServiceQuery* query = [proxy  customers];

    [query filter:@“ City eq ‘London’”];

    [query includeTotalCount];

   QueryOperationResponse *response  =[query execute];

    NSLog(@”Total Number of Customers:”,  [response totalCount]);

    NSLog(@”Number of Customers in London:”,[ response count]);

}

@catch(DataServiceRequestException *exception)

{

     NSLog(@”Exception = %@”, [exception reason]); 

}

 

Count

When you add the Count query option to any entity set on the server, the result is a plaintext response that represents the count of entities in that set.

For example:

@try

{

  //all customers in London

    DataServiceQuery* query = [proxy  customers];

    [query filter:@“ City eq ‘London’”];

    NSLog(@”Number of Customers with London as City:”,  [query count]);

}

@catch(DataServiceRequestException *exception)

{

        NSLog(@”Exception = %@”, [exception reason]); 

}

 

For more information on inlinecount, refer to the MSDN blog.

Select

The select feature of OData Services allows you to work with a subset of the properties of an entity. The projections feature allows the client to address only the relevant properties to a specific request. You can optimize the client applications for bandwidth consumption by requesting the applicable subset of the properties of an entity for a particular query.

The following example shows how to use this feature: 

@try

{

   NorthWindEntites *svc= [[NorthWindEntites alloc]  

   initWithUri:@"http://localhost:13985/ACSNorthWindDataService.svc/" credential:nil];

   DataServiceQuery* query = [svc  customers];

   [query filter:@“ Country eq 'USA'”];

    [query select:@“ CustomerID,CompanyName”];

   QueryOperationResponse *response  =[query execute];

 

     NSMutableArray *customerArray= [response getResult];

     Int count = [customerArray count];

     for(int index=0;index<count;++index)

     {

         Customer *cust = [customerArray objectAtIndex:index];

         NSLog(@"CustomerID:" , [cust getCustomerID ]);

         NSLog(@"Company Name:" , [cust get CompanyName]);

         NSLog(@"Country:" , [cust getCountry]);

     }

}

@catch(ODataServiceRequestException *exception)

{

        NSLog(@”Exception = %@”, [exception reason]); 

}

 

Server Driven Paging

The service author uses the Server Driven Paging feature to set the number of entities that each request returns.  Server Driven Paging feature allows the server to provide the client with the next link. Next link is a URI that specifies how to retrieve those entities in the collection that the first request does not return. You can use the QueryOperationResponse::GetContinuation() API to get the next link.

For information on how to configure a service with the Server Driven Paging feature, refer to the MSDN blog.

The following code snippet shows how to use the QueryOperationResponse::GetContinuation() API:

@try

{

       DemoCatalog *proxy=[[DemoCatalog alloc]initWithUri:@"http://Myserver.com/catalog/" credential:nil];

       DataServiceQuery *query = [proxy genres];

       QueryOperationResponse *queryOperationResponse = [query execute];

       DataServiceQueryContinuation* nextToken=nil;

       int totalCount=0;

             

       while(queryOperationResponse)

       {     

              languageArray=[proxy getCopy:[queryOperationResponse getResult]];

              nextToken=[queryOperationResponse getContinuation:nil];

              if([nextToken getNextLinkUri])

                    queryOperationResponse =[proxy executeDSQueryContinuation:nextToken];

              else

                    queryOperationResponse =nil;            

       }

}

@catch (DataServiceRequestException * e) {

       NSLog(@"exception:: %@: %@",[e name], [e getResponse]);

}     

@catch (NSException * e) {

       NSLog(@"exception:: %@: %@", [e name], [e reason]);

}

 

For more information on how to use the QueryOperationResponse::GetContinuation(..) API to load the related entities, refer to the samples in the Loading the Related Entities Using the Expand Option and the LoadProperty API section.

 

Loading the Related Entities Using the Expand Option and the LoadProperty API

When you execute a query using the ObjectContext::Execute or DataServiceQuery::Execute APIs the query returns entities in the addressed entity set in QueryOperationResponse::Result NSMutableArray which is an array of objects.

For example:

When you run a query against the NorthWind data service, the service returns the Customers entities. By default, the service does not return the related Orders entities, even though there is a relationship between the Customers and Orders entities. You can use a single call to the server to load the related entities in the following ways:

1.       Loading using the expand query option to return entities that are related by an association to the entity set.  This type of loading is known as eager loading.

The following sample code uses the expand option to list the customer IDs and the customer’s associated OrderIDs in NorthWind database where USA is the country.

The following sample code shows how to use the QueryOperationResponse::GetContinutation(..) API to retrieve the related entities in the collection, when OData Service implements the server side paging.  

@try

{

       DemoCatalog *proxy=[[DemoCatalog alloc]initWithUri:@"http://MyServer.com/catalog/" credential:nil];

       DataServiceQuery *query = [proxy genres];

       [query expand:@"Titles"];

       QueryOperationResponse *genreResponse=nil, *titleResponse=nil;

       DataServiceQueryContinuation *nextGenreToken=nil, *nextTitleToken=nil;

       BOOL firstTimeFlag=YES;

      

       genreResponse=[query execute];

       while (genreResponse)

       {

              NSArray *genreArray=[genreResponse getResult];

              int count=[genreArray count];

              for(int i=0;i<count;i++)

              {

                    NSArray *titlesArray=nil;

                    if(firstTimeFlag)

                    {

                           titlesArray=[[genreArray objectAtIndex:i] getTitles];

                           nextTitleToken=[genreResponse getContinuation:titlesArray];

                           firstTimeFlag=NO;

                    }

                    if(!firstTimeFlag)

                    {

                            while(nextTitleToken)

                            {

                           titleResponse=[proxy executeDSQueryContinuation:nextTitleToken];

                           titlesArray=[titleResponse getResult];

                           nextTitleToken=[titleResponse getContinuation:nil];

                            }

                            firstTimeFlag=YES;

                    }

              }

              nextGenreToken=[genreResponse getContinuation:nil];

              if([nextGenreToken getNextLinkUri])

                    genreResponse=[proxy executeDSQueryContinuation:nextGenreToken];

              else

                    genreResponse=nil;

       }     

}

@catch (DataServiceRequestException * e) {

       NSLog(@"exception:: %@: %@",[e name], [e getResponse]);

}

@catch (NSException * e) {

       NSLog(@"exception:: %@: %@", [e name], [e reason]);

}

 

2.       Loading the related entities explicitly by calling the LoadProperty method of the ObjectContext class. Each call that you make to the LoadProperty method creates a separate request to the data service. The following sample code lists all the customer IDs in NorthWind DB with USA as Country and the associated Order IDs, using the LoadProperty API.

The following code snippet shows how to use QueryOperationResponse::GetContinutation(..) API to continue retrieving the rest of the related entities in the collection:

 

@try

{

       DemoCatalog *proxy=[[DemoCatalog alloc]initWithUri:@"http://MyServer.com/catalog/" credential:nil];

       DataServiceQuery *query = [proxy genres];

       QueryOperationResponse *genreResponse,*titleRespose;

       DataServiceQueryContinuation* nextToken=nil;

 

       genreResponse = [query execute];

       while(genreResponse)

       {

              NSArray* genreArray = [genreResponse getResult];

              int count = [genreArray count];

                   

              for(NSUInteger index = 0;index < count ; ++index)

              {

                    NSLog(@"%d:%@",index,[[genreArray objectAtIndex:index] getName]);

                    do

                    {

                           titleRespose=[proxy loadProperty:[genreArray objectAtIndex:index] propertyName:@"Titles" dataServiceQueryContinuation:nextToken];

                           nextToken=[titleRespose getContinuation:nil];

                    }while(nextToken!=nil);

              }

              nextToken=[genreResponse getContinuation:nil];

              if([nextToken getNextLinkUri])

                     genreResponse=[proxy executeDSQueryContinuation:nextToken];

              else

                    genreResponse=nil;

       }

             

}

@catch (DataServiceRequestException * e) {

       NSLog(@"exception:: %@: %@",[e name], [e getResponse]);

}

 

@catch (NSException * e) {

       NSLog(@"exception:: %@: %@", [e name], [e reason]);

}

 

Creating a Instance in a Data Service

You can create an object on the client and save the instance on the database that OData Service exposes.

To create an object and save the instance in the data service, perform the following steps:

1.       Create an object.

2.       Set the required properties.

3.       Call AddObject method to pass the object and the target entity set to the service, to add the object to the collection of objects in the database.

4.       Call the SaveChanges method to make the changes permanent in the database.

 

The following code snippet shows how to create an object on the client and save the instance on the database:

#import “NorthWindEntities.h”

@try

{

   NorthWindEntites *proxy= [[NorthWindEntites alloc]  

   initWithUri:@"http://localhost:13985/ACSNorthWindDataService.svc/" credential:nil];

 

  //Create a Customer Object 

   Customer* customer = [[Customer alloc] init];

 

   //inserting Customers object context tracking system 

   [proxy addObject:@”Customers” object:customer];

 

   //SaveChange insert the object into data service

    [proxy saveChanges];

}

@catch(DataServiceException *exception)

{

   NSLog(@”Exception = %@”, [exception reason]); 

}

 

Updating an Instance in Data Service

To update an instance in a data service, perform the following steps:

1.       Use the ObjectContext::Execute or LoadProperty or DataServiceQuery::Execute methods to retrieve an instance from data service.

2.       Set the required properties.

3.       Call the UpdateObject API to apply the changes to the table(s) in the database.

 The following code snippet shows how to use the UpdateObject API to update an instance in a data service:

#import “NorthWindEntities.h”

 

@try

{

    NorthWindEntites *proxy= [[NorthWindEntites alloc]  

   initWithUri:@"http://localhost:13985/ACSNorthWindDataService.svc/" credential:nil];

   QueryOperationResponse *response  =[query execute:@” Customers(‘CHAN5’)”];

    Customer *customer = [[response getResult] objectAtIndex:0];

 

    //update the CompanyName property

    [customer setCompanyName:@"Channel13"];

 

    //add the object to the list of objects that needs to be updated in the database

    [proxy updateObject:customer];

 

    //SaveChanges updates the object in the data service

    [proxy saveChanges];

}

@catch(DataServiceRequestException *exception)

{

        NSLog(@”Exception = %@”, [exception reason]); 

}

@catch(ODataServiceRequestException *ex)

{

        NSLog(@”Exception = %@”, [ex reason]); 

}

 

Deleting an Instance from a Data Service
To delete an instance from a data service, perform the following steps:

 

1.       Use the ObjectContext::Execute or LoadProperty or DataServiceQuery::Execute method to retrieve an instance from a data service.

2.       Call the DeleteObject method to add the object to the list of objects that have to be deleted in the database.

The following code snippet shows how to use the DeleteObject API:

#import “NorthWindEntities.h”

 

@try

{

    NorthWindEntites *proxy= [[NorthWindEntites alloc]  

   initWithUri:@"http://localhost:13985/ACSNorthWindDataService.svc/" credential:nil];

   QueryOperationResponse *response  =[query execute:@” Customers(‘CHAN5’)”];

    Customer *customer = [[response getResult] objectAtIndex:0];

 

    //The object is added to the list of objects to delete in the database

    [proxy  deleteObject:customer];

 

    //SaveChanges persists the changes in the database 

    [proxy saveChanges];

}

@catch(DataServiceRequestException *exception)

{

       NSLog(@”Exception = %@”, [exception reason]); 

}

@catch(ODataServiceException (ex)

{

       NSLog(@”Exception = %@”, [ex reason]); 

}  

 

Creating a Link between Instances Using AddLink

You can use the AddLink method to create a link between the corresponding entity instances, if a relationship  already exists between two entities in a data service with multiplicity = *, .

Note:

You can use the AddLink method to add links to relationships with multiplicity = * (one-to-many relationships). For example, you can use this method when the source property is a collection. You can use the SetLink method when you want to add links to relationships with multiplicity = 1 (many-to-one relationships).

 

The following code sample shows how to create a link between the instances of customer and Order:

#import “DemoService.h”;

 

try

{

       DemoService *proxy=[[DemoService alloc]initWithUri:@"http://MyServer.com/OData/OData.svc/" credential:nil];

      

       Category *c=[Category CreateCategoryWithid:[NSNumber numberWithInt:22]];

       [c setName:@"category1"];

       [proxy addToCategories:c];

      

       Product *p=[Product CreateProductWithid:[NSNumber numberWithInt:22] releasedate:[NSDate date] rating:[NSNumber numberWithInt:100] price:[NSDecimalNumber decimalNumberWithString:@"10.9"]];

       [p setName:@"product1"];

       [proxy addToProducts:p];

             

       [proxy addLink:c sourceProperty:@"Products" targetObject:p];

       [proxy saveChanges];

 

}

@catch(NSException *e)

{

       NSLog(@"Exception:%@",[e name]);

}

 

Deleting the Link between Instances Using DeleteLink
If a relationship is defined between two entities in the data service with multiplicity = * , then you can use DeleteLink to remove the relationship between the corresponding entity instances.

 The following code snippet shows how to delete the link.

#import "DemoService.h"

 

try

{

       DemoService *proxy=[[DemoService alloc]

       initWithUri:@"http:// MyServer.com/OData/OData.svc/" credential:nil];

             

       DataServiceQuery *query = [proxy categories];

       [query filter:@"Name eq 'category1'"];

       [query expand:@"Products"];

       QueryOperationResponse *queryOperationResponse = [query execute];

       languageArray=[queryOperationResponse getResult];

       Category *c=[languageArray objectAtIndex:0];

             

       Product *p=[[c getProducts] objectAtIndex:0];

      

       [proxy deleteLink:c sourceProperty:@"Products" targetObject:p];

       [proxy saveChanges];

}

@catch(NSException *e)

{

       NSLog(@"Exception:%@",[e name]);

}

 

Creating and Deleting the Link between Instances Using SetLink
If a relationship is defined between two entities in the data service with multiplicity = 1, then you can use SetLink to create and delete relationship between the corresponding entity instances.

 

Note:

You can use the SetLink method to add links to relationships with multiplicity = 1 (many-to-one relationships). For example, you can use this method when the source property is a reference. You can use AddLink method for adding links to relationships with multiplicity = * (one-to-many relationships). If the targetObject argument is null, the link represented by sourceProperty is deleted.

 

The following code snippet shows how to create a link between an Order_Details instance and an Orders instance:

 

#import "DemoService.h"

 

@try{

       DemoService *proxy=[[DemoService alloc]

       initWithUri:@"http:// MyServer.com/OData/OData.svc/" credential:nil];

             

       DataServiceQuery *query = [proxy products];

       [query filter:@"ID eq 35"];

       QueryOperationResponse *queryOperationResponse = [query execute];

       languageArray=[queryOperationResponse getResult];

       Product *p=[languageArray objectAtIndex:0];

             

       query = [proxy categories];

       [query filter:@"ID eq 35"];

       queryOperationResponse = [query execute];

       languageArray=[queryOperationResponse getResult];

       Category *c=[languageArray objectAtIndex:0];

             

       [proxy setLink:p sourceProperty:@"Categories" targetObject:c];

       [proxy saveChanges];

 

       [proxy setLink:p sourceProperty:@"Categories" targetObject:nil];//deleting link

       [proxy saveChanges];

 

}

@catch (NSException * e) {

       NSLog(@"exception:: %@: %@", [e name], [e reason]);

}

Batch and Non-Batch Modes of the SaveChanges Operation

When you call the SaveChanges API, all the changes that the context tracks are translated into REST-based operations. By default, these changes are packed into a batch and sent as a single request message. To ensure that the changes are sent as separate HTTP requests, set the mode of SaveChanges API to None.

To change the mode of SaveChanges API to non-batch, you can use the following code snippet:

[svc setSaveChangesOptions:SaveChangesOptions::None];

 

To change the mode of SaveChanges API to batch, you can use the following code snippet:

[svc setSaveChangesOptions:SaveChangesOptions::Batch];

Enhanced BLOB Support

V1.0 of the OData allows you to define entities with the Binary Large Object (BLOB) property. However, OData V1.0 does not allow you to defer the loading of the BLOB property. For example, you have an Employee entity definition with a BLOB property that represents the photo of an employee. When you request for a specific set of employees, the resultant payload from the data service is huge as each entity includes the BLOB stream. 

The V2.0 of the OData enhances the BLOB support that V1.0 of the OData protocol provides. The V2.0 of the OData enables the data service to stream large BLOBs arbitrarily. This version allows you to store binary content separate from BLOB’s metadata. When you request for a BLOB’s metadata, OData enables the data service to defer the loading of the BLOB content. This type of entity with BLOB property is known as the Media Link Entry (MLE) entity.

To support the enhanced BLOB feature of OData V2.0, OData Client for Objective-C exposes the following APIs:

API Prototype

Parameters

Expected Exceptions

Description

getReadStream:entity :args

entity: The entity that has the binary property to retrieve

args: The values can be null, string or object of DataServiceRequestArgs

Throws InvalidOperation exception, if the value of args is not null, string or instance of DataServiceRequestArgs. The method throws an exception if the entity is not an MLE.

If OData Service returns any error, the OData Client for Objective-C throws the ODataServiceException exception.

Synchronously requests a data stream that contains the binary property of the Media Link Entry (MLE) entity that is requested. The args value can be null, a string representing the Accept HTTP header or the instance of the DataServiceRequestArgs class. The DataServiceRequestArgs class contains settings for the HTTP request message such as Slug, Accept, and Content-Type.

getReadStreamUri:entity

entity: The entity that has the binary property to retrieve

Throws InvalidOperation, if the context is not currently tracking the entity.

Gets the URI that is used to return the binary property data as a data stream.

setSaveStream:entity :stream :closeStream :contentType :slug

entity: The entity that has the binary property to set

stream: The binary stream to set

contentType: A type of stream

slug: The default path and name of the stream after the stream is uploaded

Throws InvalidOperation, if any one of the following criteria is not satisfied:

·      contentType is not null

·      slug is not null

·      stream is not null

Sets a new data stream as the binary property of an entity, with the specified settings in the request messages.

 

Associating a Stream with the MLE entity Using GetReadStream

To retrieve the stream associated with the MLE entity set SharedDocuments of SharePoint 2010 site, the following code snippet uses the GetReadStream API: 

#import "TeamSiteDataContext.h"

 

WindowsCredential *credt;

TeamSiteDataContext *proxy;

SharedDocumentsItem *item ;      

@try

{

          credt = [[WindowsCredential alloc] initWithUserName:@"domain_name\\user_name " password:@"password"];

          proxy = [[TeamSiteDataContext alloc] initWithUri:@"http://MyServer/ _VTI_BIN/ListData.svc/"

                        credential:credt];

          QueryOperationResponse * response = [proxy execute:@"SharedDocuments"];

 

          if(response)

          {

       NSArray *array=[response getResult];

       item = [[SharedDocumentsItem alloc] initWithUri:nil];

       if([item hasBlob])

       {

              DataServiceStreamResponse *response=[proxy getReadStream:[array objectAtIndex:0]];

              NSData *stream =[response getStream];

       }

         }

}

@catch (NSException * e) {

       NSLog(@"exception::%@:%@",[e name],[e reason]);

}

@finally {

       [credt release];

       [proxy release];

       [item release];

}

 

Creating an MLE Entity and Associating it with a Stream Using setSaveStream

To create an MLE entity and associate it with a stream, you can use the following code snippet: 

#import "TeamSiteDataContext.h"

 

WindowsCredential *credt;

TeamSiteDataContext *d;

SharedDocumentsItem *item ;      

 

@try

{

       credt = [[WindowsCredential alloc] initWithUserName:@" domain_name\\user_name"

       password:@"password"];

       d = [[TeamSiteDataContext alloc]

       initWithUri:@"http:// MyServer/ _VTI_BIN/ListData.svc/" credential:credt];

       NSString *qry = [NSString stringWithString:@"SharedDocuments"];

       QueryOperationResponse * queryOperationResponse = [d execute:qry];

       if(queryOperationResponse)

       {

              NSArray *arr=[queryOperationResponse getResult];

              int count=[arr count];

              item = [[SharedDocumentsItem alloc] initWithUri:nil];

              if([item hasBlob])

              {

                    [item setId:[NSNumber numberWithInt:3]];

                    [d addToSharedDocuments:item];//sharedDocument is added to resourcebox

                    NSString *aStr=@"This is a iPhone test Document";

                    NSData* aData=[aStr dataUsingEncoding: NSASCIIStringEncoding];

                    [d setSaveStream:item stream:aData closeStream:NO contentType:@"plain/text" slug:@"/Shared Documents/test.txt"];

                    [d saveChanges];

              }

       }

}

@catch (NSException * e) {

       NSLog(@"exception::%@:%@",[e name],[e reason]);

}

@finally {

       [credt release];

       [d release];

       [item release];

}

 

Working with the Azure Storage Tables

You can perform the following tasks on the Azure storage tables and entities:

·         Create a Azure table or entity.

·         Insert a Blob Entity into an Azure Table.

·         Update an entity.

·         Delete a table or entity.

To perform operations on the Azure storage tables, you must specify the Azure credentials. The constructor of the AzureTableCredential class accepts the Azure account name and key. 

[proxy setCredential:[[ AzureTableCredential alloc] initWithAccountName:account-name accountKey:account-key userPathStyleUrl:flag]]; 

 

Creating an Azure Storage Table

The following code snippet shows how to create a table with the name Employees in Azure: 

#import “Context/ObjectContext.h”

#define AZURE_SERVICE_URL “http://<Account>.table.core.windows.net”

#define AZURE_ACCOUNT_NAME “specify your account name here”

#define AZURE_ACCOUNT_KEY “specify your account key here”

 

- (void) onBeforeSend:(HttpRequest*)request;

{

 

       AzureTableUtil *util=[[AzureTableUtil alloc]

       initWithAccountName:AZURE_ACCOUNT_NAME accountKey:AZURE_ACCOUNT_KEY usePathStyleUri:NO];

      

       @try

       {

              [[request getHeaders]CopyFrom:[util getSignedHeaders:[request getUri]]];

       }

       @catch (NSException *e) {

              @throw e;

       }

}

 

@try

{     

       AzureTableCredential *cred=[[AzureTableCredential alloc]

       initWithAccountName:AZURE_ACCOUNT_NAME accountKey:AZURE_ACCOUNT_KEY userPathStyleUrl:YES];

      

       ObjectContext *proxy=[[ObjectContext alloc]

       initWithUri:AZURE_SERVICE_URL credentials:cred dataServiceVersion:@"1.0"];

      

       [proxy setODataDelegate:self];

             

       Tables *table=[[Tables alloc]initWithUri: AZURE_SERVICE_URL];

       [table setTableName:@"Table1"];

       [proxy addObject:@"Tables" object:table];

       [proxy saveChanges];

}     

@catch(NSException *e)

{

       NSLog(@"Exception:%@:%@",[e name],[e reason]);

}

Inserting a Blob Entity into an Azure Table

 

You must define the table entry metadata for the Employee entity. The Employees table stores the table entry metadata.

When you define the table entry metadata, ensure that the following rules are satisfied:

·         Derive the table entry from the TableEntry class.

·         All the properties in the Azure table must have the attribute @Type:EntityProperty.

·         The table entry must have the following attributes:

o   @Class:name_of_table_Entry_class

o   @Key:PartitionKey

o   @Key:RowKey

 The following code snippet shows how to insert an entity into a table:

 

#import “Context/ObjectContext.h”

#define AZURE_SERVICE_URL “http://<Account>.table.core.windows.net”

#define AZURE_ACCOUNT_NAME “specify your account name here”

#define AZURE_ACCOUNT_KEY “specify your account key here”

 

@interface Person :TableEntry

{

        NSString *m_Name;

        NSString *m_Age;

}

 

@property(nonatomic,retain,getter=getName, setter = setName)NSString *m_Name;

@property(nonatomic,retain,getter=getAge, setter = setAge)NSString *m_Age;

 

-(id) initWithUri:(NSString *)aUri;

 

@end

 

@implementation Person

@synthesize m_Name,m_Age;

 

-(id) initWithUri:(NSString *)aUri

{

       self=[super initWithUri:aUri];

       return self;

}

@end

 

@try

{     

       AzureTableCredential *cred=[[AzureTableCredential alloc]

       initWithAccountName:AZURE_ACCOUNT_NAME accountKey:AZURE_ACCOUNT_KEY userPathStyleUrl:YES];

      

       ObjectContext *proxy=[[ObjectContext alloc]

       initWithUri:AZURE_SERVICE_URL credentials:cred dataServiceVersion:@"1.0"];

      

       [proxy setODataDelegate:self];

 

       Person *tableEntry=[[Person alloc]initWithUri:@""];

       [tableEntry setPartitionKey:@"Partition1"];

       [tableEntry setRowKey:@"Row1"];

       [tableEntry setName:@"TableEntry1"];

       [tableEntry setAge:@"37"];

             

       [proxy addObject:@"Person" object:tableEntry];//Person is the table name

       [proxy saveChanges];

}

      

@catch(NSException *e)

{

       NSLog(@"Exception:%@:%@",[e name],[e reason]);

}

 

Updating a Table Entry by Adding a New Property

The following code snippet shows how to update the definition of an entity (table entry metadata) for the Employee entities with address property.  

#import “Context/ObjectContext.h”

#define AZURE_SERVICE_URL “http://<Account>.table.core.windows.net”

#define AZURE_ACCOUNT_NAME “specify your account name here”

#define AZURE_ACCOUNT_KEY “specify your account key here”

 

@interface Table1 :TableEntry

{

    NSString *m_Name;

    NSString *m_Age;

    NSString *m_Address;

}

@property(nonatomic,retain,getter=getName, setter = setName)NSString *m_Name;

@property(nonatomic,retain,getter=getAge, setter = setAge)NSString *m_Age;

@property(nonatomic,retain,getter=getAddress, setter = setAddress)NSString *m_Address;

-(id) initWithUri:(NSString *)aUri;

@end

 

@implementation Table1

@synthesize m_Name,m_Age,m_Address;

 

-(id) initWithUri:(NSString *)aUri

{

       self=[super initWithUri:aUri];

       return self;

}

@end

 

@try

{     

       AzureTableCredential *cred=[[AzureTableCredential alloc]

       initWithAccountName:AZURE_ACCOUNT_NAME accountKey:AZURE_ACCOUNT_KEY userPathStyleUrl:YES];

      

       ObjectContext *proxy=[[ObjectContext alloc]

       initWithUri:AZURE_SERVICE_URL credentials:cred dataServiceVersion:@"1.0"];

      

       [proxy setODataDelegate:self];

 

 

       DataServiceQuery *query=[[DataServiceQuery alloc]initWithUri:@"Table1()" objectContext:proxy];

       QueryOperationResponse *response=[query execute];

       languageArray=[response getResult];

       Table1 *tblEntry=(Table1 *)[languageArray objectAtIndex:0];

             

       if(tblEntry)

       {

              [tblEntry setName:[NSString stringWithFormat:@"updatedEntry",[tblEntry getName]]];

              [tblEntry setAddress:@"new address"];

              [proxy updateObject:tblEntry];

              [proxy saveChanges];

       }

}

      

@catch(NSException *e)

{

       NSLog(@"Exception:%@:%@",[e name],[e reason]);

}

  

Deleting a Table Entry and Table

To delete a table entry from a table and a table from Azure, perform the following steps:

1.       Fetch the table entry and table.

2.       Use the DeleteObject API to mark the table and table entry as deleted.

3.       Save the changes.

The following code snippet shows how to delete a table entry from a table and a table from Azure:

#import “Context/ObjectContext.h”

#define AZURE_SERVICE_URL “http://<Account>.table.core.windows.net”

#define AZURE_ACCOUNT_NAME “specify your account name here”

#define AZURE_ACCOUNT_KEY “specify your account key here”

 

@interface Table1 :TableEntry {

    NSString *m_Name;

    NSString *m_Age;

    NSString *m_Address;

}

@property(nonatomic,retain,getter=getName, setter = setName)NSString *m_Name;

@property(nonatomic,retain,getter=getAge, setter = setAge)NSString *m_Age;

@property(nonatomic,retain,getter=getAddress, setter = setAddress)NSString *m_Address;

-(id) initWithUri:(NSString *)aUri;

@end

@implementation Table1

@synthesize m_Name,m_Age,m_Address;

 

-(id) initWithUri:(NSString *)aUri

{

       self=[super initWithUri:aUri];

       return self;

}

@end

 

@try

{     

       AzureTableCredential *cred=[[AzureTableCredential alloc]

       initWithAccountName:AZURE_ACCOUNT_NAME accountKey:AZURE_ACCOUNT_KEY userPathStyleUrl:YES];

      

       ObjectContext *proxy=[[ObjectContext alloc]

       initWithUri:AZURE_SERVICE_URL credentials:cred dataServiceVersion:@"1.0"];

      

       [proxy setODataDelegate:self];

 

       DataServiceQuery *query=[[DataServiceQuery alloc]

       initWithUri:@"test3()" objectContext:proxy];

      

       [query filter:@"Name eq 'updatedEntry'"];

       QueryOperationResponse *response=[query execute];

       languageArray=[response getResult];

       Table1 *tblEntry=(Table1 *)[languageArray objectAtIndex:0];

 

       if(tblEntry)

       {     

              [proxy deleteObject:tblEntry];

              [proxy saveChanges];

       }

 

       query=[[DataServiceQuery alloc]initWithUri:@"Tables" objectContext:proxy];

       [query filter:@"TableName eq 'test3'"];

       response=[query execute]; 

       languageArray=[response getResult];

       Tables *table=[languageArray objectAtIndex:0];

      

       if(table)

       {     

              [proxy deleteObject:table];

              [proxy saveChanges];

       }

}     

@catch(NSException *e)

{

       NSLog(@"Exception:%@:%@",[e name],[e reason]);

}

 

FunctionImport

FunctionImport element in service metadata is used to import Stored Procedures or Functions defined in Store Schema Model into Entity Data Model. Odatagen will generate the function in the class as per functionimport element in service metadata document.  You can then use OData Client for invoking the function by passing the parameter(s) required by this function. Following are some of the examples of FunctionImport:

Function which accept single parameter and return collection:

DemoService *proxy=[[DemoService alloc]initWithUri:@"http://services.odata.org/OData/OData.svc/" credential:nil];

 

NSArray *arr=

       [proxy

              GetProductsByRatingWithrating:[NSNumber numberWithInt:4]

       ];

 

Product *p=nil;

int count=[arr count];

 

for (int i=0;i<count;i++)

{

       p=[arr objectAtIndex:i];

       NSLog(@"name = %@",[p getName]);

}

 

[proxy release];

 

Function accepts multiple parameters and return collection:

Entities *proxy=[[QPEntities alloc]initWithUri:@"http://MyServer/webservice.svc/" credential:nil];

NSArray *array=

  [proxy

    Locations:[NSDecimalNumber decimalNumberWithString:@"32.12537"]

    lng:      [NSDecimalNumber decimalNumberWithString:@"-110.9353"]

    dist:[NSNumber numberWithInt:5]

    arr:0

    dep:0

  ];

 

LocationDetails *ld=nil;

int count=[array count];

 

for (int i=0;i<count;i++)

{

       ld=[array objectAtIndex:i];

       NSLog(@"location name = %@",[ld getLocationName]);

}

[proxy release];

 

BaseType/Inheritance

BaseType attribute is used for declaring inheritance in service metadata document.  ODatagen tool will generate the proxy classes for base class as well derived classes.  The OData Client for Objective-C includes support to access and modify the base class as well as derived class object.

 

Example for retrieving derived class objects:

ODataServiceEntities *proxy=[[ODataServiceEntities alloc]initWithUri:@"http://Myserver/odatabasetype.svc" credential:nil];

 

DataServiceQuery* query=[proxy clients];

QueryOperationResponse* response=[query execute];

NSArray* array=[response getResult];

 

int count=[array count];

 

for(int i=0;i<count;i++)

{

    NSLog(@"client id = %@",[[array objectAtIndex:i]getClientID]);

}

 

[proxy release];

 

Example for adding derived class object:

ODataServiceEntities *proxy=[[ODataServiceEntities alloc]initWithUri:@"http://MyServer/odatabasetype.svc" credential:nil];

 

ODataBool* b=[[ODataBool alloc]init];

b.booleanvalue=YES;

 

IndividualClient *individualClientObj=[IndividualClient CreateIndividualClient];

[individualClientObj setClientID:[ODataGUID NewGuid]];

[individualClientObj setArchive:b];

 

[proxy addToClients: individualClientObj];

[proxy saveChanges];

 

[proxy release];

 

Example for updating derived class object:

ODataServiceEntities *proxy=[[ODataServiceEntities alloc]initWithUri:@"http://MyServer/odatabasetype.svc" credential:nil];

 

DataServiceQuery* query=[proxy clients];

[query filter:[NSString stringWithFormat:@"isof('%@.IndividualClient')",[proxy getServiceNamespace]]];

 

QueryOperationResponse* response=[query execute];

NSArray* array=[response getResult];

 

IndividualClient * individualClientObj =[array objectAtIndex:0];

[individualClientObj setNotes:@"updated client"];

[individualClientObj setTitle:@"Title1"];

 

[proxy updateObject: individualClientObj];

[proxy saveChanges];

 

[proxy release];

 

Example for deleting derived class object:

ODataServiceEntities *proxy=[[ODataServiceEntities alloc]initWithUri:@"http://MyServer/odatabasetype.svc" credential:nil];

 

DataServiceQuery* query=[proxy clients];

QueryOperationResponse* response=[query execute];

NSArray* array=[response getResult];

 

Client * clientObj =[array objectAtIndex:0];

[proxy deleteObject: clientObj];

[proxy saveChanges];

 

[proxy release];

Using the Call Back Feature

The OData Client for Objective-C allows a client application to register the callback functions. The OData Client for Objective-C exposes the following APIs to register the callback functions:

·         ObjectConext::OnBeforeSend

·         ObjectContext::OnAfterReceive

The OnBeforeSend API is used to register a function that is invoked before the OData Client for Objective-C makes a request to OData Service. The OData Client for Objective-C makes a service request in the following cases:

·         When a client executes any query using the DataServiceQuery::execute, DataServiceRequest::count, ObjectContext::loadProperty, or ObjectContext::execute APIs.

·         When a client retrieves the media stream that is associated with an MLE entity using the ObjectContex::getReadStream API.

·         When a client invokes the ObjectContext::saveChanges API to save the entity instances and links, in context to the underlying OData Service.

The OData Client for Objective-C invokes the registered function by passing a reference to the NSMutableDictionary object. The invocation of the registered function happens after the client application registers a callback function using the OnBeforeSend API, and before making any request to the OData Service. The client application can also add header information which consists of additional key-value pairs, to OData Service.

To add the authorization header to the HTTP request headers before you make a request to an ACS protected OData Service, you can use the following code snippet:

 

/**

 * CallBack function, this will be invoked before making request to

 * OData service.

 */

 

- (void) onBeforeSend:(HttpRequest*)request;

{

       NSLog(@"onBeforeSend");

      

       ACSUtil *util=[[ACSUtil alloc]initWithServiceName:@"service_name"

       wrapName:@"wrap_name" wrapPassword:@"wrap_password"

       wrapScope:@"wrap_scope" claims:nil proxy:nil];

      

       @try

       {

              [[request getHeaders] CopyFrom:[util getSignedHeaders]];

       }

       @catch (NSException *e) {

              @throw e;

       }

}

 

@try

{

       ACSCredential *cred=[[ACSCredential alloc]initWithServiceName:@" service_name "

       wrapName:@" wrap_name " wrapPassword:@" wrap_password "

       wrapScope:@" wrap_scope " claims:nil proxy:nil];

      

       NorthwindEntities1 *proxy=[[NorthwindEntities1 alloc]

       initWithUri:@"http://MyServer/acsnorthwinddataservice.svc/" credential:cred];

      

       [proxy setODataDelegate:self];

       [proxy getSVC];

       DataServiceQuery *query=[proxy customers];

       QueryOperationResponse *response=[query execute];

 

       if(response)

       {

              CustomerArray=[response getResult];

              int count=[ CustomerArray count];

              for(int i=0;i<count;i++)

              {

                    NSLog(@"Customer=%@",[[ CustomerArray objectAtIndex:i]getContactName]);

              }

       }

}

@catch(NSException *e)

{

       NSLog(@"Exception:%@:%@",[e name],[e reason]);

}

 

Note:

For more information on how to configure a service with the acs authentication, refer to samples in the location Samples/ODataServices/ACSNorthWindDataServices.

This code snippet shows how to use the ACSUtil class to retrieve the ACS token and add this token as the value of the HTTP authorization header.

The following code snippet shows how you can use the ACSCredential class and HttpRequest::Credential to add additional header information for ACS: 

- (void) onBeforeSend:(HttpRequest*)request;

{

       NSLog(@"onBeforeSend");

      

       ACSUtil *util=[[ACSUtil alloc]initWithServiceName:@"service_name"

       wrapName:@"wrap_name" wrapPassword:@"wrap_password"

       wrapScope:@"wrap_scope" claims:nil proxy:nil];

 

       @try

       {

              [[request getHeaders] CopyFrom:[util getSignedHeaders]];

       }

       @catch (NSException *e) {

              @throw e;

       }

}

 

After you receive a response from the OData Service and before you convert the response to entity instances in the context, you can use the OnAfterReceive API to register a function which is invoked.

Once the client application registers a callback function using OnAfterReceive API, and after a response is received from the O Data Service, the OData Client for Objective-C invokes the registered function by passing a reference to the NSDictionary object.  The NSDictionary object contains the response header information.

The following code snippet shows how to use the call back feature to print the response body for a query from a server:

- (void) onAfterReceive:(HttpResponse*)response

{

       NSLog(@"onAfterReceive");

       NSLog(@"%@",[response getHTMLFriendlyBody]);

}

 

@try

{

       //all customers in London

       [proxy setODataDelegate:self];

       DataServiceQuery* query = [proxy  customers];

       [query filter:@“ City eq ‘London’”];

       QueryOperationResponse *response  =[query execute];

}

@catch(DataServiceRequestException *exception)

{

    NSLog(@”Exception = %@”, [exception reason]); 

}

 

 

Note:

When the OData Client for Objective-C invokes the callback function and the definition of the callback function is not found, the OData Client for Objective-C throws the DataServiceRequestException exception.

Authentication in the Client Library

The OData Client for Objective-C toolkit works against OData Service that is hosted on Internet Information Services (IIS) along with any of the following authentication mechanisms:

·         Windows (or anonymous) authentication

·         Azure Storage Tables with Azure authentication

·         OData Services with ACS authentication

The OData Client for Objective-C assumes that OData Service uses an anonymous Windows connection. So, the OData Client for Objective-C does not allow you to specify the user credentials. However, you can set the Credentials property in the proxy object to point to the object that holds the information on the user credentials. The Credential property is used if OData Service requires the user to be authenticated. 

You can use the following code snippet for the Windows authentication:

WindowsCredential *credt= [[WindowsCredential alloc] initWithUserName:@”domain\user-name” password:@”password”];

NorthWindEntities1 *proxy = [[NorthWindEntities1 alloc] initWithUri:Uri credential:credt];

 

You can use the following code snippet for the Azure table authentication:

AzureTableCredential *cred=[[AzureTableCredential alloc]initWithAccountName:AZURE_ACCOUNT_NAME accountKey:AZURE_ACCOUNT_KEY userPathStyleUrl:YES/*currently value should be YES*/];

 

ObjectContext *proxy=[[ObjectContext alloc]initWithUri:AZURE_SERVICE_URL credentials:cred dataServiceVersion:@"1.0"/*value should be 1.0 only*/];

 

You can use the following code snippet for the ACS authentication:

ACSCredential *cred=[[ACSCredential alloc]initWithServiceName: service_namespace

              wrapName: wrap_name wrapPassword: wrap_password

              wrapScope: wrap_scope claims:claims /*array of claims*/

              proxy:nil/*currently value should be nil*/];

 

NorthwindEntities1 *proxy=[[NorthwindEntities1 alloc]initWithUri:Uri credential:cred];

 

 

Note:

If your service is running on the local machine with the ACS authentication, you can pass the proxy settings as an argument of the ACSCredential class.. In this case, you require the proxy information to retrieve the ACS token.

Custom Authentication Support

If the client application needs to set custom headers, you can register the callback function using the OnBeforeSend API.

Reference Section

The following table lists the APIs that the OData Client for Objective-C supports:

API Prototype

Parameters

Return Value

Description

execute :query-expression

The query to be executed against a data service. For more information on query expressions, refer to the Data Service Query Expressions section.

OnSuccess: Returns the result of a query as a collection.

OnFailure: If an error occurs (for example, malformed query), the OData Client for Objective-C throws the DataServiceRequestException exception with a suitable message.

Executes queries against the data service.

addObject:TargetEntitySet :ODataObject

TargetEntitySet: The name of the entity set that the object belongs to.

ODataObject :ODataObject :The instance of a data service that you want to insert.

If an error occurs (for example, the entity instance already exists), the OData Client for Objective-C throws InvalidOperation exception with s suitable message.

Creates an instance in a data service.

updateObject:ODataObject

The instance of a data service entity that you must delete.

If an error occurs (for example, the entity instance does not exist), the OData Client for Objective-C throws the InvalidOperation exception with a  suitable message.

Modifies an existing entity instance.

deleteObject:ODataObject

Represents the existing instance of a data service entity, which needs to be deleted.

If an error occurs (For example: if the entity instance does not exist), then the OData Client for Objective-C throws an InvalidOperation exception with a suitable message.

Deletes an entity instance.

addLink:Source :SourceProperty :Target

Source: The source object in the link

SourceProperty: The name of the property on the source object that represents the source in the link between the source and the target.

Target: The target object in the link that is bound to the source object.

If an error occurs, the OData Client for Objective-C throws the InvalidOperation exception with a suitable message.

Assumes that a relationship exists between the two entities, and adds a link between these entity instances.  This method supports only adding the links to relationships with multiplicity = * (one-to-many relationships). For example, when the source property is a collection, you can use the SetLink method to add links to relationships with multiplicity = 1 (many-to-one relationships).

deleteLink:Source :SourceProperty :Target

Source: The source object in the link.

SourceProperty: The name of the property on the source object that represents the source in the link between the source and the target

Target: The target object in the link that is bound to the source object

If an error occurs, the OData Client for Objective-C throws an InvalidOperation exception with a suitable message.

Deletes the link between two entity instances. This method supports only deleting the links between relationships with multiplicity = * (one-to-many relationships). For example, when the source property is a collection, you can use the SetLink method with Target as null, to delete the links between relationships with multiplicity = 1 (many-to-one relationships).

setLink:Source :SourceProperty :Target

Source: The source object in the link

SourceProperty: The name of the property on the source object that represents the source in the link between the source and the target

Target: The target object in the link that is bound to the source object

If an error occurs, the OData Client for Objective-C throws the InvalidOperation exception with a suitable message.

Assumes that there is a relationship between the two entities and adds a link between two entity instances. This method supports adding links to relationships with multiplicity = 1 (many-to-one relationships). For example, when the source property is a reference, you can use the AddLink method for adding links to relationships with multiplicity = * (one-to-many relationships). If the target argument is null, the link represented by sourceProperty is deleted.

addHeader:Header-Name :Header-Value

Header-Name: The name of the custom HTTP header

Header-Value: The value of the custom HTTP header.

No return value

Adds custom headers.

removeHeaders

No argument

No return value

Removes all the custom headers that the user adds.

saveChanges

No arguments

If an error occurrs while generating the AtomPub XML file that you want to send to the service, the OData Client for Objective-C throws the InvalidOperation exception.

If OData Service returns any error, the OData Client for Objective-C throws the ODataServiceException exception with a suitable message.

Saves the changes that are made to the entities in context to a data service.

setSaveChangesOptions:scOptions

scOptions: The values SaveChangesOptions::None or SaveChangesOptions::Batch.

If the user passes an invalid parameter, the OData Client for Objective-C throws the InvalidOperation exception.

Sets the SaveChanges mode.  If this mode is set to Batch, all the changes are packed in a single HTTP request. Otherwise, the framework generates a separate HTTP request for each change. The default mode is Batch.

setReplaceOnUpdate:rOnUpdate

rOnUpdate: Boolean values

None

Decides the HTTP verb that is used for updation. If this flag is set to true, the framework uses the PUT verb, else uses the MERGE verb.

getReadStream:entity  :args

entity: The entity that has the binary property to retrieve.

args: The possible values are null, string, or an instance of the DataServiceRequestArgs class.

Throws the InvalidOperation exception if args is not null, string, or an instance of the DataServiceRequestArgs class. If the entity is not a Media Link Entry (MLE), this API throws the invalid exception. If OData Service returns an error, this API throws the ODataServiceException exception.

Synchronously requests a data stream that contains the binary property of requested MLE entity. The args argument can be null, a string that represents the Accept message header, or an instance of the DataServiceRequestArgs class that contains the settings for the HTTP request message (for example, Slug, Accept, and Content-Type).

getReadStreamUri:entity

entity: The entity that has the binary property to retrieve

If the context is not currently tracking the entity, this API throws  the InvalidOperation exception.

Gets the URI that is used to return the binary property data as a data stream.

setSaveStream:entity :stream :closeStream :contentType :slug

entity: The entity that has the binary property to set

stream: The binary stream to set

contentType: A type of  stream

slug: The default path and name of the stream after the stream is uploaded

Throws the InvalidOperation exception, if any one of the following criteria is not satisfied:

·         contentType is not null

·         slug is not null

·         stream is not null

Sets a new data stream as the binary property of an entity, with the specified settings in the request message.

onBeforeSend:HttpHander_Object

HttpHander_Object: The reference of the HttpHandler object

None

Registers the callback function when invoked by the OData Client for Objective-C. Before sending any request to the service, the OData Client for Objective-C extends the class with odatadelegate protocol and implements the onBeforeSend function.

onAfterReceive: HttpHander_Object

HttpHander_Object: The reference of the HttpHandler object

None

Registers the callback function when invoked by the OData Client for Objective-C. Before sending any request to the service, the OData Client for Objective-C extends the class with odatadelegate protocol and implements the onAfterReceive function.