Designing IA for AI - Information Architecture Conference 2024
Dynamics NAV, Windows Azure & Windows Phone 7, Eric Wauters
1. Integrating Windows Phone 7 to
Microsoft Dynamics NAV 2009R2
Eric Wauters
Development Manager
iFacto Business Solutions NV
Waldo’s blog (www.waldo.be)
1
2. Integrating Windows Phone 7 to
Microsoft Dynamics NAV 2009R2
Eric Wauters
Backup for Freddy Kristiansen
2
3. Agenda
• Cloud? What is that all about? We’re all in .. but in what?
• Building a Windows Phone app that connects to NAV using
the cloud.
4. Cloud computing?
Cloud computing is a pay-per-use model for enabling
available, convenient, on-demand network access to a shared
pool of configurable computing resources
(e.g., networks, servers, storage, applications, services) that can
be rapidly provisioned and released with minimal management
effort or service provider interaction.
6. Cloud Computing?
On-demand self-service
Characteristics
Standard network access
Location independent resource pooling
Rapid elasticity
Pay per use
7. Enterprise Cloud Triggers – Drivers
1 Speed to Value
2 Flexibility
3 Cross Organizational Collaboration
4 Cost Reduction
5 CapEx Avoidance
6 Green IT
8. CapEx Avoidance – Traditional IT
Allocated Load Forecast
IT-capacities
“Under-supply“
of capacities
“Waste“ of Fixed cost of IT-
IT CAPACITY
capacities capacities
Barrier for
innovations Actual Load
TIME
9. CapEx Avoidance – Cloud
Load
Allocated IT Forecast
capacities
No “under-supply“
IT CAPACITY
Reduction of Possible
“over-supply“ reduction of IT-
capacities in
case of reduced
Reduction load
of initial
investments
Actual Load
Time
12. Components in play
• NAV
– Service Dispatchers RoleCenter
– Customizations
– Web Services, .net interop
• Proxy
– C# Windows Service
– Connecting to NAV Web Services
– Exposing API on the Service Bus
• Windows Phone 7
– Silverlight Application
• Cloud
– Windows Azure Account
13. Components - flow
Windows Azure
Firewall
Storage
Services
Services
Service Bus
Proxy
NAV
Service
Tier
15. NAV customizations
• Service Order card
– On Creating/Modifying Service Orders
• Send notifications (.net interop)
– Related Information – Images
• Download images from Azure storage
• Service Order Image table
• Exposes Web Services
– For the Proxy
• Customer Card Page
• Service Order Card Page NAV
Service
• Special build Codeunit Tier
16. Windows Phone 7 Application
• Connecting to NAV Web Services
– Via the Service Bus
• Get Service Orders
• Accept Service Order
• Change status on Service Order
• Attach images and notes to service orders
• Register notification channel (plumbing)
• Connecting to Azure Storage
– To storage images
17. Windows Phone 7
• Free Developer tools
– http://create.msdn.com/en-US/
– .net, C#, Silverlight
– Microsoft
• A TON of stuff on how to develop applications for Windows
Phone already out there
19. Https Web Services Proxy
• Exposes endpoint on the Service Bus
• Connects to NAV Web Services
• Dedicated for Service Orders scenario
• Only expose necessary API
– Isolating NAV from attacks
• Removes complexity
• Reduces roundtrips
• Loose coupling (device / NAV)
– Possible to use other devices
20. Windows Azure Account
• Windows Azure AppFabric (Service Bus)
– For communication between phone and on-premise NAV
• IssuerName, IssuerSecret
• Windows Azure Storage
– For image storing / retrieving
• AccountName, AccessKey
25. Register Notification Channel
RegisterWp7ChannelUri(Application :
Text[40];ChannelUri : Text[250])
• Phone
public void RegisterWp7ChannelUri(string username, string password, string channelUri
Wp7Channel.SETRANGE(UserID, USERID);
{ Wp7Channel.SETRANGE(Application, Application);
– CreatesWp7Channel.FINDFIRST THEN
var servicea= new ServiceOrderRef.ServiceOrder(); Phone API
IF NotificationChannel using Windows
Authenticate(service, username, password);
BEGIN
– Invokes RegisterWp7ChannelUri in Proxy through Web Services
service.RegisterWp7ChannelUri("ServiceOrders", channelUri);
Wp7Channel.ChannelUri := ChannelUri;
} Wp7Channel.MODIFY();
• Proxy
... END ELSE
BEGIN
HttpNotificationChannel.Find("waldo.ServiceOrders");
CLEAR(Wp7Channel);
– Authenticates null) user
if (myChannel == phone
Wp7Channel.INIT();
{
Wp7Channel.UserID := USERID;
– myChannelRegisterWp7ChannelUri in Codeunit through Web Services
Invokes = new HttpNotificationChannel("waldo.ServiceOrders");
Wp7Channel.Application := Application;
}
Wp7Channel.ChannelUri := ChannelUri;
• NAV
SendURIToService(myChannel.ChannelUri);
... Wp7Channel.INSERT();
END;
– Update/Insert record in Wp7Channel table
private void SendURIToService(Uri uri)
{
var client = App.GetServiceOrderProxyClient();
client.RegisterWp7ChannelUriAsync(App.Settings.UserName, App.Settings.Password, uri
}
27. Send Toast Notification
• DoSendToastNotification(Wp7Channel
NAV : Record Wp7Channel;Priority : Integer;Text1 :
Text[40];Text2 : Text[40])
IF – Loop through Wp7Channel table
Wp7Channel.NextAttempt > CURRENTDATETIME THEN
EXIT;
– Using a WP7Helper class to send the notification (.Net Interop)
status :=
Wp7Helper.SendToastNotification(Wp7Channel.ChannelUri, Priority, Text1, Text2);
• Create XML Document
//MESSAGE(FORMAT(status));
CASE status OF
• Error handling
1: BEGIN // Invalid Uri
Wp7Channel.DELETE();
• Send request using HttpWebRequest / HttpWebResponse
END;
2: BEGIN // Queue Full - Wait one hour before reattempting this Uri
Wp7Channel.NextAttempt := CURRENTDATETIME + 3600000;
BroadcastToastNotification(Application : Text[40];Header : Text[40];Text :
Wp7Channel.MODIFY;
Text[40])
END;
Wp7Channel.SETRANGE(Wp7Channel.Application, Application);
3: BEGIN // Service Unavailable - Wait one hour before reattempting this Uri
IF Wp7Channel.FINDSET THEN BEGIN
Wp7Channel.NextAttempt := CURRENTDATETIME + 3600000;
REPEAT
Wp7Channel.MODIFY;
Wp7Notifications.DoSendToastNotification(Wp7Channel.ChannelUri,2,Header,Text);
END;
UNTIL Wp7Channel.NEXT = 0;
END;
END;
31. Accept Service Order
public bool AcceptServiceOrder(string username, string
• Phone
password, string no)
{
var service = new ServiceOrderRef.ServiceOrder();
– Ask user if he wants to accept the
void client_AcceptServiceOrderCompleted(objectService Order
Authenticate(service, username, password);
sender, ServiceOrderProxy.AcceptServiceOrderCompletedEventArgs e)
{ – serviceOrderService = new
var Invoke AcceptServiceOrder in Proxy through Web Services
Deployment.Current.Dispatcher.BeginInvoke(() =>
ServiceOrderCardRef.ServiceOrderCard_Service();
• Proxy
{
void acceptAction_Click(object sender, EventArgs e)password);
Authenticate(serviceOrderService, username,
if (e.Result)
{
– serviceOrder = you want to accept this Service Order?", "Accept",
if{(MessageBox.Show("Do serviceOrderService.Read(no); through Web Services
var Read the Service Order from the Card Page
MessageBoxButton.OKCancel) == MessageBoxResult.OK) you!");
MessageBox.Show("ServiceOrder is now assigned to
if (!string.IsNullOrEmpty(serviceOrder.Assigned_User_ID))
{ this.AssignedToMe = true;
}
–client =false; Order is assigned already – return false
return Service
If the App.GetServiceOrderProxyClient();
varserviceOrder.Assigned_User_ID = service.GetMyUserID();
else
client.AcceptServiceOrderCompleted +=
–
try Set the Assigned_User_ID
MessageBox.Show("ServiceOrder was already assigned to someone else");
new
{
App.ViewModel.LoadData();
EventHandler<ServiceOrderProxy.AcceptServiceOrderCompletedEventArgs>(client_AcceptServi
– serviceOrderService.Update(ref serviceOrder);
ceOrderCompleted); Service Order again – if error – return false
Write the
UpdateApplicationBarButtons();
return true;
}); }
} • Phone
client.AcceptServiceOrderAsync(App.Settings.UserName, App.Settings.Password, this.Servi
catch
ceOrder.No);
{
} – return false;
Display message and refresh data
} }
}
33. Change Service Order Status
public void ChangeServiceOrderStatus(string username, string password, string
• Phone
no, string statusStr)
{
var– Change status on = new ServiceOrderCardRef.ServiceOrderCard_Service();
serviceOrderService Service Order internally
Authenticate(serviceOrderService, username, password);
– Invoke ChangeServiceOrderStatus in Proxy through Web Services
var status = (Status)Enum.Parse(typeof(Status), statusStr.Replace('
• Proxy
', '_'),void ChangeServiceOrderStatus(ServiceOrderProxy.ServiceOrder
private true);
serviceOrder, string serviceOrderService.Read(no);
var serviceOrder = status)
{ serviceOrder.Status = status;
– Read Service Order from the Card Page through Web Services
serviceOrderService.Update(ref
serviceOrder.Status = status; serviceOrder);
} var client = App.GetServiceOrderProxyClient();
– Set the Status
client.ChangeServiceOrderStatusAsync(App.Settings.UserName, App.Settings.Password,
– Update Starting Date/Time or Finishing Date/Time if necessary
serviceOrder.No, status);
}
– Write the Service Order again
34. Capture and Upload Image
Firewall
Storage
Service Bus
Proxy
NAV
Service
Tier
35. Capture and upload Image
void cameraTask_Completed(object sender, PhotoResult e)
...
• Delay’s Blog
{CameraCaptureTask cameraTask;
... (e.TaskResult == TaskResult.OK)
if
{ – http://blogs.msdn.com/b/delay/
cameraTask = new CameraCaptureTask();
void// Using WriteableBitmap's SaveJpeg to resize
PutBlobAction(Stream s, byte[] photo)
cameraTask.Completed += new EventHandler<PhotoResult>(cameraTask_Completed);
– Blob API for Windows Phone
{...var bitmapImage = new BitmapImage();7
bitmapImage.SetSource(e.ChosenPhoto);
s.Write(photo, 0, photo.Length);
} • Ms-PL (http://opensource.org/licenses/ms-pl.html)
voidvar bitmap = new WriteableBitmap(bitmapImage);
cameraAction_Click(object sender, EventArgs e)
{ var ms = new MemoryStream();
{
}
• Phone
voidbitmap.SaveJpeg(ms, 1024,camera0, 90); the picture
cameraTask.Show(); //shows 768, to take
PutBlobCompleted()
var photo = ms.ToArray();
Deployment.Current.Dispatcher.BeginInvoke(() =>
// – Capture Image
Upload photo
AttachServiceOrderImage(this.ServiceOrder, blobInfo.Name));
} blobClient = new
– Resize image
AzureBlobStoreClient(AzureStorageAccountName, AzureStoragePrimaryAccessKey, "image
s");
private void PutBlobFailed(Exception ex)
{ • to save time when uploading
blobInfo = new BlobInfo(Guid.NewGuid().ToString());
Deployment.Current.Dispatcher.BeginInvoke(() (s)MessageBox.Show("No photo),
blobClient.PutBlob(blobInfo, photo.Length, => => PutBlobAction(s,
– Upload
connectivity.”)); image (ex) => PutBlobFailed(ex));
PutBlobCompleted, to Azure Storage
} }
}
36. Attach Image / Note
Firewall
Storage
Service Bus
Proxy
NAV
Service
Tier
37. Attach Image / Note
AttachServiceOrderImage(No : Code[20];Image : Text[40])
ServiceOrderImage.INIT;
• Phone
ServiceOrderImage."Document No." := No;
ServiceOrderImage."Image ID" := Image;
ServiceOrderImage.INSERT(TRUE);
– Invoke AttachServiceOrderImage/Note in Proxy through Web Services
with Uri to Azure Storage blob or note
AttachServiceOrderNote(No : Code[20];Note : Text[250])
ServiceHeader.GET(ServiceHeader."Document Type"::Order, No);
– Display message box when
RecRef.GETTABLE(ServiceHeader); image uploaded
RecordLink.INIT();
• Proxy
RecordLink.Type := RecordLink.Type::Note;
privatevoid AttachServiceOrderImage(string username, string password, string
public void AttachServiceOrderImage(ServiceOrderProxy.ServiceOrder
Save_Click(object sender, EventArgs e)
RecordLink.Created := CURRENTDATETIME;
serviceOrder, string ID" := USERID;
{ RecordLink."User blobName)
no, string blobName)
{ var – InvokeApp.GetServiceOrderProxyClient(); in NAV Codeunit through Web
client = AttachServiceOrderImage/Note
RecordLink.URL1 := 'dynamicsnav://freddyk-
var client ==App.GetServiceOrderProxyClient();
service new ServiceOrderRef.ServiceOrder();
Services with Uri or notepassword);
appfabr:7047/DynamicsNAV/'+COMPANYNAME+'/runpage?page=5900&pers
client.AttachServiceOrderImageCompleted +=
client.AttachServiceOrderNoteAsync(App.Settings.UserName, App.Settings.Password, ID,
Authenticate(service, username,
onalization=5900&'+
new
this.textBox1.Text);
service.AttachServiceOrderImage(no, blobName);
• NAV
EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(client_AttachServiceOrder
} NavigationService.GoBack();
'bookmark='+FORMAT(RecRef.RECORDID,0,10)+'&mode=edit';
ImageCompleted);
} RecordLink.Description := 'Service Order - '+No;
– Insert Image := in ServiceOrderImage Table or password, string
public void AttachServiceOrderNote(string username, string add note to RecordLink
RecordLink.Company Uri COMPANYNAME;
client.AttachServiceOrderImageAsync(App.Settings.UserName, App.Settings.Password, ser
no, RecordLink."Record ID" := RecRef.RECORDID;
string note)
table
viceOrder.No, blobName);
{ CLEAR(RecordLink.Note);
} var service '+Note;
Note := ' = new ServiceOrderRef.ServiceOrder();
Authenticate(service, username, password);
Note[1] := STRLEN(Note)-1;
void client_AttachServiceOrderImageCompleted(object
service.AttachServiceOrderNote(no, note);
NoteText.ADDTEXT(Note);
sender, System.ComponentModel.AsyncCompletedEventArgs e)
} RecordLink.Note.CREATEOUTSTREAM(NoteStream);
{ NoteText.WRITE(NoteStream);
39. Download and display image
public class AzureStorage
{• NAV
string AccountName;
– Create .net
string AccessKey; class AzureStorage
• Using AccountName and AccessKey
public AzureStorage(string AccountName, string AccessKey)
{
• Wrapper AccountName;
this.AccountName =for AzureBlobStoreClient (Delay’s Blog)
this.AccessKey = AccessKey;
– Download OnAction()
<Action1170000001> - all blobs for a specific Service Order to the Service Tier
}
AzureStorage :=
– Use GetBlob(string to show images name, Client
AzureStorage.AzureStorage(AzureStorageAccountName, AzureStorageAccessKey);
public void File.Download container, string on the string filename)
ServiceOrderImage.SETRANGE(ServiceOrderImage."Document No.", "No.");
{
IF NOT ServiceOrderImage.FIND('-') THEN
EnsureContainerIsCreated(container);
BEGIN
var blobClient = new AzureBlobStoreClient(AccountName, AccessKey, container);
MESSAGE('No Images attached');
var blobInfo = new BlobInfo(name);
END blobClient.GetBlob(blobInfo, (s) => GetBlobAction(s, filename));
ELSE
REPEAT
}
AzureStorage.GetBlob('images', ServiceOrderImage."Image
ID", TEMPORARYPATH+ServiceOrderImage."Image ID"+'.jpg');
...
toFile := ServiceOrderImage."Image ID"+'.jpg';
FILE.DOWNLOAD(TEMPORARYPATH+ServiceOrderImage."Image ID"+'.jpg', 'Service Order
Image','', '', toFile);
UNTIL ServiceOrderImage.NEXT = 0;
40.
41. More info
• Will soon be published on:
– http://blogs.msdn.com/b/freddyk
• Related info:
– Vjeko’s blog (navigateintosuccess.com)
– Waldo’s blog (www.waldo.be)
– Mibuso
– Dynamicsuser.net
– ...
44
Key Characteristics:On-demand self-service. A consumer can unilaterally provision computing capabilities, such as server time and network storage, as needed without requiring human interaction with each service’s provider. Standard network access. Capabilities are available over the network and accessed through standard mechanisms that promote use by heterogeneous thin or thick client platforms (e.g., mobile phones, laptops, and PDAs).Location independent resource pooling. The provider’s computing resources are pooled to serve all consumers using a multi-tenant model, with different physical and virtual resources dynamically assigned and reassigned according to consumer demand. The customer generally has no control or knowledge over the exact location of the provided resources. Examples of resources include storage, processing, memory, network bandwidth, and virtual machines.Rapid elasticity. Capabilities can be rapidly and elastically provisioned to quickly scale up and rapidly released to quickly scale down. To the consumer, the capabilities available for rent often appear to be infinite and can be purchased in any quantity at any time.Pay per use.Capabilities are charged using a metered, fee-for-service, or advertising based billing model to promote optimization of resource use. Examples are measuring the storage, bandwidth, and computing resources consumed and charging for the number of active user accounts per month. Clouds within an organization accrue cost between business units and may or may not use actual currency.
1. Speed to ValueProbably the most important advantage of Cloud Computing is its ability to deploy live applications rapidly without the traditional procurement and deploy-ment cycles required to get an application into production. In pilots for our clients, we have reduced deployment times by up to seventy percent.2. FlexibilityAzure has a massive capacity, allowing for easy flex up (and down) of application workload. This flexibility opens the door to solutions not previously possible: we now can develop a hybrid approach in which you maintain fixed compute capacity in Capgemini or your own data centers while using the cloud to cope with spikes.3. Cost ReductionThe shared use of Cloud Computing infrastructure yields unmatched economies of scale in data center delivery. Speed of deployment combined with a pay-as-you-go approach means much lower costs compared to traditional models.4. CapEx AvoidanceWith a pay-per-use pricing model free of upfront investment in servers or other hardware and software, this service allows IT spending to be shifted from capital to operational expenditure and reduces barriers to adoption. Barriers to exit are also eliminated as it is possible to switch off projects and applications at any time. With up to 80% of enterprise IT budgets spent on maintenance, you can free up funding for other areas.5. Cross organizational collaboration6. Greener ITThe compute density, optimization, and capacity utilization of the cloud is much higher than that of traditional data centers. Cloud Computing represents a way today to provide compute capacity to enterprises concerned about energy efficiency and reducing carbon emissions.
Windows Phone app: Settings.cs https://ifactoclouddemo.servicebus.windows.net/https/ServiceOrderProxy/
Please do not delete thisslide – On Screen whenpresentationended.