Monday, February 04, 2008


So the sun has set on day 1 of the Summer Road Trip 2008 in Auckland - The Presentation was done by JB, Chris and Myself this afternoon - it went very well, people enjoyed the content and the mix of integration, server management, database and development topics really meshed together nicely I thought... 

There was enough to keep everyone interested, regardless of the hat you wear - and plenty of prizes too - well all love swag right?!

Big thanks to our MC Jaqcui, who handled the Intro and Outro and let everyone know about the local Ellerslie & Central Auckland user group's - where I'll be sure to run a few sessions later in the year... and of course Darryl for handling some of the finer details like the venue, lunch, and the dinner afterwards - much appreciated.

For all those that came along - first off thanks for coming, obviously without participation in these events they'll just dry up and stop happening - and second don't forgot that there is no time like the present to start picking up these technologies and developing applications with and for them - the products are all but ready, so why can't you be (and not only are they great technologies, they're fun too).

The next presentation is in Tauranga - and there are still places left, so sign up here - It's going to be on tomorrow (5th of Feb) at 1:00pm I believe.

And finally a short plug ;o)

SylviaParkArchitectureChat.JPG

For anyone that found this presentation interesting and would like to discuss the technical details of things like emerging technologies, general software Architecture, Developer Tools, Running software businesses etc.  I also organise the local Sylvia Park Architecture Chat - which is a pretty casual meeting of some very smart people in the .Net Community. 

We normally get together on a fortnightly basis at Garrisons in Sylvia Park and are always keen to have more people/fresh faces to come along and join in our discussions or even just float some development/architecture questions or problems you might have that the group can help solve - keep an eye on my blog, or the dot.net.nz mailing list for announcements of when we'll next be meeting up :)

And all are welcome of course!

posted @ Monday, February 04, 2008 8:24:40 AM (New Zealand Daylight Time, UTC+13:00)    Comments [0] | Trackback |
 Wednesday, January 23, 2008
The Sylvia Park Architecture Chat is to be held this Thursday, 11:30am at Garrisons in Sylvia Park.

I think we are well and truly into the digestion phase after being flooded with so many product drops and previews over the last few months – announcements are low, but the amount of content about taking advantage of what’s finally in our hands is increasing... arguably this is far more interesting/rewarding then actually getting the tech drops in the first place.

At any rate, things that have caught my eye this week (not much as you’ll see, I haven't had much opportunity to read blogs over the last few weeks):

If anyone has any topic suggestions, either fire me a comment/email or just bring yourself along – all are welcome.

posted @ Tuesday, January 22, 2008 9:43:41 PM (New Zealand Daylight Time, UTC+13:00)    Comments [0] | Trackback |
 Tuesday, January 15, 2008
Sorry about the late write-up, been a little busy!

Small turn out, with 5 us in all, including new comer Jamie – a recent graduate from Auckland University, it was great to talk with someone just entering the industry proper and I think everyone else found it very interesting to hear about what's going on at Auckland Uni.

So first off we discussed adventures with alternative licensing products – Gareth had a few war stories, and mentioned he’s returned to using .Net Reactor and got a refund on the previous product he tried out because it just wasn’t living up to expectations, and had some questionable weak points.

We talked about the C5 Generic Collection Library – though none of us have attempted to use them in anger, a few people had heard about them recently because it seems to be doing the podcast/blog rounds, even though the project first debuted at the start of 2006, and was in development well before then.

The classes were developed at the IT University in Copenhagen, and feature a large number of specialised collections, as well as introducing features to provide event handlers and sliding views, support for clearing collection ranges etc.

There is a recent video on MSDN Channel 9, which is worth a watch as well.

Because we didn’t have it in our hands last year before I wrapped up the Architecture Chat I discussed the ASP.Net Web Extensions CTP, mentioning the amount of community interest in MVC and that the Data Services (Astoria) is looking better and better.

From there we talked about the MVC Contrib. project (and community) that’s sprung up after the ASP.Net MVC release, which is getting some New Zealander's attention around the world because of their contributions of both an XSLT View engine and NHaml View engine – the contrib project is also providing integration with the popular IoC containers in .Net and a number of other extensions.

I talked about F# parsing, and my explorations of writing parsers in F#, especially after reading the series of posts from DevHawk (Harry Pierson), and experimenting with writing DSL’s in F# by hand.  I think my next "goal" is to master integrating F# libraries into my C# code, so I can commercialize on it and start weaving it into my day to day tool set.

I’m not sure I articulated how elegant F# syntax can be - but hand writing custom parsers in F# with the aid of Active Patterns is much nicer then the equivalent in a language like C#, If you’re following the pragmatic programmer guidelines of learning a language a year, you could do much worse than to learn F# for 2008, it certainly gives the brain a good workout :)

From their I mentioned PEX for use in automated white box testing – Jamie said he had worked on a 4th year project to do automated black box testing, and found it interesting that white box testing of this nature could be made viable/useful... PEX seems to have done it though.

Though it’s not been made available to the general public as yet (only academia) – I suggest having a listen to this hanselminutes podcast, and then watching this screencast to get a better idea of just what PEX is doing/aiming to achieve...  I find the support for mocking particularly interesting, as I’m always sceptical of automated test generation, as it normally falls apart once you start to work objects when have numerous injected dependencies that are used by the class to do it's work.

Last of all I mentioned TeamCity (JetBrains CI & Build server) Professional Edition is free, and I’ve been starting to play with it - and considering migrating over from my current CruiseControl.Net setup for new projects, I’m going to trial it on a small project and see if it’s worth moving to for what I do.

Last of all – though I forgot to mention it at the time - I’ve also been looking at Jazz (or more so Rational Team Concert) lately, it certainly looks to resolve many of the headaches I suffer with managing concurrent versions of products, especially from the build server perspective – obviously anything with the name “Rational” is to be feared by the small or micro ISV because of prohibitive costs – but it’s nice to see just how they approach solving the problem, as shown in the video "fixing a bug in a previous release".

Not a bad start to the year, hopefully we can make the next one bigger and better, and maybe get some more long-running architectural discussions going on ... maybe around Behavior Driven Development/Design or maybe Feature Driven Development and what makes it more suitable for fixed-price jobs.

See you all next time!

posted @ Monday, January 14, 2008 7:50:00 PM (New Zealand Daylight Time, UTC+13:00)    Comments [0] | Trackback |
 Wednesday, January 09, 2008
Update: 22nd May 2008 - Please if you have any .Net reactor related queries, post them to this google group, rather then emailing me directly, as you're more likely to get a response.  Cheers!

If you recall in the last Architecture Chat (#21) Gareth of Slyce Software mentioned the demise of .Net Reactor, a German code obfuscation product that many users swear by.. but over the past 2 or so months it went completely dark as the company ceased responding to any correspondence, or making releases - where as prior they were almost phenomenally fast to get back to queries, and we releasing every couple of weeks.

Well I got an interesting email from a .Net Reactor user who has been suffering the same issues, not having a response out the company in the last couple of months but he did advise that releases are starting to flow out the company again, with 3 new versions released in December... so things could be looking up, even if the wall of silence continues.

Apparently it got so bad that even some add-on vendors have withdrawn in-prorgess products due to a lack of communication with Eziriz - what I find interesting here is how quickly all the good work you can do building up a community around a product for developers can unwind - and demonstrates how important a visible "heartbeat" for a product is to keeping trust in your project - be it through regular releases, actively participating in forums and mailing lists or at the very least publishing the odd bit of news on your site.

Fingers crossed the company starts answering peoples emails again at any rate - it'd be a shame to see a popular product's community turn their back on it just because their worried it's going to dissappear completely.

Right, so yes, there is a point to this post - assuming the worst and .Net Reactor is winding down, can anyone else recommend an obfuscation/licensing products in the sub 300 US$ range that works well?

Edit:

Shortly after posting this I was notified that Apparently everything is good once more in .Net Reactor land with the lead developer having been taken away from development due to other life issues taking over in the later part of last year, but he's back and developing/communicating once more so it should be "business as usual".

I look forward to seeing what cool things Eziriz get up to in 2008!

Edit (Again):

And shortly after that I also got an email, so everything is definitely fine once more with .Net Reactor:
Dear Mr. Henderson, I am sorry for the problems you had with my product support. Gareth Hayter referred me to your blog. Please be sure, .NET Reactor product development/support is firing on all cylinders again.

Best regards,

Denis Mierzwiak, Chief Technical Officer.
posted @ Wednesday, January 09, 2008 6:17:54 AM (New Zealand Daylight Time, UTC+13:00)    Comments [2] | Trackback |
 Tuesday, January 08, 2008
So, the Sylvia Park Architecture Chat is back for 2008 – To be held this Thursday, 11:30am at Garrisons in Sylvia Park.

Some possible topics for discussion are listed below, though feel free to suggest additional topics either via a comment on this post or email - I haven't been keeping much of an eye on technology over the xmas break.

  • ASP.Net Web Extensions CTP Preview Was Released, including MVC, Dynamic Data Support (scaffolding *yawn*), Silverlight extensions & Data Services (Astoria).
  • MvcContrib project sparked off – adding NVelocity, NHaml (developed by Andrew Peters) and Xslt  (Developed by Ivan Porto Carrero) view engines to the ASP.Net MVC project as well as providing integration with a select number of IoC containers and other improvements to make the ASP.Net MVC CTP easier to work with i.e. convention over configuration.
  • The Castle Refactor – Monorail is getting an overhaul in the Castle Project, and they are in the process of moving to the NHibernate (2.0) Trunk as well.  Hopefully it will make my guide to running with the “trunks” largely obsolete.
  • Parallel FX CTP – We didn’t talk about this last time, might be worth giving it a look.
  • Practical F# Parsing – I mentioned a little bit about parsing with F# at the last Architecture, and this series I've been watching with interest as it's covered off implementing a PEG parser with F# -writing Parsers with F# is pretty slick compared to object oriented languages.
  • Internet Explorer 8 announcements.
  • Rod Drury – For anyone involved in a startup from day one I’m sure we can give these series of posts (1,2 & 3 of 6 so far) a definite head nod (or a long drawn out sigh for guidance arriving to late ;o)
See you all there!

posted @ Monday, January 07, 2008 7:59:12 PM (New Zealand Daylight Time, UTC+13:00)    Comments [0] | Trackback |
 Tuesday, December 11, 2007
So my last post was a quick look at Volta & WinForms (if you wonder why I was using WinForms rather then web, I just figured it'd be a good place to start, without the complications of what can/can not be translated to javascript) - I haven't really been keeping track of what other people are doing with Volta, but there have been a couple of posts from Microsoft bloggers, including Wes Dyer (on tier splitting a web application) and Drargos Manolescu (on tier splitting a winforms application) - hopefully some of the other members on the Volta team will pick up the gauntlet and start blogging shortly too.

This post is going to be very short (if you ignore the code snippets) - we've already seen how classes can be split into two, one being the client proxy the other the service implementation - but what happens if a class is used in the client and server but isn't fixed/pinned to run on a specific Tier via the RunAt attribute... that's the focus of this post.

So, in this example - I'm going to add a helper class to my previous example from the last post - which just formats some text all "pretty" like:

public class HelperClass

{

    public string PrettyFormat(string text)

    {

        return string.Format("--> {0} <--", text);

    }

}


Now I'll update my service to use pretty formatting:

[RunAt("Server")]

public class SomeService

{

    private readonly HelperClass _helper = new HelperClass();

 

    public string WelcomeMessage(string name)

    {

        return _helper.PrettyFormat(string.Format("Welcome: {0}", name));

    }

}


And lastly, I'll add an additional button called "welcomeLocalButton" which when clicked will make use of the helper to format some text on the client, instead of calling the WelcomeMessage method on the service.

public partial class Form1 : Form

{

    SomeService service = new SomeService();

    private HelperClass helper = new HelperClass();

 

    public Form1()

    {

        InitializeComponent();

    }

 

    private void welcomeButton_Click(object sender, EventArgs e)

    {  

        welcomeOutput.AppendText(service.WelcomeMessage(nameTextBox.Text));

    }

 

    private void welcomeLocalButton_Click(object sender, EventArgs e)

    {

        welcomeOutput.AppendText(helper.PrettyFormat("Welcome Local " + nameTextBox.Text));

    }

}


So this is how the app looks now...

6_welcome_local_winforms.png

Compile and run - everything works as expected, now looking at the generated client and server assemblies we see both contain... the same class - yep that's right it just duplicates the class in both the client and server tiers, without changing it at all.

Now for windows forms this doesn't appear as all that much of a miracle (it's still nice, we wouldn't want helper classes being turned into implicit services just because they're used across tiers)... but for web or other target platforms (such as embedded devices) this is where the elegance begins to kick in, not only are you able to declaratively specify where code is executing, but if you don't specify/fix (I wonder what the correct terminology is here?) a type to a specific tier you get the best of both worlds i.e. code that's native assemblies on the origin/server side and javascript implementations on the client side.

So to demonstrate the web equivalent I whipped up the following example - I was trying to think of something interesting that would be useful to do client and server-side - and thought, perhaps I would use Andrew's Inflector.Net - sadly there are a few issues with that, namely Regex isn't supported out of the box in Volta (which is a real shame, this is something I would've have expected to have been in the preview - regex being such a swiss army knife, especially for scripting) - so instead I picked something a little simpler - the Ordinalize functionality from Inflector, and just stripped out the other unrequired methods which would give us grief.

So the example is pretty simple, we have a web page that looks like this:

3_ordinalizer.png

You enter a number, it get's ordinalized, either via a remote call or a client-side call... so let's take a quick look at the code, first we have the ordinalize method:

public static class Inflector

{

    public static string Ordinalize(string number)

    {

        int n = int.Parse(number);

        int nMod100 = n % 100;

 

        if (nMod100 >= 11 && nMod100 <= 13)

        {

            return number + "th";

        }

 

        switch (n % 10)

        {

            case 1:

                return number + "st";

            case 2:

                return number + "nd";

            case 3:

                return number + "rd";

            default:

                return number + "th";

        }

    }

}


Then we have the server-side OrdinalizerService:

[RunAtOrigin]

public class OrdinalizerService

{

    public string Ordinalize(string number)

    {

        return Inflector.Ordinalize(number);

    }

 

    [Async]

    public extern void Ordinalize(string number, Callback<string> callback);

}


And finally we have the UI code:

public partial class VoltaPage1 : Page

{

    Input numberElement;

    Button button1;

    Div resultsElement;

    Button button2;

 

    public VoltaPage1()

    {

        InitializeComponent();

 

        var ordinalizer = new OrdinalizerService();

 

        button1.Click += delegate

        {

            var name = numberElement.Value;

            resultsElement.InnerText = Inflector.Ordinalize(numberElement.Value);

        };

 

        button2.Click += delegate

        {

            var name = numberElement.Value;

            ordinalizer.Ordinalize(

                name,

                message => { resultsElement.InnerText = "Remote: " + message; });

        };

    }

 

    partial void InitializeComponent()

    {

        numberElement = Document.GetById<Input>("Text1");

        resultsElement = Document.GetById<Div>("Results");

        button1 = Document.GetById<Button>("Button1");

        button2 = Document.GetById<Button>("Button2");

    }

}


So... what's left after the tier-split?

Well if you open up reflector and look at either the client or the server assembly - you will still see the same Inflector class i.e. just like the previous winforms example it just copies it to both tiers - and that's it... no magic at this point, because the process of transforming the class to javascript doesn't occur untill runtime.  Though obviously it does need to copy it because the client and service layer are entirely independent.

I could stop there - but I'm sure many people are curious as to just what the javascript looks like :)

So just to round this post out - we'll take a brief look at the generated javascript - so if you use a tool like firebug while loading a page developed with volta you will see alot of activity going on as individual types are loaded one by one from the server, like so...

4_loading_assemblies_js.PNG

There are pages and pages of these calls... though there are plans to reduce the number of round trips in the future (obviously a round trip per type is a pretty bad idea in a complex app) but you need to keep your eye on the prize... Volta is not competing with meticulously hand crafted MVC web-based solutions with little sprinkles of Ajax here and there... the benefits would come from employing it where the complexity and drudgery of client side scripting is overwhelming and large amounts of asynchronous messages are being exchanged between client and server... at least that's where I see a sweet spot... obviously there are plenty of other side-effects as well (ubiquitous refactoring springs to mind).

Now if you scroll down past all the standard (BCL) and volta related types you eventually find... no mention of the Inflector type.

No magic here though - just type a value into the textbox and click Ordinalize Local - and flick back to Firebug's console... and you will see a request being made for the Inflector type - types are lazy loaded - makes a lot of sense, when you might have types only required by a single UI element on the screen that the user never touches.

So here's the request for the inflector type:

5_inflector_js_request.PNG

Notice the two query string parameters a (assembly) and t (type) ... and now if we were to flick over to the response tab, we would see the javascript produced for that type (see below)

Notice it adds this type to the list of types in the assembly, maintaining the same symantecs between javascript and the .net framework - obviously the javascript is a little scary, especially considering all the existing variable names have been lost - but for all that it's quite readable:

var CurrentAssembly = Assemblies["VoltaWeb.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"];

 

CurrentAssembly.TypeDefs["VoltaWeb.Inflector"] = (function(){

 

    var Ret = {};

 

    var Assembly = CurrentAssembly;

 

    var asmRef_0 = Assembly.References.ref_0();

 

    var typDef_0 = GetTypeDef(asmRef_0, "System.Object");

 

    var typDef_1 = GetTypeDef(asmRef_0, "System.Object");

 

    var typRef_0 = GetTypeRef(asmRef_0, "System.String");

 

    var typRef_1 = GetTypeRef(Assembly, "VoltaWeb.Inflector");

 

    var typRef_2 = GetTypeRef(asmRef_0, "System.Object");

 

    var typRef_3 = GetTypeRef(asmRef_0, "System.Object");

 

    var typRef_4 = GetTypeRef(asmRef_0, "System.Boolean");

 

    var typRef_5 = GetTypeRef(asmRef_0, "System.Int32");

 

    var typRef_6 = GetTypeRef(asmRef_0, "System.Void");

 

    var methDef_0 = GetMethodDef(typDef_0, "ToString", [typRef_0]);

 

    var methDef_1 = GetMethodDef(typDef_1, "Equals", [typRef_2, typRef_4]);

 

    var methDef_2 = GetMethodDef(typDef_1, "GetHashCode", [typRef_5]);

 

    var methDef_3 = GetMethodDef(typDef_0, "Finalize", [typRef_6]);

 

    var methRef_0 = GetMethodRef(typRef_3, "ToString", [typRef_0]);

 

    var methRef_1 = GetMethodRef(typRef_3, "Equals", [typRef_2, typRef_4]);

 

    var methRef_2 = GetMethodRef(typRef_3, "GetHashCode", [typRef_5]);

 

    var methRef_3 = GetMethodRef(typRef_3, "Finalize", [typRef_6]);

 

    var Methods = {};
   
    Methods["meth_13"/*VoltaWeb.Inflector.Ordinalize*/] = function _VoltaWeb_Inflector_Ordinalize_System_String_(param_2) {

 

        var asmRef_0 = Assembly.References.ref_0();

 

        var typDef_0 = GetTypeDef(asmRef_0, "System.Int32");

 

        var typDef_1 = GetTypeDef(asmRef_0, "System.String");

 

        var typRef_0 = GetTypeRef(asmRef_0, "System.String");

 

        var typRef_1 = GetTypeRef(asmRef_0, "System.Int32");

 

        var methDef_0 = GetMethodDef(typDef_0, "Parse", [typRef_0, typRef_1]);

 

        var methDef_1 = GetMethodDef(typDef_1, "Concat", [typRef_0, typRef_0, typRef_0]);

 

        var loc_3 = typDef_0.Initializer({});

 

        var loc_4 = typDef_0.Initializer({});

 

        var loc_5 = typDef_0.Initializer({});

 

        var $next;

 

        $next = 0;

 

        while (true) switch($next) {

 

            case 0:

 

                {

 

                    loc_3 = methDef_0(param_2)/*System.Int32.Parse(System.String)*/;

 

                    loc_4 = loc_3 % 100;

 

                    var br1 = loc_4 < 11;

 

                    if (br1 || br1 === "") {

 

                        $next = 40;

 

                        continue;

 

                    }

 

                    var br2 = loc_4 > 13;

 

                    if (br2 || br2 === "") {

 

                        $next = 40;

 

                        continue;

 

                    }

 

                    return methDef_1(param_2, "th")/*System.String.Concat(System.String,System.String)*/;

 

                    $next = 40;

 

                }

 

            case 40:

 

                {

 

                    loc_5 = loc_3 % 10;

 

                    switch(loc_5 - 1){

 

                        case 0:

 

                            $next = 70;

 

                            continue;

 

                        case 1:

 

                            $next = 82;

 

                            continue;

 

                        case 2:

 

                            $next = 94;

 

                            continue;

 

                    }

 

                    $next = 106;

 

                    continue;

 

                    $next = 70;

 

                }

 

            case 70:

 

                {

 

                    return methDef_1(param_2, "st")/*System.String.Concat(System.String,System.String)*/;

 

                    $next = 82;

 

                }

 

            case 82:

 

                {

 

                    return methDef_1(param_2, "nd")/*System.String.Concat(System.String,System.String)*/;

 

                    $next = 94;

 

                }

 

            case 94:

 

                {

 

                    return methDef_1(param_2, "rd")/*System.String.Concat(System.String,System.String)*/;

 

                    $next = 106;

 

                }

 

            case 106:

 

                {

 

                    return methDef_1(param_2, "th")/*System.String.Concat(System.String,System.String)*/;

 

                }

 

        }

 

    };

 

    Ret["Methods"] = Methods;

 

    var VTable = {};VTable[methRef_0] = methDef_0;VTable[methRef_1] = methDef_1;VTable[methRef_2] = methDef_2

 

;VTable[methRef_3] = methDef_3;

 

    Ret["VTable"] = VTable;

 

    var Parents = {};Parents[typRef_1.Id] = true;Parents[typRef_2.Id] = true;

 

    Ret["Parents"] = Parents;

 

    Ret["PublicMethods"] = {};

 

    Ret["PublicMethods"]["Ordinalize"] = {};

 

    Ret["PublicMethods"]["Ordinalize"][GetSignature([typRef_0, typRef_0])] = "meth_13"/*VoltaWeb.Inflector

 

.Ordinalize*/;

 

    Ret["Assembly"] = CurrentAssembly;

 

    Ret["Name"] = "VoltaWeb.Inflector";

 

    Ret["Initializer"] = (function(instance){

 

        return instance;

 

    });

 

    Ret["TypeInitializer"] = (function(_vT){

 

    });

 

    return Ret;

 

})();


Now the above output was generated because I had enabled "verbose javascript output" - by default this isn't on, but can be enabled via a check box on the Volta tab, in the properties for the Volta project:

7_volta_tab_options.PNG

For those people who care about their javascript being compacted, this is what the non-verbose equivalent looked like, which has no comments or unnecessary whitespace - though with all the qualified type name strings sprinkled everywhere I think the javascript's always going to be a little weighty, and I do wonder if perhaps they couldn't eliminate the need for a lot of them in the non-verbose javascript via some variables being introduced at the top - just look how many times the "System.String" literal is sprinkled around in this small class.

var CurrentAssembly = Assemblies["VoltaPrelude, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"];CurrentAssembly.TypeDefs["Microsoft.LiveLabs.Volta.JavaScript.Global"] = (function(){

var Ret = {};var Assembly = CurrentAssembly;var oA = Assembly.References.cA();var kA = GetTypeDef(oA, "System.Object");var kB = GetTypeDef(oA, "System.Object");var lA = GetTypeRef(oA, "System.String");var lB = GetTypeRef(Assembly, "Microsoft.LiveLabs.Volta.JavaScript.Object");var lC = GetTypeRef(TypeParameters, "T");var lD = GetTypeRef(oA, "System.Double");var lE = GetTypeRef(oA, "System.Boolean");var lF = GetTypeRef(oA, "System.Int32");var lG = GetTypeRef(Assembly, "Microsoft.LiveLabs.Volta.JavaScript.Function");var lH = GetTypeRef(Assembly, "Microsoft.LiveLabs.Volta.JavaScript.Arguments");var lI = GetTypeRef(Assembly, "Microsoft.LiveLabs.Volta.JavaScript.Global");var lJ = GetTypeRef(oA, "System.Object");var lK = GetTypeRef(oA, "System.Object");var lL = GetTypeRef(oA, "System.Void");var mA = GetMethodDef(kA, "ToString", [lA]);var mB = GetMethodDef(kB, "Equals", [lJ, lE]);var mC = GetMethodDef(kB, "GetHashCode", [lF]);var mD = GetMethodDef(kA, "Finalize", [lL]);var nA = GetMethodRef(lK, "ToString", [lA]);var nB = GetMethodRef(lK, "Equals", [lJ, lE]);var nC = GetMethodRef(lK, "GetHashCode", [lF]);var nD = GetMethodRef(lK, "Finalize", [lL]);var Methods = {};Methods["eEI"] = function(iDV){

var rv = decodeURI(iDV);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.String");}return rv;};Methods["eEJ"] = function(iDW){

var rv = decodeURIComponent(iDW);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.String");}return rv;};Methods["eEK"] = function(iDX){

var rv = encodeURI(iDX);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.String");}return rv;};Methods["eEL"] = function(iDY){

var rv = encodeURIComponent(iDY);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.String");}return rv;};Methods["eEM"] = function(iDZ){

var rv = escape(iDZ);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.String");}return rv;};Methods["eEN"] = function(iD0){

var rv = (function(code){

eval("var temp = " + code);return temp;})(iD0);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly, "Microsoft.LiveLabs.Volta.JavaScript.Object");}return rv;};Methods["eEO"] = (function(dB){

return function(iD1){

var rv = (function(code){

eval("var temp = " + code);return temp;})(iD1);if (rv != null && rv._vT == null) {

rv._vT = dB;}return rv;};});Methods["eEP"] = function(){

var rv = Infinity;if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.Double");}return rv;};Methods["eEQ"] = function(iD2){

var rv = isFinite(iD2);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.Boolean");}return rv;};Methods["eER"] = function(iD3){

var rv = isNaN(iD3);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.Boolean");}return rv;};Methods["eES"] = function(){

var rv = NaN;if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.Double");}return rv;};Methods["eET"] = function(iD4){

var rv = parseFloat(iD4);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.Double");}return rv;};Methods["eEU"] = function(iD5){

var rv = parseInt(iD5);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.Double");}return rv;};Methods["eEV"] = function(iD6, iD7){

var rv = parseInt(iD6, iD7);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.Double");}return rv;};Methods["eEW"] = function(iD8){

var rv = (function(item){

return typeof item;})(iD8);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.String");}return rv;};Methods["eEX"] = function(iD9, iEA){

var rv = (function(item, typeConstructor){

return item instanceof typeConstructor;})(iD9, iEA);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.Boolean");}return rv;};Methods["eEY"] = function(iEB, iEC){

var gE;var $next;$next = 0;while (true) switch($next){case 0:

{

gE = GetTypeDef(Assembly, "Microsoft.LiveLabs.Volta.JavaScript.Function").Methods.eED(iEC);var br1 = gE;if (br1 || br1 === "") {

$next = 12;continue; }return 0;$next = 12;}case 12:

{

return Methods.eEX(iEB, gE);}}};Methods["eEZ"] = function(){

var rv = undefined;if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly, "Microsoft.LiveLabs.Volta.JavaScript.Object");}return rv;};Methods["eE0"] = function(iED){

var rv = unescape(iED);if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly.References.cA(), "System.String");}return rv;};Methods["eE1"] = function(){

var rv = arguments;if (rv != null && rv._vT == null) {

rv._vT = GetTypeDef(Assembly, "Microsoft.LiveLabs.Volta.JavaScript.Arguments");}return rv;};Ret["Methods"] = Methods;var VTable = {};VTable[nA] = mA;VTable[nB] = mB;VTable[nC] = mC;VTable[nD] = mD;Ret["VTable"] = VTable;var Parents = {};Parents[lI.Id] = true;Parents[lJ.Id] = true;Ret["Parents"] = Parents;Ret["PublicMethods"] = {};Ret["PublicMethods"]["DecodeURI"] = {};Ret["PublicMethods"]["DecodeURI"][GetSignature([lA, lA])] = "eEI";Ret["PublicMethods"]["DecodeURIComponent"] = {};Ret["PublicMethods"]["DecodeURIComponent"][GetSignature([lA, lA])] = "eEJ";Ret["PublicMethods"]["EncodeURI"] = {};Ret["PublicMethods"]["EncodeURI"][GetSignature([lA, lA])] = "eEK";Ret["PublicMethods"]["EncodeURIComponent"] = {};Ret["PublicMethods"]["EncodeURIComponent"][GetSignature([lA, lA])] = "eEL";Ret["PublicMethods"]["Escape"] = {};Ret["PublicMethods"]["Escape"][GetSignature([lA, lA])] = "eEM";Ret["PublicMethods"]["Eval"] = {};Ret["PublicMethods"]["Eval"][GetSignature([lA, lB])] = "eEN";Ret["PublicMethods"]["Eval"][GetSignature([lA, lC])] = "eEO";Ret["PublicMethods"]["get_Infinity"] = {};Ret["PublicMethods"]["get_Infinity"][GetSignature([lD])] = "eEP";Ret["PublicMethods"]["IsFinite"] = {};Ret["PublicMethods"]["IsFinite"][GetSignature([lD, lE])] = "eEQ";Ret["PublicMethods"]["IsNaN"] = {};Ret["PublicMethods"]["IsNaN"][GetSignature([lD, lE])] = "eER";Ret["PublicMethods"]["get_NaN"] = {};Ret["PublicMethods"]["get_NaN"][GetSignature([lD])] = "eES";Ret["PublicMethods"]["ParseFloat"] = {};Ret["PublicMethods"]["ParseFloat"][GetSignature([lA, lD])] = "eET";Ret["PublicMethods"]["ParseInt"] = {};Ret["PublicMethods"]["ParseInt"][GetSignature([lA, lD])] = "eEU";Ret["PublicMethods"]["ParseInt"][GetSignature([lA, lF, lD])] = "eEV";Ret["PublicMethods"]["TypeOf"] = {};Ret["PublicMethods"]["TypeOf"][GetSignature([lB, lA])] = "eEW";Ret["PublicMethods"]["InstanceOf"] = {};Ret["PublicMethods"]["InstanceOf"][GetSignature([lB, lG, lE])] = "eEX";Ret["PublicMethods"]["InstanceOf"][GetSignature([lB, lA, lE])] = "eEY";Ret["PublicMethods"]["get_Undefined"] = {};Ret["PublicMethods"]["get_Undefined"][GetSignature([lB])] = "eEZ";Ret["PublicMethods"]["Unescape"] = {};Ret["PublicMethods"]["Unescape"][GetSignature([lA, lA])] = "eE0";Ret["PublicMethods"]["get_Arguments"] = {};Ret["PublicMethods"]["get_Arguments"][GetSignature([lH])] = "eE1";Ret["Assembly"] = CurrentAssembly;Ret["Name"] = "Microsoft.LiveLabs.Volta.JavaScript.Global";Ret["Initializer"] = (function(instance){

return instance;});Ret["TypeInitializer"] = (function(_vT){

});return Ret;})();


At any rate I'm going to wrap it up there for now... work to do, but I have to say for a very early preview Volta is proving suprisingly robust - I really hope in the long term it becomes a product in and of itself, rather then being reabsorbed - to me it's a logical approach to reducing complexity in a lot of what we do today - especially when it comes to embracing the DRY principal.

posted @ Tuesday, December 11, 2007 9:44:49 AM (New Zealand Daylight Time, UTC+13:00)    Comments [0] | Trackback |
As mentioned on both Darryl's and JB's blogs - in February of next year Chris, Darryl & JB will be touring around New Zealand to tell you all about "2008" (SQL 2K8, VS.NET 2K8 & Windows 2K8 that is) and they're getting "locals" to participate in the various centers, and I just happen to be that "Local" for Auckland, though hopefully not too "Local".

So either follow the links above for the full announcement of centers and dates, or if you are local to Auckland then just click on the following link and sign yourself up for the session on Monday February 4th - it's also worth noting that if you both register and turn up (I believe you have to do both ;o) you go into the draw for one of 3 home servers... education and swag.

It should be a lot of fun, so register and come along next year!

Edit: just noticed Chris has also posted, and included some extra details - so take a look!

posted @ Monday, December 10, 2007 11:50:38 PM (New Zealand Daylight Time, UTC+13:00)    Comments [3] | Trackback |
 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)

    {