|
OData
Client for Objective-C User Guide |
|
|
Contents
Installation and Configuration of OData Client
Data Service Query Expressions
DataServiceRequestException Class
Loading the Related Entities Using the Expand Option and the
LoadProperty API
Creating a Instance in a Data Service
Updating an Instance in Data Service
Deleting an Instance from a Data Service
Creating a Link between Instances Using AddLink
Deleting the Link between Instances Using DeleteLink
Creating and Deleting the Link between Instances Using
SetLink
Batch and Non-Batch Modes of the SaveChanges Operation
Associating a Stream with the MLE entity Using GetReadStream
Creating an MLE Entity and Associating it with a Stream
Using setSaveStream
Working with the Azure Storage Tables
Creating an Azure Storage Table
Inserting a Blob Entity into an Azure Table
Updating a Table Entry by Adding a New Property
Deleting a Table Entry and Table
Authentication in the Client Library
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.
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 |
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.
2. Click Build tab.
3. Click the Search Path settings.
4. Add the odatalib include path in Header Search Paths.
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.
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.
2. Navigate to the required LibMSOdatalib.a and click Add. LibMSOdatalib.a is built for both simulator and device SDKs.
3. Click Add to add it to the selected targets.
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] ]
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.
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.
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.
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.
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.
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 Client for
Objective-C supports the following types of exception classes:
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.
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. |
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. |
This section
explains the query options and samples that are used in the DataServiceQuery
class build and execute the query expressions.
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”];
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 |
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]);
}
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]);
}
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]);
}
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]);
}
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.
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]);
}
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.
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]);
}
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]);
}
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]);
}
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];
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. |
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];
}
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];
}
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]];
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]);
}
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]);
}
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]);
}
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 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 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];
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.
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.
If the client
application needs to set custom headers, you can register the callback function
using the OnBeforeSend API.
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. |