Sunday, December 09, 2007
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".

2_simple_winform.png

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.

1_log_requests.png

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<Guid>(__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<string>(__Serializer.GetInstance(), __GetUri(), "SomeService", 0, this, null, parameters);

    }

 

    // Nested Types

    public sealed class __TypeSerializer : AbstractTypeSerializer<SomeService>

    {

        // 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<SomeService>

    {

        // Methods

        public override string get_Name()

        {

            return "SomeService";

        }

 

        protected override void ProcessMethod(Service<SomeService>.Call call)

        {

            switch (call.GetMethod())

            {

                case 0:

                    call.Return<string>(call.GetInstance().WelcomeMessage(call.GetParameter<string>(0)));

                    break;

 

                case 1:

                    call.Return<Guid>(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.
posted @ Sunday, December 09, 2007 10:08:19 AM (New Zealand Daylight Time, UTC+13:00)    Comments [0] | Trackback | Tracked by:
"Volta - Type reuse across tiers" (Bitter Coder) [Trackback]
Comments are closed.
Search
FeedCount

Tags...
Who am I?
Alex Henderson
Alex Henderson
Auckland, New Zealand
Managing Director at Dev|Defined Limited

"Self Confessed Coding Junky for 15 years"
View Alex Henderson's profile on LinkedIn
 
Mobile: +64-21-402-969
Email: bittercoder 'at' gmail 'dot' com
MSN: bittercoder_nz@hotmail
Skype: alex.devdefined
Navigation