Tuesday, July 1, 2008

Big Silverlight Troubles! No synchronous server calls

We have been tearing our hair out around here because we can’t find a way in Silverlight 2.0 Beta to write a method that blocks the UI thread while it fetches data.

In case you are not aware of this problem, permit me to illustrate.

Suppose in the bowels of your business logic you have a validation rule that says: “total of order not to exceed the customer’s maximum cost for a single order”.

You’re using a domain model with objects that have dotted navigation. You write your rule along the lines of:

… TotalCost(myOrder.Details) < myOrder.Customer.SingleOrderLimit …

Unfortunately, both myOrder.Details and myOrder.Customer.SingleOrderLimit could involve lazy loads of the Details and the Customer.

The UI that enabled the user to create a new detail item for the order is not aware of this rule and didn’t pre-fetch Details and/or SingleOrderLimit.

No problem in regular .NET. You just wait while you lazy load the SingleOrderLimit from the database.

Big problem in Silverlight.

All server calls have to be asynchronous. Your fetch of the SingleOrderLimit is going to have to return immediately; the value will be accessible later during the asynch callback.

Ok, you try to rewrite SingleOrderLimit to stall the UI thread until you get the callback. But there’s no way to do it. If you sleep the thread, you never hear the callback. You make the server call on a worker thread and sleep the UI thread; you never hear the callback. Sit and spin on the UI thread? You never hear the callback. What’s with that?

I understand that I can write synchronous code on the background thread. The problem is that I can't make the UI thread wait for the result.

So you try to live within this crazy world. You decide that you will somehow detect this particular problem and you stash the validation object somewhere, postponing its execution until all the dependent server trips have completed; in my example, the validator can’t run until Details and Customer have been retrieved and how I knew to stack these dependencies is anyone's guess. Good thing they're mutually independent requests because if one query depended on the outcome of another query, the logic would get really twisted.

Meanwhile your original validation call - the one that got us into this mess - must return with some indication like “I don’t know”.

It can't report “everything is ok” or “it’s invalid”. So you have to be sure that the ancestor caller somewhere way up the stack postpones whatever it was doing (like trying to save the order) until you have a definitive answer. And, of course, when it becomes possible to render an answer, you have to remember to renew your originating operation (e.g., the save). You weren't just going to tell the user "Sorry, I can't save yet, not enough information ... try again soon" were you?

Let's say you got that all figured out. But watch out. Maybe your WPF-ish UI is binding to objects in your unit-of-work container. Over on your worker thread you're doing some synchronous magic that fetches Customer and Detail objects. You better not put them into that container on the UI thread ... because it's not thread-safe and if WPF is looking for 'em there's going to be trouble! You better be ready to fetch Customer and Detail into a separate container and then martial them back across the thread boundary in the callback. Oh joy.

You see where I’m going with this. Application programming just became immensely difficult. You can’t write a real program if every data fetch is async. Your UI controller shouldn't have to anticipate every bit of data you might possibly need upfront either. This is a disaster.

Do you know what to do about this? Do you know anyone who does? We’re beating every bush and so far, nada.

21 comments:

Justin Chase said...

We solved this by adding an IsValidating property to our objects performing the validation. You can then bind controls on your UI to the IsValidating property, such as an IsEnabled property of an Ok button or the Visibility property of a processing animation (with a converter of course).

Frankly, this is much better in my honest opinion. It is a little harder for the developer to start to think async but it's much better from a user experience perspective. Synchronous validation is a very bad user experience.

Justin Chase said...

Oh by the way, I think the reason why making a service call waits forever if you block the UI thread to return is because WCF is trying to dispatch the call to the server on the UI thread... This is my theory only but it certainly seems to behave this way even if it's not entirely true.

Interestingly enough this seems to be compounded if you have multiple silverlight applications in the same browser, they seem to all share a UI thread. If you make a call to the server and the server holds onto your thread then all other service calls from that process (i.e. other tabs or child windows) will also be blocked until that call returns.

The threading model in silverlight is extremely limited. In it's defense though I think it's just calling into IE's threading model and is probably limited for security reasons.

Good luck!

btw: http://www.justnbusiness.com

Ward Bell said...

@justin - Thanks for the comments. I'm glad you "solved" it with an "IsValidating" property. Solutions along this line have occurred to us ... and to others.

It is difficult for me to agree that this is "much better". It is intrinsically awful if the developer must disrupt the logical flow to accommodate an implementation artifact.

When I choose to validate at the point of data entry it is because I think the user should be held in place until I can pronounce on the validity of the data entered. That is my choice of user experience. If I know that the validation cannot complete in "reasonable time" then I may choose an async approach and suffer the added burden of figuring out what it means for my UI to "hold" while I get the data. How to make it hold and how I display that it is "waiting for data" will undoubtedly burden my user experience with all kinds of unwanted behavior. If that's what I need, I'll deal with it.

But the choice should be mine, not dictated to me because Microsoft thinks it may take too long to get an answer from the server.

HAVING to think in async terms violently breaks encapsulation by coupling my user experience to the peculiarities of data retrieval.

It gets much worse if my scenario is unpredictable as when it has nothing to do with Validation.

Suppose I want to display the TotalCost of an invoice. Why should my UI be made aware of the possibility that TotalCost cannot be calculated directly? Why do I have to write explicitly for that possibility?

How does Microsoft know that my user will be unwilling to wait while I retrieve the elements of cost? MS has no business telling me what is or is not a good user experience. It's an unwarranted and paternalistic intrusion on my perogatives as developer.

Justin Chase said...

This seems mostly true except that I think that they didn't choose to deprive you of a synchronous data access method intentionally, I believe it is more a limitation of living withing a browser. I believe it is piggy backing on the same mechanism that ajax uses and suffers from the same limitations.

That being said, I believe that asyncrhonous network connectivity is actually superior in every way and does not have to disrupt your logical flow at all. Lambda expressions, for example, are a very powerful way to allow synchronous logical flow despite actually asynchronous execution. This is going to be a more and more common scenario as we find that processors do not become much more powerful but instead we have to utilize multiple cores. Something like this might look like:

Customer.Fetch( 10, c => list.DataSource = c );

Here, your logical flow is synchronous but the actual execution is not. I guess what I'm trying to say is that passing methods as parameters can allow you to chain asynchronous behavior in a logically synchronous manner.

I don't really see how this breaks encapsulation to be honest. It seems fair to have a UI be aware of network activity and such. Users want loading screens and progress indicators, this is the perfect way to do it.

Mark Norman said...

I have to agree with Ward on this. Here is my scenario. I am building dynamic class loading into my Silverlight application. If ClassA requests ClassB from a ClassFactory, and the ClassFactory discovers that ClassB is not in the AppDomain on the client, the ClassFactory calls to the server to retrieve ClassB. Because of async the ClassFactory returns nothing back to ClassA, so the whole operation fails.

I also have to agree with Ward that async breaks encapsulation. You do not want your UI code to know about anything except UI. The fact that there is a network should be abstracted from the UI.

Justin Chase said...

I still don't think it breaks encapsulation, you just have to encapsulate something different. For example, use closures instead.

For your scenario suppose you had a method:
CreateType(Type t)

What you would want to do to make it async friendly is to use a closure so change it to: CreateType(Type t, Action<object> complete)

Calling it might have looked like this the old way:
object obj = CreatType(t);
DoSomething(obj);

But now it looks like this:
CreateType(t, o =>
DoSomething(o));

Any nested calls to CreateType would go through the same process and the final complete lambda would only be called when it was actually complete. You just have to pass method pointers (aka delegates) as variables instead of values.

It's tricky and affects your design considerably but it is a quite powerful technique for asynchronous code. The C# lambda syntax is very friendly also (sorry if you're trying to use VB). But this is totally doable and quite elegant when taken to its logical conclusion.

Anonymous said...

What if there are a few service function calls in a row, with ifs and elses.
It's not always Func() and then DoSomething(). Pretty often it's Func1(); Func2(); Func3(); etc..

This is not a matter of design change but having broken functionality in a base function.
Having OnFuncEnd handlers all over.

And I'm not even talking about combinations of function calls,
func1() { f1();f2();f3() }
func2() { f3();f1();f2() }

makes the owner class look horrible.

Justin Chase said...

func1(Action completed)
{
f1(() => f2(f3));
completed();
}
func2(Action completed)
{
f3(() => f1(f2);
completed()
}

func1(() =>
func2(() => {});

Anonymous said...

Yes, that's just what i meant, not nice at all, isn't it :-)

Justin Chase said...

Beauty is in the eye of the beholder I guess :)

Anonymous said...

Hard to argue with that :-)

Anonymous said...

Async ruined my life

Rajesh Cheedalla said...

http://www.codeproject.com/KB/silverlight/SynchronousSilverlight.aspx

This article discusses about synchronous programming within Silverlight.

Ward Bell said...

@Rajeesh - that article does a nice job of explaining how Silverlight gets us into this predicament.

If I read it correctly, it does not address the synchronous programming problem we're covering here ... which is how to stall the UI while waiting for a remote process to finish.

@Justin's resort to functional programming style is wonderful at explaining how to respond to the inherent async nature of SL ... and I applaud him for it.

But I also think he dramatizes my very point ... which is that I must be aware of side-effects in my business logic (e.g., validations) at the UI level ... and must code accordingly with functional programming. That's a burden, as he acknowledges. It means I have to be aware of concerns (trip to server) where I do not want such awareness.

Now there may be good perf reasons for such concerns to leak into my UI thinking; if I could block the UI, I might freeze the UI unacceptably. But that should be my decision; I shouldn't have the async patterns forced upon me.

Regarding function chaining (@Justin and Anonymous), it may be worth noting that DevForce provides a facility for parallel and serial chaining of tasks ... especially those that involve async trips to the server.

Moreover, you can write these so that the decision to move from task 'A' to 'B' is contingent on outcome from 'A'. And there are provisions for exception handling. Pretty robust. Check it out.

Thanks everyone for your contributions; it's a worthy exchange.

qwe said...

shaiya gold is the important one in the Shaiya Game, when I begin to come into contact with the wonderful Online Game. Every one also likes playing this Shaiya game with some shaiya online gold. Although the game is free to play, we have to cost some shaiya money to buy our favorite equipment. Their primary goal of cheap shaiya gold is not damage infliction, but rather keeping foes away from other party members. So I have decided to buy shaiya gold to try playing this game first.
rappelz rupees of Rappelz Online Game has more functions and this Rappelz Game is a very good free game. This rappelz gold of score could go down A LOT if they choose to mess up the classes even more. I would have rated it much cheap rappelz rupees better when it was epic3. If yes, then the first step to buy rupees when you play Rappelz is for you. Play rappelz and feel the power and might of rappelz money. See you in game!

said...

You know ,I have some kal geons, and my friend also has some
kal gold, do you know they have the same meaning, Both of them can be called
kal online geons,I just want to buy some
kal online gold, because there are many
kalonline Geons.

said...

.

You know ,I have some ghost gen, and my friend also has some ghost online gen, do you know they have the same meaning, Both of them can be called ghost gold,I just want to buy some
ghost online gold, because there are many cheap ghost money.

Anonymous said...

Making requiem gold is the old question : Honestly there is no fast way to make lots of requiem lant . Sadly enough a lot of the people that all of a sudden come to with millions of requiem money almost overnight probably duped . Although there are a lot of ways to make lots of requiem online gold here I will tell you all of the ways that I know and what I do to buycheap requiem lant

Anonymous said...

As a new player , you may need some game guides or information to enhance yourself.
Rose zuly is one of the hardest theme for every class at the beginning . You must have a good way to manage your rose zulie.If yor are a lucky guy ,you can earn so many rose online zuly by yourself . But if you are a not , I just find a nice way to get rose online zulie. If you need , you can Arua ROSE zuly at our website . Go to the related page and check the detailed information . Once you have any question , you can connect our customer service at any time .

Anonymous said...

I think EtherSaga is my favorite game, I did not know how to play at first, someone told me that you must have EtherSaga Online Gold. That he gave me some EtherSaga Gold, and then he said that I could buy EtherSaga Gold, but I did not have so much money, and then I played it in all my spare time. From then on, I have got a lot of EtherSaga Online money, if I did not continue to play it, you can buy cheap EtherSaga Gold.

Once I played GuildWars, I did not know how to get strong, someone told me that you must have gw gold. He gave me some GuildWars Gold, he said that I could buy Guild Wars Gold, but I did not have money, then I played it all my spare time. From then on, I got some GuildWars money, if I did not continue to play it, I can sell cheap gw gold to anyone who want.

lanzi said...

i can get maple mesos cheaply,
Yesterday i bought mesos for my brother.
i hope him like it. i will give maple story mesos to him
as birthday present. i like the cheap mesos very much.
I usually buy the maplestory mesos and keep it in my store.

I can get LOTRO Gold cheaply.
Yesterday i bought Lord Of The Rings Gold for my brother.
i hope him like it. i like the cheap Lord Of The Rings Gold very much.
I usuallybuy LOTRO Gold and keep it in my store.