Wednesday, April 29, 2009
Developing systems is a creative process. Sometimes we build things, recognize that there is a better way and then go back and fix or "refactor" our code.
After working with the same type of architecture as Silver Sample, on a production system, I realized that I had made a mistake in an earlier post. I have since found a better way to use the proxy that is setup when we create a service reference from the Silverlight client.
Originally, I created a static reference to the proxy in App.xaml.cs, (see Figure 1), and used App.Proxy in the application (see Figure 2).
Figure 1 - original App.Proxy property defined in App.xaml.cs
Figure 2 - original source that uses App.Proxy from Page.xaml.cs
This first problem that I noticed was the my CityListCompleted method was getting called multiple times.
After some work, I realized that every time getCityList() was invoked, I was adding a new event handler. So if I invoked this method twice, I'd get 2 event handlers.
This may seem obvious, but I have been working in ASP.NET with a stateless model for a long time and I'm still adjusting to this change of paradigm.
The short-term fix was to move the logic that hooks up the event handler to the constructor for the page - see Figure 3.
Figure 3 - short-term fix
After working in this manner, I realized that my problem was fixed - one "complete" per asynchronous request, but then I ran into another problem...By putting the proxy in App.xaml.cs as a property, I made it global in scope. If I had 2 different user controls or areas in my system that referenced the same method, I would hook up 2 "complete" event handlers and they would both be executed when I made my asynch call, no matter where I executed my asynch call from. This worked in my favor in certain situations, but I found this to be sloppy and difficult to control.
The solution was to create the endpoint address for the application and the binding for the application in App.xaml.cs, then create an instance of the proxy within the method that made the asynch request, using the application-level endpointaddress and binding.
Figure 4 shows the address and binding properties in App.xaml.cs and Figure 5 shows my new method which creates the proxy, keeping the scope of the proxy at the method level instead of the application level.
Figure 4 - Endpoint and Binding Properties in App.xaml.cs
Note: I still haven't addressed the issue with the hard-coded endpoint address, but I will eventually figure out how to read this value from a .config file, (any suggestions would be welcome).
Figure 5 - New method that uses App.Binding and App.EndpointAddress
The one thing that I was worried about was performance. Would performance suffer if I moved the scope of the proxy from the application level, where it was created once and only once for the entire life of the application, to the method level where it would be created and destroyed each and every time the method was called.
To prove out my new technique, I created Silver Sample #2.
When you click on "Test 1" button, the application-level proxy is used. When you click on the "Test 2", the new method-level proxy is used. When you click on "Test Both", both the old and new versions of the proxy are called.
My testing showed that there really was no difference, from a performance standpoint, between the 2 techniques. Click here. to see for yourself.
The new technique gives me centralized control of the endpoint and binding, which is what I wanted, with method-level scoping on the proxy which takes an extra line of code, but is a better technique from an organizational, style and stability perspective.