91

Protips for Windows Azure Mobile Services

  • Upload
    didina

  • View
    53

  • Download
    0

Embed Size (px)

DESCRIPTION

Protips for Windows Azure Mobile Services . Chris Risner Technical Evangelist 3-543. Introduction. Windows Azure Technical Evangelist. @ chrisrisner. Mobile Developer. http:// chrisrisner.com. Former .NET developer. Live in Washington. Grew up in Michigan. - PowerPoint PPT Presentation

Citation preview

Page 1: Protips  for Windows Azure  Mobile  Services
Page 2: Protips  for Windows Azure  Mobile  Services

Protips for Windows Azure Mobile Services Chris RisnerTechnical Evangelist3-543

Page 3: Protips  for Windows Azure  Mobile  Services

Introduction

@chrisrisner

http://chrisrisner.com

Live in Washington

Windows Azure Technical Evangelist

Mobile Developer

Former .NET developer

Co-Organizer of Seattle GDG

Grew up in Michigan

Page 4: Protips  for Windows Azure  Mobile  Services

Agenda

Mobile Services Recap

Tricks

Tips

Tips

Tricks Questions

Page 5: Protips  for Windows Azure  Mobile  Services

Mobile Services Recap

Page 6: Protips  for Windows Azure  Mobile  Services

Windows Azure Mobile Services

Data

Notifications

Auth

Server Logic

Scheduler

Logging & Diag

Scale

Page 7: Protips  for Windows Azure  Mobile  Services

Multi-Platform Apps

Page 8: Protips  for Windows Azure  Mobile  Services

You don’t need a different Mobile Service for each platform!

Page 9: Protips  for Windows Azure  Mobile  Services

Connect them all!

Page 10: Protips  for Windows Azure  Mobile  Services

Cross-Platform Support

Page 11: Protips  for Windows Azure  Mobile  Services

Cross-Platform Support

Page 12: Protips  for Windows Azure  Mobile  Services

Multi-Device Push

Page 13: Protips  for Windows Azure  Mobile  Services

Single Platform Push NotificationsWindows Storepush.wns.sendToastText04(item.channel, {text1: text}, … );

Windows Phonepush.mpns.sendFlipTile(item.channel, {title: text}, …);

iOSpush.apns.send(item.token, { alert: text, payload:

{ inAppMessage: Details }}, …);

Androidpush.gcm.send(item.registrationId, item.text, …);

Page 14: Protips  for Windows Azure  Mobile  Services
Page 15: Protips  for Windows Azure  Mobile  Services

Multi-Platform Push Notificationsfunction sendNotifications() {

var deviceInfoTable = tables.getTable('DeviceInfo');

deviceInfoTable.where({ userId : user.userId }).read({

success: function(deviceInfos){

deviceInfos.forEach(function(deviceInfo){

if (deviceInfo.uuid != request.parameters.uuid) {

if (deviceInfo.pushToken != null && deviceInfo.pushToken != 'SimulatorToken') {

if (deviceInfo.platform == 'iOS') {

push.apns.send(deviceInfo.pushToken, {

alert: "New something created"

} , { //success / error block});

} else if (deviceInfo.platform == 'Android') {

push.gcm.send(deviceInfo.pushToken, "New something created", { success / error block});

}

}

}

});

}

});

}

Page 16: Protips  for Windows Azure  Mobile  Services

Don’t forget to check the response on error (or getFeedback for APNS)

Also, check out Delivering Push Notifications to Millions of Devices – Friday @12pm

Page 17: Protips  for Windows Azure  Mobile  Services

Virtual Tables

Page 18: Protips  for Windows Azure  Mobile  Services

Create a tableUse it’s endpointDon’t call request.Execute

Page 19: Protips  for Windows Azure  Mobile  Services

Custom API

Page 20: Protips  for Windows Azure  Mobile  Services

• Non-table based scripts• Accessible from• GET• POST• PUT• PATCH• DELETE

• Permissions based

Custom API

Page 21: Protips  for Windows Azure  Mobile  Services

Custom API Demo

Page 22: Protips  for Windows Azure  Mobile  Services

Talking to Azure Storage

Page 23: Protips  for Windows Azure  Mobile  Services

It’s doableIt’s not perfectScriptsand the Azure module

Page 24: Protips  for Windows Azure  Mobile  Services

Reading Tablesvar azure = require('azure');

function read(query, user, request) {

var accountName = 'accountname';

var accountKey = 'Accountkey------------nKHDsW2/0Jzg==';

var host = accountName + '.table.core.windows.net';

var tableService = azure.createTableService(accountName, accountKey, host);

tableService.queryTables(function (error, tables) {

if (error) {

request.respond(500, error);

} else {

request.respond(200, tables);

}

});

}

Page 25: Protips  for Windows Azure  Mobile  Services

Reading Table Rowsvar azure = require('azure');

function read(query, user, request) {

var accountName = 'accountname';

var accountKey = 'Accountkey------------nKHDsW2/0Jzg==';

var host = accountName + '.table.core.windows.net';

var tableService = azure.createTableService(accountName, accountKey, host);

var tq = azure.TableQuery

.select()

.from(request.parameters.table);

tableService.queryEntities(tq, function (error, rows) {

if (error) {

request.respond(500, error);

} else {

request.respond(200, rows)

}

});

}

Page 26: Protips  for Windows Azure  Mobile  Services

Creating Containersvar azure = require('azure');

function insert(item, user, request) {

var accountName = 'accountname';

var accountKey = 'Accountkey------------nKHDsW2/0Jzg==';

var host = accountName + '.blob.core.windows.net';

var blobService = azure.createBlobService(accountName, accountKey, host);

if (request.parameters.isPublic == 1) {

blobService.createContainerIfNotExists(item.containerName

,{publicAccessLevel : 'blob'}

, function (error) {

if (!error) { request.respond(200, item); } else { /* error */ request.respond(500);}

});

} else {

blobService.createContainerIfNotExists(item.containerName, function (error) {

if (!error) { request.respond(200, item); } else { /*error */ request.respond(500);

}

});

}

}

Page 27: Protips  for Windows Azure  Mobile  Services

Reading and “Creating” Blobsvar azure = require('azure'), qs = require('querystring');

function insert(item, user, request) {

var accountName = 'accountname';

var accountKey = 'Accountkey------------nKHDsW2/0Jzg==';

var host = accountName + '.blob.core.windows.net';

var blobService = azure.createBlobService(accountName, accountKey, host);

var sharedAccessPolicy = {

AccessPolicy: {

Permissions: 'rw', //Read and Write permissions

Expiry: minutesFromNow(5)

}

};

var sasUrl = blobService.generateSharedAccessSignature(request.parameters.containerName,

request.parameters.blobName, sharedAccessPolicy);

var sasQueryString = { 'sasUrl' : sasUrl.baseUrl + sasUrl.path + '?' + qs.stringify(sasUrl.queryString) };

request.respond(200, sasQueryString);

}

function minutesFromNow(minutes) {

var date = new Date()

date.setMinutes(date.getMinutes() + minutes);

return date;

}

Page 28: Protips  for Windows Azure  Mobile  Services

Storage Demo

Page 29: Protips  for Windows Azure  Mobile  Services

Talking REST

Page 30: Protips  for Windows Azure  Mobile  Services

The REST API

Action HTTP Verb URL SuffixCreate POST /TodoItemRetrieve GET /TodoItem?$filter=id%3D42Update PATCH /TodoItem/idDelete DELETE /TodoItem/id

Data Operations and their REST Equivalents

Base REST API Endpoint URLhttp://Mobileservice.azure-mobile.net/tables/*

Page 31: Protips  for Windows Azure  Mobile  Services

JSON to SQL Type MappingsJSON Value T-SQL TypeNumeric values (integer, decimal, floating point)

Float

Boolean BitDateTime DateTimeOffset(3)String Nvarchar(max)

Page 32: Protips  for Windows Azure  Mobile  Services

Postman &Runscope Demo

Page 33: Protips  for Windows Azure  Mobile  Services

Sending Emails

Page 34: Protips  for Windows Azure  Mobile  Services
Page 35: Protips  for Windows Azure  Mobile  Services

Sending an Email//var crypto = require('crypto');

//item.tempId = new Buffer(crypto.randomBytes(16)).toString('hex');

function sendEmail(item) {

var sendgrid = new SendGrid('[email protected]', 'mypassword');

var email = {

to : item.email,

from : '[email protected]',

subject : 'Welcome to MyApp',

text: 'Thanks for installing My App! Click this link to verify:\n\n'

+ 'http://myapp.azurewebsites.net/activate.html?id=' + item.id + '&tid=' + item.tempId,

createDate : new Date()

};

sendgrid.send({

to: item.email,

from: email.from,

subject: email.subject,

text: email.text

}, function(success, message) {

// If the email failed to send, log it as an error so we can investigate

if (!success) {

console.error(message);

} else {

saveSentEmail(email);

}

});

}

Page 36: Protips  for Windows Azure  Mobile  Services

Setting up SendGrid Demo

Page 37: Protips  for Windows Azure  Mobile  Services

The CLI

Page 38: Protips  for Windows Azure  Mobile  Services

It’s aweSOME

Page 39: Protips  for Windows Azure  Mobile  Services

CLI Demo

Page 40: Protips  for Windows Azure  Mobile  Services

Service Filters and DelegatingHandlers

Page 41: Protips  for Windows Azure  Mobile  Services

Client sideIntercepts requestsIntercepts responses

Page 42: Protips  for Windows Azure  Mobile  Services

Sending Version Info with Each Request- (void)handleRequest:(NSURLRequest *)request next:(MSFilterNextBlock)next response:(MSFilterResponseBlock)response{ MSFilterResponseBlock wrappedResponse = ^(NSHTTPURLResponse *innerResponse, NSData *data, NSError *error) { response(innerResponse, data, error); }; // add additional versioning information to the querystring for versioning purposes NSString *append = [NSString stringWithFormat:@"build=%@&version=%@", self.build, self.version]; NSURL *url = nil; NSRange range = [request.URL.absoluteString rangeOfString:@"?"]; if (range.length > 0) { url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&%@&p=iOS", request.URL.absoluteString, append]]; } else { url = [NSURL URLWithString:[NSString stringWithFormat:@"%@?%@&p=iOS", request.URL.absoluteString, append]]; } NSMutableURLRequest *newRequest = [request mutableCopy]; newRequest.URL = url; next(newRequest, wrappedResponse);}

Page 43: Protips  for Windows Azure  Mobile  Services

DelegatingHandlers are Service Filterspublic static MobileServiceClient MobileService = new MobileServiceClient( "https://<your subdomain>.azure-mobile.net/", "<your app key>", new VersionHandler()); using System;using System.Net.Http;using System.Threading.Tasks;namespace WindowsStore{ public class VersionHandler : DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,

System.Threading.CancellationToken cancellationToken) { request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.ToString() + "?version=v2"); return base.SendAsync(request, cancellationToken); } }}

Page 44: Protips  for Windows Azure  Mobile  Services

Script Versioning

Page 45: Protips  for Windows Azure  Mobile  Services

Checking the Version in Scriptsfunction insert(item, user, request) { if (request.parameters.build < 2.0) { item.description = 'Not entered'; } request.execute({ success : function() { if (request.parameters.build < 2.0) { delete item.description; } request.respond(); } }); }

Page 46: Protips  for Windows Azure  Mobile  Services

For more on versioning, check outGoing Live and Beyond with Windows Azure Mobile ServicesFriday @ 10:30 am

Page 47: Protips  for Windows Azure  Mobile  Services

Talking Twitter

Page 48: Protips  for Windows Azure  Mobile  Services

v1 is deadv1.1 is hard

Page 49: Protips  for Windows Azure  Mobile  Services

Part 1: The Helpersfunction generateOAuthSignature(method, url, data){

var index = url.indexOf('?');

if (index > 0)

url = url.substring(0, url.indexOf('?'));

var signingToken = encodeURIComponent('Your Consumer Secret') + "&" + encodeURIComponent('Your Access Token Secret');

var keys = [];

for (var d in data){

if (d != 'oauth_signature') {

console.log('data: ' , d);

keys.push(d);

}

}

keys.sort();

var output = "GET&" + encodeURIComponent(url) + "&";

var params = "";

keys.forEach(function(k){

params += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(data[k]);

});

params = encodeURIComponent(params.substring(1));

return hashString(signingToken, output+params, "base64");

}

function hashString(key, str, encoding){

var hmac = crypto.createHmac("sha1", key);

hmac.update(str);

return hmac.digest(encoding);

}

function generateNonce() {

var code = "";

for (var i = 0; i < 20; i++) {

code += Math.floor(Math.random() * 9).toString();

}

return code;

}

Page 50: Protips  for Windows Azure  Mobile  Services

Part 2: The Work (part 1)var crypto = require('crypto');

var querystring = require('querystring');

function read(query, user, request) {

var result = {

id: query.id,

identities: user.getIdentities(),

userName: ''

};

var identities = user.getIdentities();

var userId = user.userId;

var twitterId = userId.substring(userId.indexOf(':') + 1);

//API 1.0

//url = 'https://api.twitter.com/1/users/show/' + twitterId + '.json';

//API 1.1

var url = 'https://api.twitter.com/1.1/users/show.json?user_id=' + twitterId;

var key = 'This is your consumer key';

var nonce = generateNonce();

var sigmethod = 'HMAC-SHA1';

var version = '1.0';

var twitterAccessToken = identities.twitter.accessToken;

var oauth_token = 'The Access Token';

var seconds = new Date() / 1000;

seconds = Math.round(seconds);

var requestType = 'GET';

var oauthData = { oauth_consumer_key: key, oauth_nonce: nonce, oauth_signature:null,

oauth_signature_method: sigmethod, oauth_timestamp: seconds,

oauth_token: oauth_token, oauth_version: version };

var sigData = {};

for (var k in oauthData){

sigData[k] = oauthData[k];

}

sigData['user_id'] = twitterId;

Page 51: Protips  for Windows Azure  Mobile  Services

Part 2.2: The Workvar sig = generateOAuthSignature('GET', url, sigData);

oauthData.oauth_signature = sig;

var oauthHeader = "";

for (k in oauthData){

oauthHeader += ", " + encodeURIComponent(k) + "=\"" + encodeURIComponent(oauthData[k]) + "\"";

}

oauthHeader = oauthHeader.substring(1);

var authHeader = 'OAuth' + oauthHeader;

//Generate callback for response from Twitter API

var requestCallback = function (err, resp, body) {

if (err || resp.statusCode !== 200) {

console.error('Error sending data to the provider: ', err);

request.respond(statusCodes.INTERNAL_SERVER_ERROR, body);

} else {

try {

var userData = JSON.parse(body);

if (userData.name != null)

result.UserName = userData.name;

else

result.UserName = "can't get username";

request.respond(200, [result]);

} catch (ex) {

console.error('Error parsing response from the provider API: ', ex);

request.respond(statusCodes.INTERNAL_SERVER_ERROR, ex);

}

}

}

//Create the request and execute it

var req = require('request');

var reqOptions = {

uri: url,

headers: { Accept: "application/json" }

};

if (authHeader != null)

reqOptions.headers['Authorization'] = authHeader;

req(reqOptions, requestCallback);

}

Page 52: Protips  for Windows Azure  Mobile  Services

That was terribleDo this

Page 53: Protips  for Windows Azure  Mobile  Services

The Easy Wayexports.post = function(request, response) { var twitter = require(‘ctwitter.js’); twitter.init(’consumer key',’consumer secret'); twitter.tweet(request.body.tweettext, request.user, request);}

Get the script here: http://bit.ly/14b73Gg

Page 54: Protips  for Windows Azure  Mobile  Services

Script Source Control

Page 55: Protips  for Windows Azure  Mobile  Services

Enable on dashboardCreates Git repoChanges push from client

Page 56: Protips  for Windows Azure  Mobile  Services
Page 57: Protips  for Windows Azure  Mobile  Services

Shared Scripts

Page 58: Protips  for Windows Azure  Mobile  Services

require(‘jsfile.js');

*Need a config change on update (for now)

Page 59: Protips  for Windows Azure  Mobile  Services

Auth Part 1: Custom

Page 60: Protips  for Windows Azure  Mobile  Services

Pass creds inValidateHash your saltCreate a JWT

Page 61: Protips  for Windows Azure  Mobile  Services

Part 1: The Helpersfunction hash(text, salt, callback) { crypto.pbkdf2(text, salt, iterations, bytes, function(err, derivedKey){ if (err) { callback(err); } else { var h = new Buffer(derivedKey).toString('base64'); callback(null, h); } });} function slowEquals(a, b) { var diff = a.length ^ b.length; for (var i = 0; i < a.length && i < b.length; i++) { diff |= (a[i] ^ b[i]); } return diff === 0;} function zumoJwt(expiryDate, aud, userId, masterKey) { var crypto = require('crypto'); function base64(input) { return new Buffer(input, 'utf8').toString('base64'); } function urlFriendly(b64) { return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(new RegExp("=", "g"), ''); } function signature(input) { var key = crypto.createHash('sha256').update(masterKey + "JWTSig").digest('binary'); var str = crypto.createHmac('sha256', key).update(input).digest('base64'); return urlFriendly(str); } var s1 = '{"alg":"HS256","typ":"JWT","kid":0}'; var j2 = { "exp":expiryDate.valueOf() / 1000, "iss":"urn:microsoft:windows-azure:zumo”, "ver":1, "aud":aud, "uid":userId }; var s2 = JSON.stringify(j2); var b1 = urlFriendly(base64(s1)); var b2 = urlFriendly(base64(s2)); var b3 = signature(b1 + "." + b2); return [b1,b2,b3].join(".");}

Page 62: Protips  for Windows Azure  Mobile  Services

Part 2: The Workvar crypto = require('crypto'), iterations = 1000, bytes = 32;var aud = "Custom", masterKey = "MyMobileServiceMasterKey”;function insert(item, user, request) { var accounts = tables.getTable('accounts'); if (!item.username.match(/^[a-zA-Z0-9]{5,}$/)) { request.respond(400, "Invalid username (at least 4 chars, alphanumeric only)"); return; } else if (item.password.length < 7) { request.respond(400, "Invalid password (least 7 chars required)"); return; } accounts.where({ username : item.username}).read({ success: function(results) { if (results.length > 0) { request.respond(400, "Username already exists"); return; } else { // add a unique salt to the item item.salt = new Buffer(crypto.randomBytes(bytes)).toString('base64'); // hash the password hash(item.password, item.salt, function(err, h) { item.password = h; request.execute({ success: function () { // We don't want the salt or the password going back to the client delete item.password; delete item.salt; var userId = aud + ":" + item.id; item.userId = userId; var expiry = new Date().setUTCDate(new Date().getUTCDate() + 30); item.token = zumoJwt(expiry, aud, userId, masterKey); request.respond(); } }); }); } } });}

Page 63: Protips  for Windows Azure  Mobile  Services

Part 3: Signing Invar crypto = require('crypto'), iterations = 1000, bytes = 32;var aud = "Custom", masterKey = "MyMobileServiceMasterKey"; function insert(item, user, request) { var accounts = tables.getTable('accounts'); accounts.where({ username : item.username }).read({ success: function(results) { if (results.length === 0) { request.respond(401, "Incorrect username or password"); } else { var account = results[0]; hash(item.password, account.salt, function(err, h) { var incoming = h; if (slowEquals(incoming, account.password)) { var expiry = new Date().setUTCDate(new Date().getUTCDate() + 30); var userId = aud + ":" + account.id; request.respond(200, { userId: userId, token: zumoJwt(expiry, aud, userId, masterKey) }); } else { request.respond(401, "Incorrect username or password"); } }); } } });}

Page 64: Protips  for Windows Azure  Mobile  Services

…or just use Auth0http://aka.ms/authZeroZumo

Check out Who’s that User? – Friday @ 2pm

Page 65: Protips  for Windows Azure  Mobile  Services

Auth Part 2: Identity Caching

Page 66: Protips  for Windows Azure  Mobile  Services

Storing Credentials in .NETpublic static class CredentialLocker    {        private const string RESOURCE= "MobileServices";        public static void AddCredential(string username, string password) {            var vault = new PasswordVault();            var credential = new PasswordCredential(RESOURCE, username, password);            vault.Add(credential);        }        public static PasswordCredential GetCredential() {                        PasswordCredential credential = null;                        var vault = new PasswordVault();                        try {                                        credential = vault.FindAllByResource(RESOURCE).FirstOrDefault();                                if (credential != null){                                                               credential.Password = vault.Retrieve(RESOURCE, credential.UserName).Password;                        }                }    

catch (Exception)    { //creds not found       }

            return credential;        }

        public static void RemoveCredential(string username) {                var vault = new PasswordVault();                try {                        // Removes the credential from the password vault.                        vault.Remove(vault.Retrieve(RESOURCE, username));                }                catch (Exception)    { //creds not stored       }        }    }

Page 67: Protips  for Windows Azure  Mobile  Services

Getting and Setting CredentialsSetting:

MoblieServiceUser user;user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook);CredentialLocker.AddCredential(user.userId, user.MobileServiceAuthenticationToken);

Getting:

var credential = CredentialLocker.GetCredential();if (credential != null){ MobileService.CurrentUser = new MobileServiceUser(credential.UserName); MobileService.CurrentUser.MobileServiceAuthenticationToken =

credential.Password; }

Page 68: Protips  for Windows Azure  Mobile  Services

Auth Part 3: Expired Tokens

Page 69: Protips  for Windows Azure  Mobile  Services

Expiration FlowInitial request

Check response for 401

Relogin user

Update request with new token

Resend request

Update UI

Page 70: Protips  for Windows Azure  Mobile  Services

DelegationHandlers (again)public class VersionHandler : DelegatingHandler{ protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,

System.Threading.CancellationToken cancellationToken) { var response = await base.SendAsync(request, cancellationToken); while (response.StatusCode == HttpStatusCode.Unauthorized) { try { await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook); request.Headers['X-ZUMO-AUTH'] = App.MobileService.CurrentUser.MobileServiceAuthenticationToken; } catch (Exception ex) {} response = await base.SendAsync(request, cancellationToken); } } }

Page 71: Protips  for Windows Azure  Mobile  Services

ServiceFilter (iOS)- (void) filterResponse: (NSHTTPURLResponse *) response forData: (NSData *) data withError: (NSError *) error forRequest:(NSURLRequest *) request onNext:(MSFilterNextBlock) onNext onResponse: (MSFilterResponseBlock) onResponse{ if (response.statusCode == 401) { [self.client loginWithProvider:@"facebook" onController:[[[[UIApplication sharedApplication] delegate] window] rootViewController]

animated:YES completion:^(MSUser *user, NSError *error) { if (error && error.code == -9001) { [self busy:NO]; onResponse(response, data, error); return; } NSMutableURLRequest *newRequest = [request mutableCopy]; [newRequest setValue:self.client.currentUser.mobileServiceAuthenticationToken forHTTPHeaderField:@"X-ZUMO-AUTH"]; onNext(newRequest, ^(NSHTTPURLResponse *innerResponse, NSData *innerData, NSError *innerError){ [self filterResponse:innerResponse forData:innerData withError:innerError forRequest:request onNext:onNext onResponse:onResponse]; }); }]; } else { [self busy:NO]; onResponse(response, data, error); }}

Page 72: Protips  for Windows Azure  Mobile  Services

Auth Demo

Page 73: Protips  for Windows Azure  Mobile  Services

One-to-Many

Page 74: Protips  for Windows Azure  Mobile  Services

ClientServer

Page 75: Protips  for Windows Azure  Mobile  Services

Clientpublic class BigTodo { public int Id { get; set;} ... [IgnoreDataMember] public List<LittleTodo> LittleTodos { get; set; }

}

//writing dataprivate async Task InsertBigTodo(MobileServiceClient mobileServiceClient, BigTodo bigTodo){ var bigTodoTable = mobileServiceClient.GetTable<BigTodo>(); await bigTodoTable.InsertAsync(bigTodo); var bigTodoId = bigTodo.Id; var littleTodosTable = mobileServiceClient.GetTable<LittleTodo>(); foreach (var littlTodo in bigTodo.LittleTodos) { littleTodo.BigTodoId = bigTodoId; await littleTodosTable.InsertAsync(littleTodo); }}

Page 76: Protips  for Windows Azure  Mobile  Services

Server 1function insert(item, user, request) { var littleTodosTable = tables.getTable('LittleTodo'); var littleTodos = item.LittleTodos; var ids = new Array(littleTodos.length); var count = 0; littleTodos.forEach(function(littleTodo, index) { littleTodosTable.insert(littleTodos, { success: function() { // keep a count of callbacks count++; // build a list of new ids - make sure // they go back in the right order ids[index] = littleTodos.id; if (littleTodos.length === count) { // we've finished all updates, // send response with new IDs request.respond(201, { littleTodoIds: ids }); } } }); });}

Page 77: Protips  for Windows Azure  Mobile  Services

Server 2function insert(item, user, request) { var littleTodos; if (item.LittleTodos) { littleTodos = item.LittleTodos; delete item.LittleTodos; } request.execute({ success: function () { item.LittleTodos = []; if (littleTodos) { var i = 0; var insertNext = function () { if (i < littleTodos.length) { var littleTodo = littleTodos[i]; littleTodo.BigTodoId = item.id; littleTodo.LittleTodoOrder = i; tables.getTable('LittleTodo').insert(littleTodo, { success: function () { item.LittleTodos.push(littleTodo); i++; insertNext(); } }); } else { request.respond(); } }; insertNext(); } } });}

Page 78: Protips  for Windows Azure  Mobile  Services

Remember API call #s when considering client side one-to-many

Page 79: Protips  for Windows Azure  Mobile  Services

Paging Data

Page 80: Protips  for Windows Azure  Mobile  Services

ClientServer

Page 81: Protips  for Windows Azure  Mobile  Services

ClientC#:IMobileServiceTableQuery<TodoItem> query = todoTable .Where(todoItem => todoItem.Complete == false) .Skip(3) .Take(3);items = await query.ToCollectionAsync();ListItems.ItemsSource = items;

iOS:NSPredicate * predicate = [NSPredicate predicateWithFormat:@"complete == NO"];MSQuery * query = [self.table queryWithPredicate:predicate];query.includeTotalCount = YES; // Request the total item count

query.fetchOffset = 3;query.fetchLimit = 3;

[query readWithCompletion:^(NSArray *results, NSInteger totalCount, NSError *error) { …

Android:mToDoTable.where().field("complete").eq(false).skip(3).top(3) .execute(new TableQueryCallback<ToDoItem>() {

Page 82: Protips  for Windows Azure  Mobile  Services

Server ScriptsOption 1:query.where({complete: false}) .take(3) .skip(3);

Option 2:var q = query.getComponents();q.take = 3;q.skip = 1;query.setComponents(q);

Option 3:query.where(function() {

return this.complete == false}) .take(3) .skip(3);

Page 83: Protips  for Windows Azure  Mobile  Services

On-Prem

Page 84: Protips  for Windows Azure  Mobile  Services

On-Prem Solutions in Windows Azure

Secure Site-to-Site Network Connectivity

Windows Azure Virtual Network

CLOUD ENTERPRISE

Data Synchronization

SQL Data Sync

Application-Layer Connectivity &

Messaging Service Bus

Secure Machine-to-Machine Network

ConnectivityWindows Azure Connect

Secure Point-to-Site Network Connectivity

Windows Azure Virtual Network

Page 85: Protips  for Windows Azure  Mobile  Services

Service Bus RelaysCorporate NetworkWindows Azure

Database

Service Bus ApplicationSQLMobile Service Cloud Worker

Corporate NetworkWindows Azure

Mobile Service On-premise AppService Bus

Page 86: Protips  for Windows Azure  Mobile  Services

On-premises

Your datacenter

Individual computers behind corporate firewall

Point-to-Site VPN

Route-based VPN

Windows Azure

Virtual NetworkVPN Gateway

<subnet 1>

<subnet 2>

<subnet 3> DNS

Server

VPN Gateway

Remote devices

Site-to-SiteVPN

Point-to-Site VPNs

Page 87: Protips  for Windows Azure  Mobile  Services

On-premises

Your datacenter

Hardware VPN or Windows RRAS

Windows Azure

Virtual NetworkVPN Gateway

<subnet 1>

<subnet 2>

<subnet 3> DNS

Server

VPN Gateway

Site-to-SiteVPN

Site-to-Site Connectivity• Extend your premises to the cloud securely• On-ramp for migrating services to the cloud• Use your on-prem resources in Azure

(monitoring, AD, …)

Page 88: Protips  for Windows Azure  Mobile  Services

• Hybrid Networking with Windows Azure – http://aka.ms/zumoprem1

• Windows Azure Websites and On-Prem http://aka.ms/zumoprem2

Links for more

Page 89: Protips  for Windows Azure  Mobile  Services

ResourcesGet a Windows Azure Free Trial Accounthttp://www.windowsazure.com

Videos, Tutorials and morehttp://www.windowsazure.com/mobile

Mobile Services Resourceshttp://aka.ms/CommonWAMS

Contact mehttp://chrisrisner.com@chrisrisner

Page 90: Protips  for Windows Azure  Mobile  Services

Mobile Services at BuildMobile Services – Soup to NutsJosh Twist – Thursday 2pm

Protips for Mobile ServicesChris Risner – Thursday 5pm

Cross-Platform w/ Mobile ServicesChris Risner – Thursday 4pm

Connected Win Phone AppsYavor Georgiev – Friday 9am

Going Live and BeyondKirill and Paul – Friday 10:30am

Delivering Push Notifications to MillionsElio Demaggio – Friday 12pm

Who’s that user?Dinesh Kulkarni – Friday 2pm

Developing Windows 8.1 apps using Windows Azure Mobile Services Nick Harris – Friday 2pm

Page 91: Protips  for Windows Azure  Mobile  Services

© 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, Windows Vista and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.