Volta WinForms..

So I've been playing with Volta in my spare time this evening -
just had a go at tier-splitting a winforms application to see how
that works... so I did something pretty trivial, the form has a
button, you type a name into a textbox, click the welcome button,
it invokes a method on the welcome service and outputs the results
to the textbox... I plan to tier split between the client and the
underlying service, so that the service could be executed on a
"Server".




alt="2_simple_winform.png" border="0" height="300" widthsrc="300" />


public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void welcomeButton_Click(object sender, EventArgs e)
{
SomeService service = new SomeService();
welcomeOutput.AppendText(service.WelcomeMessage(nameTextBox.Text));
}
}

The service itself looks like this:



[RunAt("Server")]
public class SomeService
{
public string WelcomeMessage(string name)
{
return string.Format("Welcome: {0}", name);
}
}


Unlike a web-app, you can't seem to use "RunAtOrigin" for
indicating a tier-split between client/server - when you think
about this makes sense (you could have more then server for a
win-forms app, because the "origin" and the client are the same
thing, unlike the client (javascript) and origin (web server)
relationship that exists for a web app.



Now running the application spins up the Volta test server, if you
click on volta icon (btw, cudos to whoever did the volta
branding/logos - I love em) in the system tray, you can tick the
"log requests" to see what traffic between client/server is
occurring... clicking the welcome button shows two requests being
made for each button click.




alt="1_log_requests.png" border="0" height="323" widthsrc="590" />



Now moving our eyes towards the tier splitter's output, we can see
on the client side that our test service class is converted into a
proxy (obviously)


[RunAt("Server")]
public class SomeService
{
// Fields
private Guid __id;
private static ClientMap __map;
private static string __uri;

// Methods
public SomeService()
{
object[] parameters = new object[0];
this.__id = Proxy.Call(__Serializer.GetInstance(), __GetUri(), "SomeService", 1, null, null, parameters);
__GetMap().Add(this.__id, this);
}

private SomeService(__TypeSerializer __typeSerializer)
{
}

private static ClientMap __GetMap()
{
if (__map == null)
{
__map = new ClientMap();
}
return __map;
}

private static string __GetUri()
{
if (__uri == null)
{
string service = ConfigurationManager.AppSettings.Get("Server");
if (service == null)
{
throw new ArgumentException("Could not find application configuration entry for 'Server'.");
}
__uri = ServiceLocation.GetService(service, "__TierSplit.aspx");
}
return __uri;
}

public string WelcomeMessage(string name)
{
object[] parameters = new object[] { name };
return Proxy.Call(__Serializer.GetInstance(), __GetUri(), "SomeService", 0, this, null, parameters);
}

// Nested Types
public sealed class __TypeSerializer : AbstractTypeSerializer
{
// Methods
public override SomeService Deserialize(ISerializer serializer, IObjectReader reader)
{
reader.ReadSeparator();
reader.ReadMemberName("id");
Guid id = new Guid(reader.ReadPrimitive());
ClientMap map = SomeService.__GetMap();
SomeService byID = (SomeService)map.GetByID(id);
if (byID == null)
{
byID = new SomeService(this);
byID.__id = id;
map.Add(id, byID);
}
return byID;
}

public override void Serialize(ISerializer serializer, IObjectWriter writer, SomeService value)
{
writer.WriteSeparator();
writer.WriteMemberName("id");
writer.WritePrimitive(value.__id.ToString());
}
}
}



And on the server-side, our service has also been altered slightly,
note the inner __Service class:


[RunAt("Server")]
public class SomeService
{
// Methods
public string WelcomeMessage(string name)
{
return string.Format("Welcome: {0}", name);
}

// Nested Types
public sealed class __Service : Service
{
// Methods
public override string get_Name()
{
return "SomeService";
}

protected override void ProcessMethod(Service.Call call)
{
switch (call.GetMethod())
{
case 0:
call.Return(call.GetInstance().WelcomeMessage(call.GetParameter(0)));
break;

case 1:
call.Return(call.AddInstance(new SomeService()));
break;

default:
throw new InvalidMethodException();
}
}
}
}



Now what about those two requests - looking at the code we can
deduce that the two calls are occurring first for
construction/registration of the client proxy, and second for
handling the message - changing the client app to look like
this:


public partial class Form1 : Form
{
SomeService service = new SomeService();

public Form1()
{
InitializeComponent();
}

private void welcomeButton_Click(object sender, EventArgs e)
{
welcomeOutput.AppendText(service.WelcomeMessage(nameTextBox.Text));
}
}



And clicking the welcome button fixes the behavior, so that for
successive button clicks only one request is being generated.



Afraid that's all I have time for tonight... but I'll post more
about my Volta explorations this week when I get spare
moments.
Written on December 9, 2007