13.02.2010 г.

Some experience with Silverlight

So as I promised here is the article about what I learned about Silverlight from my first serious touch with it.
As I mentioned before, from some time I'm working on a Silverlight project, that is some kind of workflow designer. Briefly said I have nodes and routes between them and nodes plus routes make a task.I will not deepen on the purpose of this architecture, because it is related to our specific needs. So actually this WF designer is not a general purpose one, but it is specifically designed to serve us. However it was a good experience for me, because you know, the best way to understand some new technology is to start using it. Then comes Google :).

I had some challenges before I start to write.

First of all was the data model.The objects I described above(tasks, nodes and routes) were represented as database tables. To spice the things up, the node object had some bunch of dependent objects, that were not presented as database tables but serialized as XML and stuck in a text column of the node table. So there was a serialize/deserialize process when dealing with the nodes. However this part was already done for an old windows forms application with the same purpose as the silverlight designer. Btw the silverlight designer was meant to replace the old windows forms one. I had the XML <-> objects conversion done. However I couldn't use those objects, because they were not marked as Data contracts. But for that I will write below.
So enough for the issues regarding the data and its representation.

The second challenge was how to get this data from the server and serve it to the client and vice versa. Here the solution was straightforward.

The last major problem was how the hell to implement a GUI for editing the whole bunch of objects. The problem was that I needed to mitigate the complication of the data model and to do the GUI as simple as possible for the user.

So let me start with the decisions I made.
About the data model, I decided to create a mirror object to every object I needed to transfer across the wire. So lets say as example that I had three objects - NodeSettings, NodeCoordinates, NodeSomethingElse. Remember, all these objects are serialized to XML and the result XML is saved in a text column of the node table. Also I had an API to work with these objects. Lets name the "mirror" objects for these three ones respectively NodeSettingsTransfObj, NodeCoordinatesTransfObj and NodeSomethingElseTransfObj. They were marked as data contracts. To work easily with both the framework ones and the transfer ones I created for each of the objects an extension method. Did I mentioned that extensions rule! So for example the pair NodeSettings <-> NodeSettingsTransfObj. I created on NodeSettings an extension method named ToTransfObj and on the NodeSettingsTransfObj an extension method named ToFrameworkObj. All the extension methods I put in a single class. The name of the class can be whatever you want, but the class needs to be static. After this really hard and boring work you will be using the extensions on a single line.

Here is the whole picture:


About the second challenge - the actual transport between the server and the client:
Here as I said the choice was clear. WCF is the only way to transfer information when we talk about silverlight. So there was no other option for me but WCF. And I don't complain. WCF is marvelous. It is bullet fast, extremely stable, reliable and very flexible and easy to configure. But not everything is so pink with WCF services called from silverlight. The key thing here is that you can call operations of the service only in Asynchronous manner. This is a little bit tricky. In some scenarios you will need the synchronous calls desperately but sorry - you just can't do this. I spent a whole day to dig for a workaround in the Internet with no success. I don't exclude the chance that a workaround exists, I simply say that I didn't manage to find a real good one. One of the workarounds was actually helpful. It was using a separate thread for the call to the service operation and also it was using the BeginXXX and EndXXX methods of the service operation. However to assure the thread has finished its work and the call to the operation has returned a result I needed to block the main thread and join the service calling one. This is important: never block the main thread. The main thread appears responsible for handling the communication with the service API. So actually when blocking the main thread you get yourself in deadlock situation. The main thread is needed to handle the response from the service and as it is blocked it can't do that. Also the supporting thread can't finish its job while the call is in progress. Well I hope you got the picture, cause I can't explain it better. It is already blur for me, because it's a month since I fought that problem. But the picture was something like that. So I had to stick to the natural design, because the whole "thread using solution" didn't seem to be stable and reliable. The last thing related to the WCF part and its asynchronous calls is actually more like an advice. Whenever possible do call the service operations before they are actually needed. For example you have a form (or window if you like) that you use to edit object of type NodeSettings. This object lets say has a property that is collection of type SomeType. This collection you edit on a separate form that is shown when you click a button on the first form (a button saying for example "Edit Members"). So the best practice in my opinion is to call the operation that will return all the "members" of type SomeType before the user clicks the button. So to sum up: Call the operations ASAP! These calls are not expensive. WCF is very very fast.

The last one is the GUI. GUI has always been a pain in my ass. My designer abilities are are enough good to realize that my GUI sucks :). However with this project I had to face some issues. I will just mark them briefly, because this article became bigger than planned. First of all I consider a good practice to use a single instance of each form rather than creating and disposing one every time. So I have lots of hidden forms on the visible area of the silverlight application and I show/hide them according my needs. To pass data between forms I use events and stick the data in the args.
Also data can be shared between forms across the whole application by using Application.Current.Resources dictionary.

I hope you can get something valuable from this long and boring article :)

Няма коментари:

Публикуване на коментар