This is a reposting of an article Chris Rudolph originally wrote on my Developing for Dynamics GP blog.
Hello, my name is Chris Rudolph. I have been working as a software engineer on the Dynamics GP team here in Fargo since the summer of 2008. I like to spend my time hanging out with my wife (we just started disc golf!) and playing the cello.
So far at Microsoft I have worked on a variety of projects including the WCF conversion and install for GP Web Services 2010, the Workflow install, SRS reports, Analysis Cubes for AA, and now the web client. My web client work has been primarily in the depths of the Dexterity runtime and the ‘interop’ layer it uses to communicate with the .NET world.
A previous installment of ‘Dynamics GP Developer Insights’ highlighted the implementation of one of our Silverlight controls. Today we are shifting our focus away from the UI and to the plumbing used to handle data transmission between the client and server. The red boxed section in the graphic below indicates where I will be focusing my attention.
Rather than discussing abstract concepts, I’d like to walk through an example using one of the most familiar windows in the product – the login window.
The first message sent from the client is a ‘Connect’ message, which causes dictionaries to be loaded and the login form to be opened. During the startup sequence, the code executing is nearly identical to that which executes today. The primary difference is that the web client’s version of Dexterity has been instrumented to raise callbacks at notable times during execution so that external consumers can react to changes occurring in the runtime. Implementations for each exposed callback are written and registered by the managed Dexterity wrapper, allowing changes from deep within Dexterity to be surfaced to the client over a WCF service. Let’s take a look at the first notable event.
As the login form opens, a callback is executed indicating that a form is being opened. The callback contains data used to identify the form, as well as the action that is being taken (in this case, the form action is OpenStarting).
When invoked, the managed handler for this action calls back into Dexterity and retrieves metadata about the form using the new Dexterity Object Model (DexOM). After the raw data is retrieved from the DexOM, a series of type converters run. These have the responsibility of populating fields in the GUI Independent Window Object Model (GIWOM) objects of the form, each of which represents a single window. At this point we have a .NET representation of the window that matches the dictionary definition exactly. However, the dictionary definition is insufficient because it does not account for changes that happen when scripts execute. The graphic below illustrates this by showing that the window’s ‘Sources’ drop-down list contains no entries.
So how does the GIWOM find out about ‘dynamic’ changes to its properties? Through more callbacks of course! Pertinent to our discussion is the filling of the drop-down list which occurs in the Login_WIN_PRE script. Dynamic changes to metadata are handled in fundamentally the same way as the original retrieval of metadata from the dictionary, the key difference being where the source data originates. In our example a script is responsible for adding two items to the list. As the sanScript instructions are processed, Dexterity invokes the following callbacks.
The changes are applied to the GIWOM so that its contents are kept synchronized with Dexterity. In the interest of simplicity we have only looked at the example of adding items to a list control, but callbacks exist to handle every meaningful change that the client needs to know about. For example, we have callbacks for changing colors, fonts, boolean properties, sizes, positions, and more. Here is the GIWOM after the recent callbacks have been applied.
As soon as the form load finishes (remember, this is all happening as Dexterity opens the form and runs the appropriate PRE scripts), the collection of callback data is serialized and sent to the client as the response to its ‘Connect’ message. The client then proceeds to process the data and performs the actions required to draw the Silverlight UI.
Our discussion would not be complete without a brief foray into the world of ‘actions’. The discussion above centered around data originating on the server and being moved to the client for display there. However, there also needs to be a mechanism for transferring data in the other direction and updating the relevant state within Dexterity. We will take a look at how this happens in the next phase of our example. The interaction scenario I’ll be focusing on is one where I enter my credentials then click the ‘OK’ button on the open login window. My actions cause the following messages to be queued and sent across to the server:
The client has some knowledge about which fields contain scripts, which allows for intelligent decision making about trips back to the server. In this example, I am assuming the only field with a script is the ‘OK Button’. As a result, actions are able to be batched up and sent only when server code needs to run.
When the server receives the payload, it converts each message into the relevant macro system action. Our ‘Focus’ messages each become ‘MoveTo’ instructions, our ‘ValueChange’ messages become ‘TypeTo’ instructions, and our ‘Click’ message is translated into a ‘ClickHit’. As these instructions are played back, they potentially trigger scripts, which cause more callbacks, which surface new data for the client, and so on. Upon completion of the playback, the call returns to the client with whatever data has been collected (exactly like what happened in the initial ‘Connect’ message example, only with different data this time). Here is the GIWOM after it is updated with the credentials I sent back to the server.
This describes the client-server data flow at a very high level. Some simplifications have been made to avoid getting lost in unnecessary details, but the concepts are accurately represented.
For other posts in this series go to:
This article was originally posted on the Developing for Dynamics GP Blog and has been reposted on http://www.winthropdc.com/blog.