Wednesday, November 30, 2011

10-Minute App - Windows Phone 7 App

This is the third and final article in a 3-post about the Windows Phone 7 app for AX 2012. Before we continue on I think it's import to re-iterate that this is a proof-of-concept and you should not take this code as a basis to make a real world app. The goal is to show the potential and relative ease of making AX 2012 work with the rest of the Microsoft technology stack.
That being said, if you missed the first two articles:

- Part 1 - Setting up IIS
- Part 2 - Creating the intermediate WCF service for Windows Phone 7

In this final article, we'll build the actual Windows Phone 7. You need to have the setup and code in place of the first two articles. Before we get started, make sure you have the WCF service that we created in Part 2 running (press F5 in Visual Studio), and make sure you have the Windows Phone 7 SDK installed.

So, with the WCF service running, open a SECOND instance of Visual Studio 2010, this one does not need to run as administrator. Create a new project of type "Silverlight for Windows Phone and select the "Windows Phone Databound Application" template. I'm calling this project "DAXMusings_WP7".


You will get a dialog asking what version of the OS you want to use. The current version as of this writing is 7.1, which is Mango. I know it's confusing, I thought Mango was 7.5, but 7.1 is what it is. If you are a time traveler from the future reading this old blog post from November 2011, feel free to use a newer version. We're not using any specific OS features, so as long as .NET still supports WCF when you're reading this, I'm suspecting this code will still work :-)


First thing we need to do is add the service reference to our WCF service. This goes back to article 1 and 2. You NEED to enter the FULL qualified domain name for your server (the server name needs to match the certificate created in part 1), and you need to know the path to your service. If you've followed my lead on naming of the classes and folders etc, then (except for the server name) your URL should be:

https://yourmachinename.domainname/WP7/Service1.svc?WSDL

obviously replacing the "yourmachinename.domainname" with your server's FQDN. To add the reference, right-click the "References" node and select "Add Service Reference". On the dialog, enter the URL to your service's WSDL and click GO. In the namespace, I called the reference "AXItems".


The default layout defined in the XAML file contains a grid with two rows: one for the application and page title, one for the actual content grid. We'll add a third row to go in between, where we can enter a user name and have a button to retrieve the data. In the XAML file, add a third "RowDefinition" in between the two existing ones, with Height set to "Auto", so it looks like this:
Click here for a full code screenshot
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>


Next, under the StackPanel node containing the application title and page title (you can change the TEXT attribute on those TextBlock nodes to change the title that will show in your app... for example, change it to "DAXMusings Rules") and before the Grid called ContentPanel, we will add the following XAML. Since we're adding a Row in between, we need to make sure the Grid.Row property of contentpanel below is changed to 2! (see the full screenshot)
Click here for a full code screenshot
        <StackPanel x:Name="LogonPanel" Grid.Row="1">
            <TextBox x:Name="Logon" Text="claimguy@myapp.com" /%gt;
            <Button x:Name="GetItems" Click="GetItems_Click">
                <TextBlock Text="Get Items" />
            </Button>
        </StackPanel>

The keen eye noticed that the logon TextBox I added contains a username already (claimguy@myapp.com - the user we setup in the trusted intermediary article). Obviously this is not something you want to do in your production app. However, the Windows Phone Emulator (or at least the current version of it) does not support your machine's keyboard, so you actually have to use the on-screen keyboard for testing. So putting it in here saves you some typing. This is also a good moment to remind you this article series depends on the trusted intermediary setup, so you need to go through that for this to work.

If the hard-coded user was caught by your left keen eye, then your right keen eye may have noticed there is an event handler for the "Click" event on the button. If you are typing this in manually, the Visual Studio editor will prompt you to add that event handler method. If you just copy/paste the code from above, you can right-click the GetItems_Click text and select "Navigate to Event Handler" and this will create the method for you.


Before we can actually do something, we'll have to add the code that actually makes the call. There is already some basic code added for that in the template, but we want to change that of course. In your solution explorer, open the "ViewModels" folder and double click the "MainViewModel.cs" file.


The grid on the main page uses the Observable Collection that is defined in this file. In the template, they define a new data structure. We want to actually just use the data structure returned from the WCF service. So we'll need to make some changes to use the AXItems.AxdEntity_InventTable as the class contained in the collection:


You will notice that there is a method in this file called "LoadData", which due to this change will now contain a bunch of error lines. Just remove the error lines or comment them out:


Next, we'll add a method at the end of this MainViewModel class, which will be used as the event handler when the results are received from our WCF app. First we'll make sure there were no errors, if there were we will show that in a popup, and return out of the method. Next, we check to see there were items in the result set, if not we say so and return out of the method. Lastly, we loop over the items that were in fact returned, and add them to our observable collection. The isdataloaded=true is not really necessary, it's a variable maintained by this template but it doesn't really do much anyway.
Click here for a full screenshot
void client_GetItemCompleted(object sender, AXItems.GetItemCompletedEventArgs e)
{
    if (e.Error != null)
    {
        Exception ex = e.Error;
        MessageBox.Show(ex.Message);
        return;
    }
    if (e.Result == null)
    {
        MessageBox.Show("No items found");
        return;
    }

    foreach (AXItems.AxdEntity_InventTable item in e.Result)
    {
        this.Items.Add(item);
    }
    this.IsDataLoaded = true;
}

Finally, we'll add the actual code that makes the call. As mentioned a few times in this article series, the authentication is not what it should be and you should do proper WCF authentication.
Click here for a full screenshot
public void GetItems(string username)
{
    this.Items.Clear();

    try
    {
        AXItems.Service1Client client = new AXItems.Service1Client();
        // Set the client credentials property here for proper authentication
        client.GetItemCompleted += new EventHandler(client_GetItemCompleted);
        client.GetItemAsync(username, "10000", "10005");
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Another thing here is the hard coded item numbers range. We're basically getting items 10000 through 10005. Feel free to add these as parameters on the XAML file as well. However, you need some type of limitation or you will run into the maximum message size of your WCF service. These can be changed easily in the web.config settings of the WCF service if you wish to do so. However, since we're doing a quick prototype here we're using mostly standard settings and a standard service too. Remember this service actually returns ALL item data, not just a few fields.
Ok, so now that we have the WCF call in place, we just need to call this method from our button click event handler. You should still have MainPage.xaml.cs open, so click on that tab.


in the GetItems_Click method, we now call our new method GetItems, which was put in the main view model. We need to pass the username entered on the screen. The textbox we added we gave the x:Name="Logon" name, so we can just call that and retrieve its property "Text" to pass to our method as the username:
private void GetItems_Click(object sender, RoutedEventArgs e)
{
    App.ViewModel.GetItems(Logon.Text);
}

Almost there! The last thing we need to change is back in the MainPage's XAML file:


Now that we have changed the observable collection ("Items" - which is what you see in the XAML the grid's ItemsSource property is bound to), we need to make sure the two TextBlocks in the data bound grid actually bind to properties that exist on the AXItems.AxdEntity_InventTable class. This is the class AIF returns from the service, and the properties on it basically reflect the fields on the InventTable. In fact, it also contains some sub-tables of InventTable, so feel free to experiment with the binding. For now, we'll just bind the two text blocks to item id and name alias ("search name"):
Click here for a full screenshot
<TextBlock Text="{Binding itemIdField}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding nameAliasField}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>


With all this in place, we're ready to run the App!! Now, one thing to remember from Part 1 was the self-signed certificate issue with Windows Phone 7. Remember that to resolve this issue we added a ZIP file called certificate.zip onto the IIS website in the root folder.
Press F5 to run and deploy the app to the phone emulator. Make sure the emulator is selected and it doesn't try to deploy using Zune software (in case you actually have a windows phone or a zune player perhaps).


Ok, so when the emulator opens, it will load all the assemblies and then open your app, which should look like this:


Feel free to click the "Get Items" button, but you will get the "endpoint not listening" error (see Part 2 for explanation). So use your mouse to click on the Windows button. Once in the main menu, open internet explorer.


In internet explorer (after click away the privacy notice from Microsoft), click the URL bar at the bottom and type in the URL of the certificate.zip file. Now, I will save you some typing (clicking - since the windows keyboard doesn't seem to be supported) here. For development purposes, the windows phone emulator re-routes "localhost" to your computer running visual studio, rather than localhost being the windows phone device itself. So, to get to the certificate, if you followed the naming of my articles, the URL should be https://localhost/WP7/certificate.zip (don't forget the "S" at the end of https). Click the white button with the arrow on it. You will get a warning from IE and just click continue.


When I do this the first time, I get an error "can't download the file". If someone can explain why this is, feel free to leave a comment. In any case, if you click the URL bar at the bottom again, it will pop up with the URL still in it, and if you hit the white arrow again, it will actually work... (not sure what this is all about)


This will open the ZIP file on screen, and will show you the certificate.CER file contained within. Click the CER file, and you will be prompted with the details. Click the "Install" button at the bottom. This will confirm it's installed, just click OK.


Once this is done, click and HOLD the back button at the bottom. This will bring up the task switcher. Click on your APP (you can swipe the screen to get to it) and this will switch back to your app.


Back in your app, you can now click the "Get Items" button, and after a few seconds it should display your item numbers 10000 through 10005 (assuming you have those, remember we hardcoded the range). If your app doesn't react to clicks on the username field or the item button, make sure you have set the Grid.Row property of the Grid/ContentPanel to "2". Else you have two overlapping controls and it gets stuck basically (guess how I know this).


A word of advice. If you close the emulator screen, you will LOSE the certificate installation and have to repeat the process every time. So rather than closing it, go to Visual Studio and STOP debugging to close the app. Next time you wish to run your app, just hit F5 again, and the emulator will just pop back up. But if you close it, you'll have to reinstall the certificate.


So (63 screenshots and some sleep deprivation later) that concludes our Windows Phone 7 app. Your mileage may vary on the development being 10 minutes, but at least the intent was there. And the windows phone app itself was probably about 10 minutes, no? :-) The setup and potential pitfalls is what makes this tough.

Hope you enjoyed this one, I know I did (once it was working!). Please drop me a line on my blog and let me know if you have this running, if you have feedback of any kind.

Monday, November 28, 2011

10-Minute App - WCF service for Windows Phone 7

This is Part 2 in the Windows Phone 7 app. Due to length of the articles, the "10 minutes" is split up in three articles. Part 1 talks about setting up IIS to support the intermediary WCF service.


After an explanation on the trusted intermediary in AIF, and the IIS setup needed to be able to run our WCF service for use with Windows Phone 7 (WP7), we're ready to code! We will create two Visual Studio projects, one is the WCF service that consumes the original AIF service and exposes it back out again (translating the authentication in the process). The second project is the actual WP7 app consuming our WCF service to display the AX data.

Part of the trusted intermediary article was setting up the AIF service enhanced port for item data and allowing a trusted intermediary for that web service. We will re-use that in this article, so if you haven't done so already, please go through the article for the setup.
Also a must is the setup necessary for IIS. You need to go through this setup first, before we can get into the code below.

First, the WCF application. To work with the IIS setup, you will need to start Visual Studio 2010 in administrator mode. To do this, right-click Visual Studio 2010 in your start menu, and select "Run as administrator". Once in Visual Studio 2010, create a new WCF project of type "WCF Service Application". I called my project "DAXMusings_WCF".

First thing is changing the setup to use our IIS setup instead of the built-in development server of Visual Studio. On the Project menu, select "DAXMusings_WCF Properties..." (if you named your project differently, you should see that here). In the properties window, select the Web tab.

Select the "Use Local IIS Web server", and enter the URL for the website we created, including a new sub-folder (I changed the default, which is the project name, to something short: WP7). Don't forget to use the FQDN (fully qualified domain name) that IIS used for the certificate! Also make sure you enter httpS to indicate it's an SSL site. If you setup the website on another port, make sure to add the port (for example if you used port 80443: https://myserver:80443/WP7).

Once entered, click the "Create Virtual Directory". This will create a new virtual sub-folder in the site, pointing to the output folder of your Visual Studio project. Unfortunately, IIS assigns the default application pool to it, so we'll need to go back to IIS and change the application pool. Under administrative tools, open "Internet Information Services (IIS) Manager". Under sites, browse to the site we created (if you followed to the letter, this will be called "DAXMusings"), and click on the new virtual directory called "WP7". On the right-hand side panel, select "Advanced Settings".

In the application pool setting, click the ellipsis and select your application pool and click OK. That's it.

Back in Visual Studio, we'll add a reference to the AX 2012 AIF service we've setup in the trusted intermediary article article. Check back there what to setup and how to find the WSDL URL to use.
To add the service reference, right-click on References in your project and click "Add Service Reference".

On the dialog, type in or paste in the address for the item service WSDL and click the "Go" button. In the namespace field, type "ItemService". Click OK to add the service.

Now, there is a catch which took me a long time to figure out. By default, the proxies are generated for the item service from AX. My idea was to just re-use those objects for this WCF service. They are already a data contract, plus the service from AX will return the object, so you can just pass it along. Well, until the WP7 app tries to consume it. You will get the following error:
Inner Exception: Type 'System.ComponentModel.PropertyChangedEventHandler' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.
Apparently this is a SilverLight issues, but it took me a while to figure out how to circumvent it. Turns out it's all in the proxies generated by the service reference we just added. By default, the reference tries to be helpful and adds events for property changes, so the proxy objects return from the service can be used for data binding and other cool stuff. But that is exactly what we cannot pass along to a silverlight service without making it part of the data contract. Since the data contract is auto-generated, we're stuck. I finally found a helpful blog post explaining how to turn off the automatic addition of the events. To do this, right-click your project and select "Open Folder in Windows Explorer". In the folder, you will find the "Service References" folder, inside of it, the ItemService folder.

In this folder, we need to edit a file called "Reference.svcmap". You can edit this file in notepad or even in Visual Studio, it's an XML file. In the XML file, the second field is called "EnableDataBinding" and is set to true. Setting this to false will avoid the service proxies having the notification properties.

Save the file and close it. Back in Visual Studio, right-click the service reference "ItemService" and select "Update Service Reference". This will regenerate the proxies and effectively remove the events.

Ok, so now to the code. We need to change the service interface (IService1), and then the implementation itself as well. Open the IService1.cs file by double clicking on it in the project.

To cut things short, all we want to do is add a method to the interface (feel free to clean up the rest, we won't use it). We'll return an array of InventTable records coming from the AIF service, so add the following line under the "TODO: add your service operations here" comment:
Click here for full code screenshot
[OperationContract]
ItemService.AxdEntity_InventTable[] GetItem(string username, string itemIdFrom, string itemIdTo);

As you can see, we're cheating on the authentication here by just passing in the username as a parameter to the operation. No password here either. Please look into proper authentication mechanisms for WCF (I know I am). As I said in the previous article, a good place to start is this article on MSDN.
As far as the operation, we'll use the "Find" method of the AX service which can use a range of filters, so we'll do a from and to value for the find range.
Next in the Service1.svc file, we want to implement this new operation inside the Service1 class, as follows:
Click here for full code screenshot
public ItemService.AxdEntity_InventTable[] GetItem(string username, string itemIdFrom, string itemIdTo)
{
 ItemService.AxdItem item = null;

 ItemService.ItemServiceClient client = new ItemService.ItemServiceClient();
 ItemService.CallContext context = new ItemService.CallContext();

 context.Company = "CEU";
 context.MessageId = Guid.NewGuid().ToString();
 context.LogonAsUser = String.Format("DAXMusingsAuth\\{0}", username);

 try
 {
  ItemService.QueryCriteria criteria = new ItemService.QueryCriteria();
  criteria.CriteriaElement = new ItemService.CriteriaElement[1];
  criteria.CriteriaElement[0] = new ItemService.CriteriaElement();
  criteria.CriteriaElement[0].DataSourceName = "InventTable";
  criteria.CriteriaElement[0].FieldName = "ItemId";
  criteria.CriteriaElement[0].Operator = ItemService.Operator.Range;
  criteria.CriteriaElement[0].Value1 = itemIdFrom;
  criteria.CriteriaElement[0].Value2 = itemIdTo;
  item = client.find(context, criteria);
 }
 catch (Exception ex)
 {
  Console.WriteLine(ex.Message);
  throw ex;
 }

 return item.InventTable;
}

As you can see this uses the context for the intermediary user (read the article if you haven't already!). We re-throw any errors coming back from AIF, otherwise we return the InventTable datasource coming from AIF... Ok, last thing to do is actually tell WCF we want to use https and basic authentication. To do that we need to change the Web.config file (double-click on the Web.config in your project). First, we need to add a section for basic http binding. Inside the "bindings" node, after the </netTcpBinding> node, add the following:
<basicHttpBinding>
        <binding name="webBinding">
          <security mode="Transport" />
        </binding>
      </basicHttpBinding>

Next, under behaviors/servicebehaviors we'll add the following node after the </behavior> :
<behavior name="ServiceBehaviors">
          <serviceDiscovery/>
          <serviceMetadata httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>

Next, we add a services node with a service setup, linking to the basic http binding, the service behavior, and the data contract. If you used any other names for the service reference namespace make sure to change the service class name and the data contract interface name (shown in bold) below:
<services>
      <service name="DAXMusings_WCF.Service1" behaviorConfiguration="ServiceBehaviors">
        <endpoint name="Endpoint"
                  address=""
                  binding="basicHttpBinding"
                  contract="DAXMusings_WCF.IService1"
                  bindingConfiguration="webBinding"/>
        <endpoint kind="udpDiscoveryEndpoint" />
      </service>
    </services>

Lastly, at the top of the web.config, we need to remove the targetFramework attribute in the compilation node, so it looks like this:

I know this was a lot, so feel free to look at the actual web.config file as I'm using it. It's up for download here.
We're done with the WCF service. You can hit F5 to run the service. Nothing much to see, you can browse to your https site under /WP7/Service1.svc?WSDL to see if it's working. We'll need this URL later for the WP7 app itself.

On to the WP7 app!

10-Minute App - IIS Setup for Windows Phone 7

The final 10-minute app, this time for Windows Phone 7! I will be using a trusted intermediary, so if you haven't read that article yet, head on over here. You'll also need the Windows Phone 7 SDK installed. As with the Windows Azure app, there were some hurdles in this setup, mainly all the setup around IIS. Therefor the article is split up in two, first is this setup article, next will be the actual code.
If you missed the previous 10-minute articles, visit my blog for the Windows Presentation Foundation and the Windows Azure articles.

As I explained in the Trusted Intermediary article, mobile applications usually do not have access or support for active directory authentication. Which is exactly why we need an intermediary. For this article, we will need to create two visual studio projects:
1) A WCF service that consumes the actual AIF service using a trusted intermediary account, and exposes the AX AIF service out again with authentication of the consumer
2) A Windows Phone 7 (WP7) app that will consume our WCF service and display the results of the AIF service on the phone

The intermediary WCF service will need to authenticate the user. Unfortunately I'm still trying to figure out WCF authentication myself, and it would probably go beyond the "10 minutes" I'm aiming for anyway. A good start if you want to take this further would be MSDN. This blog article will give you all the basics, you just need to implement the authentication side properly.
That being said, for basic authentication to work, Windows Phone 7 requires you to use SSL. It will effectively ignore any credentials you try to pass if you try to use a basic http site. This is a security decision and makes sense, you don't want to be sending usernames and passwords in plain text around the internet. Unfortunately the Visual Studio built-in development web server ("Cassini") does not support SSL. So we'll have to change our Visual Studio solution to use IIS instead. No big deal, but a dent in my 10 minutes to go over this setup with you :-)

So first things first, which is setting up IIS. Our WCF service will run as whatever identity is used in the application pool for our website in IIS. Since our WCF service will call AX as a trusted intermediary, that means the application pool needs to run as the trusted intermediary user. Under administrative tools, open the Internet Information Services (IIS) Manager. Right-click on Application Pools and select "Add Application Pool".


Name the application pool (I chose DAXMusings) and make sure to set the .NET Framework version to 4.0 and the pipeline mode to classic.


Next, right-click on your new application pool and select Advanced Settings.


Under Process Model, click the ellipsis button on the Identity property. Select the "Custom account" and click the Set button. Set the username and password for your trusted intermediary in AX (make sure to prefix the username with the domain name and a backslash). Remember in a production environment things are different and security is top priority, and definitely don't use your personal account for the application pool.


Click OK until you get back to the IIS Manager. Next we'll need to create a certificate for our SSL site. Unless you want to shell out the big bucks for an actual certificate for your development server or laptop or whatever, we will just create a self-signed certificate.
Click on the root level (the name of your server), and in the Features View, double click on the "Server Certificates" icon.


In the Server Certificate pane, click the "Create Self-Signed Certificate..." link in the right-hand side menu. In the dialog that pops up, enter a "friendly name" for your certificate. I just entered the name of my laptop (everything runs locally on my laptop). You should see the certificate show up with an expiration date. Note that under "Issued To", IIS will have listed the FQDN (fully qualified domain name) of your server. You will need to use this later in the code, so make sure you know what the FULL name of your server is.


Double-click on your new certificate, or click the "View…" link on the right-hand side. On the certificate view dialog, click on the details tab. In the list, try to find the "Public key" row. When you click on it, the bottom part will show the key, and you can click the "Copy to file..." button.


On the first wizard page, just click next. Select the default option to NOT export the private key. Click next. Select the default option "DER encoded binary", click next. Select a location to save the .CER file, and click next. On the last page, just click the Finish button to save the file. This will save the public key to a file.


The reason we need this is a Windows Phone 7 "issue". For security purposes, Windows Phone 7 apps can only accept certificates signed by a trusted authority. We will need to install the certificate on your windows phone device emulator for the phone to accept the certificate at all. Without it, you will get the very detailed and inspiring error message:
System.ServiceModel.EndpointNotFoundException : There was no endpoint listening at that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
There is an open-source tool to install certificates on a WP7 device, but I found the easiest way to be providing the certificate file as a download on your website, so you can just download and install it on the WP7's internet explorer. As an FYI, I finally found what this error meant thanks to this blog post.

Next in our IIS setup, right-click on Sites and select "Add Web Site". On the dialog, give the site a name (internal name so not an important choice), and under the "Select" button select the application pool we created previously. Under Physical Path, select the ellipsis lookup button and create a new folder. The default path for IIS sites is on C:\InetPub, so I just created a new folder "DAXMusings" underneath. If you already have a website on the default port (443), you will need to change the port. Just make sure you remember that port number, we'll need it in code later. Under "SSL Certificate", select the certificate (friendly name) we created in the previous step. Click OK.


Finally in this setup, we need to put the CER file in a zip file to be able to install it on the WP7 device. Locate the .CER file we created from the server certificate, and right-click it, select "Send To", and select "Compressed (zipped) folder". If you have any other means of creating a zip file, feel free to use that instead. Move the new ZIP file (which I called "certificate.zip") to the new folder you created called "DAXMusings".


I think we're finally ready to get coding!