Architecture Chat #22 This Thursday

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!
Read More

Volta - Type reuse across tiers

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} <--",>
}
}

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...

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:

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 <=>
{
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 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("Text1");
resultsElement = Document.GetById("Results");
button1 = Document.GetById

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...

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:

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

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:

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 surprisingly 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.

Read More

2008 Summer Road Trip

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!

Read More

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.
Read More

Architecture Chat 21

So plenty of subjects today, though a small'ish turn-out, kinda goes with the time of year, and is probably also related to the bar camp Agile down in Wellington tomorrow, which I know at least one of the regulars is heading to.

Code protection and obfuscation was floated by Garreth - sparked off by his need to find a new code protection production, as the product he currently uses doesn't appear to be under development any longer (.Net Reactor I believe was the product).

In fact, it's worse than that - he suspects that the Author may have had an accident or something similar, because the releases stopped a few months back and the Author is no longer replying to questions or queries on the forum, but prior to that was a very active individual - He can't even be contacted by cell phone.  Incidentally if anyone reading this knows of the Author or his fate personally, we'd love to find out more.

Right - so deploying key logic on web-services and some of the alternative licensing products having a requirement of a windows service being installed alongside the product got discussed, with the good and the bad points - it was interesting to see that most of us didn't mind having a windows service installed on our machine to license the product, as long as it was well behaved and made it abundantly clear that this was being done by the product, and was named/described well (so you knew just what it was) - Garreth just
wanted to confirm this with some fellow developers because he's looking into a range of products including PRO-Tector from Nalpeiron that uses this approach, and was worried he might get too much "kick back" from his target customers (i.e. other developers).

From there we moved onto obfuscation products - we've talked about these before, including  Dofuscator and SmartAssembly which came highly recommended from AndrewMindscape.

All of this rounded up with a discussion around "should you even bother obfuscating" and the rationale behind Microsoft obfuscating key portions of their technologies such as SharePoint or Dynamics CRM (i.e. not just protecting IP, but perhaps protecting volatile
and undocumented behaviour which you might leverage or rely on which could be broken easily between service packs for even the same version of the product).

Garreth also recommended Kagi that none of us had heard of - which is a great e-commerce solutions/services provider that goes above and beyond using alternatives like Paypal or local credit card merchants, key differentiating points he made
were:

  • It takes care of varying Tax legislation etc. so you don't personally need to think about collecting VAT etc. - because they act as a "reseller' for your product.
  • Supports non-electronic processing i.e. Cheques, purchase orders, direct debit etc.
  • Entirely customizable UI, can be entirely gutted and made to look like your own site.
  • It understands selling Software and digital content, and provides call backs so you can integrate custom license generation etc.  Within the payment site and provisioning workflow.


It could be just the ticket if your thinking of selling some software and don't want to completely roll your own solution.

Next I ran quickly through the various product announcements etc. over the last 3 weeks - We pretty much bypassed everything and headed straight to Silverlight 2.0 and Volta (VS2008 is old news already, though the Isolated VS2008 Shell got a mention, however most were shocked at the size of the redistributable component - 315MB - that's a big ask if you want to leverage it for a simple product/project).

I was also hoping to get back to talking about the ParalellFX CTP which I've also been playing around with - but never got the opportunity.

So Volta has featured in previous discussions - some of us didn't even imagine it would see the light of day - yet we now have a preview in our
hot little hands which can be played with - I took it for a test drive this morning, and miraculously it works!?

With naught but an attribute I was happily able to move code between my client script and server... We discussed the implications of this, the intended audience (It looks very
appealing to existing win forms developers who have resisted until now from engaging a web platform, because it feels very "familiar").

I'm quite keen to take an existing windows forms application and see how easily I can move from thick client to web client/server using this approach i.e. just how much of the existing code base, event driven structure etc. can be maintained - even in its current
state I think it could do a pretty good job of converting some winforms apps I've built in the past, short of re-implementing complex visual controls.

Things I've still yet to try:

  • Unit testing with NUnit (can't see why it wouldn't work though)
  • Introducing additional tiers i.e. tier splitting between an application server, webservice and client script (not sure this works when using Visual Studio at the moment - the release notes suggest "no", so will probably require some spelunking and command line compilation at this stage.


We talked about Silverlight 2.0, its release date (probably near the end of Q1 2008) - and just what ships and what doesn't i.e. is there a compiler and does it live on the server, extensible control model, 2-way data binding etc.  Certainly Silverlight 2.0 is looking very comprehensive, and the Silverlight team look to have been/are very busy (I wonder how on track the Moonlight team are, considering the surface area of things to develop has grown somewhat from the original 1.1 scope).

Next we talked about DSL's ... what makes a DSL... the stages of DSL'ness from the "barely there" Fluent interface, through to varying grades of internal DSL and finally a custom language which requires parsing into a syntax tree (which F# is great for I think)
and being consumed by your application, or a Visually depicted DSL, ah-la Microsofts DSL SDK for VS.Net.

In the same conversation we also talked about operator overloading, making things explicit, avoiding operators all together (i.e.  "A == B" is far less meaningful in relation to a domain specific language then "A.Equals(B)" or even better "A equals B" - i.e.
being terse may prevent your DSL from being explicit enough to be read by the people who best understand the domain language and the concepts it's modeling to begin with.

We also talked about the things which make a internal DSL work well with languages like Boo i.e. the ability to drop braces, and whitespace awareness (which I hated, but I'm gravitating back towards liking it once more in the right context, because a couple
of spaces is a lot nicer to read then opening/closing braces and associated visual noise - and it's easier to show the raw code to clients when discussing problems because it's less confusing).

Along the same line we talked about operator overloading conventions and on a side note Peter gave Algol a mention for having such wonderful (!) set of operators and needing a special keyboard or a memory for keystroke combos ;o)

?, ?, ?, ?, ?, ?, ?, ??, ?*, %?, ?, ?:=, ?:=, ??:=, ?*:=,
%?:=, ~ ?, ?,?::=,:?:, :,::, ::=...

He also mentioned Postscript, though I don't know much about its operators etc. and didn't really feel like digging up some samples from the red book.  Incidentally F# lets you define new operators:

let ( <:-> ) a b = string.Format("{0} is happy about {1}", a, b) let ( <%=> ) a b = string.Format("{0} is confused by {1}", a, b) let m = "mort" <:-> "user defined operators" let n = "but mort" <%=> "his own code" let result = Console.WriteLine("{0}, {1}", m, n)

Which of course displays:

mort is happy about user defined operators, but mort is confused
by his own code.

From there we branched into discussing multi-language projects - i.e. why do we need to have separate languages in separate assemblies... a lot of us fondly remember the old days of in-line assembler, and would like opportunities to use a language like F#, Boo or one of the DLR based languages in-line within a C# or VB.Net project - i.e. within the bounds of a function/method... I see F# pattern matching as a great example of where this would be really handy - same goes for embedding your own DSL's in-line, or even leveraging a few lines of concise ruby code to do some string manipulation.

I suggested a compiler model much like Boo's would work well in allowing this to happen - though there are issues with just how you compile it, approach merging the separate chunks of IL etc. and cross-referencing.

PDF file parsing got discussed - there is a binary and text based standard (which is largely human readable) - Peter suggested that ABC PDF is very good at pulling apart PDF documents for information extraction in the Enterprise edition, and also supports CMYK colour, useful if you're working with professional printers.  Because there isn't much metadata stored in many cases, you often have to rely on specific style information to identify key bits of content.

We then had a talk about model driven architecture, and in particular, integrating UML models, code and business related information such as device and install inventories, and product life cycle information - we're finally starting to see some traction in New Zealand in this space, and it's ushering in the rise of the true "Enterprise Architect", with people becoming more aware of frameworks for Enterprise Architecture such as Zachman, TOGAF etc. and realizing the true value of driving from an ultimate and largely strategic
high-level organizational model of past, present and future systems (including when certain systems are going to be retired), all the way down to individual applications, business processes, use cases, test scripts/runs, classes, unit tests, machine inventories and even live application data that can be used to be make decisions... even as a develop this has real value, i.e. integrating with a system you could identify how many systems integrate with it, when it's due to retired/replaced/upgraded, maybe how many transactions it's processing, or how often it refreshes itself with key business data from other parts of the organization.   I saw a nice example of this just last week at the  EA Symposium presented by IAG.

It's definitely a subject I'd like to visit again in the future, and perhaps we can get one of the NZ experts in this field to come and participate such as Lukas Svodba (who also runs the arcast.co.nz site) - this also ties in with MDA and MDG, The later of which I'm still having difficulty reconciling with a largely agile test driven development process.

Also on that note I mentioned the JetBrains meta programming system and wondered if it will ever reach a v1 status, I had high hopes for it a couple of years ago.  It doesn't seem to have had much activity associated with it over the last couple of years (I almost wonder if it was largely an Idea which was well before it's time for a lot of the .Net community, that are only now starting to appreciate DSL's and considering working/developing at a higher level of abstraction - or lower level if you look at it from the perspective of the problem your trying to solve i.e. closer the metal of the problem domain itself).

We pretty much wrapped it up there, so that's year 1 of the Architecture Chats brought to a close (we decided against one more for this year, because of the holidays and not to mention our venue being overrun with rampant shoppers, probably removing any chance of finding a park or hearing ourselves think!)

Big thanks to Alex James (Meta me) for getting this chat off the ground, and thanks to all the regulars who come along every fortnight - you've all helped to make the Architecture chat one of the most successful and interesting recurring events I've had the pleasure of attending/participating in - and it's been great to have been able to swap ideas and information over the last year.

Have a merry Xmas and a happy new year one and all!

Read More