Sunday, December 07, 2008

Someone's asked for my hack of JsonWriter from Facebook.NET

Got an email today asking for the source to HackJsonWriter - the code I ripped out of Facebook.NET on Codeplex - as described here.

If you want to include this code in your projects then you probably need to add a comment attributing it back to Facebook.NET -not sure what the open source license is on that project (but it should be OK as you'll be using Facebook.NET anyway)

Also, importantly.... unless you are updating an old project, my advice is do not use Facebook.NET - the FacebookToolkit from Microsoft (also on Codeplex) looks a much better way to go!

Here's the very longwinded code:

using System;

using System.Collections.Generic;

using System.Web;

using System.Text;

using System.Globalization;

using System.Collections;

using System.ComponentModel;

using System.IO;

internal sealed class IndentedTextWriter : TextWriter

{

// Fields

private int _indentLevel;

private bool _minimize;

private bool _tabsPending;

private string _tabString;

private TextWriter _writer;

// Methods

public IndentedTextWriter(TextWriter writer, bool minimize) : base(CultureInfo.InvariantCulture)

{

this._writer = writer;

this._minimize = minimize;

if (this._minimize)

{

this.NewLine = "\r";

}

this._tabString = " ";

this._indentLevel = 0;

this._tabsPending = false;

}

public override void Close()

{

this._writer.Close();

}

public override void Flush()

{

this._writer.Flush();

}

private void OutputTabs()

{

if (this._tabsPending)

{

if (!this._minimize)

{

for (int i = 0; i < this._indentLevel; i++)

{

this._writer.Write(this._tabString);

}

}

this._tabsPending = false;

}

}

public override void Write(bool value)

{

this.OutputTabs();

this._writer.Write(value);

}

public override void Write(char value)

{

this.OutputTabs();

this._writer.Write(value);

}

public override void Write(string s)

{

this.OutputTabs();

this._writer.Write(s);

}

public override void Write(char[] buffer)

{

this.OutputTabs();

this._writer.Write(buffer);

}

public override void Write(double value)

{

this.OutputTabs();

this._writer.Write(value);

}

public override void Write(int value)

{

this.OutputTabs();

this._writer.Write(value);

}

public override void Write(long value)

{

this.OutputTabs();

this._writer.Write(value);

}

public override void Write(object value)

{

this.OutputTabs();

this._writer.Write(value);

}

public override void Write(float value)

{

this.OutputTabs();

this._writer.Write(value);

}

public override void Write(string format, params object[] arg)

{

this.OutputTabs();

this._writer.Write(format, arg);

}

public override void Write(string format, object arg0)

{

this.OutputTabs();

this._writer.Write(format, arg0);

}

public override void Write(char[] buffer, int index, int count)

{

this.OutputTabs();

this._writer.Write(buffer, index, count);

}

public override void Write(string format, object arg0, object arg1)

{

this.OutputTabs();

this._writer.Write(format, arg0, arg1);

}

public override void WriteLine()

{

this.OutputTabs();

this._writer.WriteLine();

this._tabsPending = true;

}

public override void WriteLine(bool value)

{

this.OutputTabs();

this._writer.WriteLine(value);

this._tabsPending = true;

}

public override void WriteLine(char value)

{

this.OutputTabs();

this._writer.WriteLine(value);

this._tabsPending = true;

}

public override void WriteLine(double value)

{

this.OutputTabs();

this._writer.WriteLine(value);

this._tabsPending = true;

}

public override void WriteLine(int value)

{

this.OutputTabs();

this._writer.WriteLine(value);

this._tabsPending = true;

}

public override void WriteLine(char[] buffer)

{

this.OutputTabs();

this._writer.WriteLine(buffer);

this._tabsPending = true;

}

public override void WriteLine(long value)

{

this.OutputTabs();

this._writer.WriteLine(value);

this._tabsPending = true;

}

public override void WriteLine(object value)

{

this.OutputTabs();

this._writer.WriteLine(value);

this._tabsPending = true;

}

public override void WriteLine(float value)

{

this.OutputTabs();

this._writer.WriteLine(value);

this._tabsPending = true;

}

public override void WriteLine(string s)

{

this.OutputTabs();

this._writer.WriteLine(s);

this._tabsPending = true;

}

public override void WriteLine(uint value)

{

this.OutputTabs();

this._writer.WriteLine(value);

this._tabsPending = true;

}

public override void WriteLine(string format, params object[] arg)

{

this.OutputTabs();

this._writer.WriteLine(format, arg);

this._tabsPending = true;

}

public override void WriteLine(string format, object arg0)

{

this.OutputTabs();

this._writer.WriteLine(format, arg0);

this._tabsPending = true;

}

public override void WriteLine(string format, object arg0, object arg1)

{

this.OutputTabs();

this._writer.WriteLine(format, arg0, arg1);

this._tabsPending = true;

}

public override void WriteLine(char[] buffer, int index, int count)

{

this.OutputTabs();

this._writer.WriteLine(buffer, index, count);

this._tabsPending = true;

}

public void WriteLineNoTabs(string s)

{

this._writer.WriteLine(s);

}

public void WriteNewLine()

{

if (!this._minimize)

{

this.WriteLine();

}

}

public void WriteSignificantNewLine()

{

this.WriteLine();

}

public void WriteTrimmed(string text)

{

if (!this._minimize)

{

this.Write(text);

}

else

{

this.Write(text.Trim());

}

}

// Properties

public override Encoding Encoding

{

get

{

return this._writer.Encoding;

}

}

public int Indent

{

get

{

return this._indentLevel;

}

set

{

if (value <>

{

value = 0;

}

this._indentLevel = value;

}

}

public override string NewLine

{

get

{

return this._writer.NewLine;

}

set

{

this._writer.NewLine = value;

}

}

}

///

/// Summary description for HackJsonWriter

///

public class HackJsonWriter

{

// Fields

private Stack _scopes;

private IndentedTextWriter _writer;

// Methods

public HackJsonWriter(TextWriter writer)

: this(writer, true)

{

}

public HackJsonWriter(TextWriter writer, bool minimizeWhitespace)

{

this._writer = new IndentedTextWriter(writer, minimizeWhitespace);

this._scopes = new Stack();

}

public void EndScope()

{

if (this._scopes.Count == 0)

{

throw new InvalidOperationException("No active scope to end.");

}

this._writer.WriteLine();

this._writer.Indent--;

if (this._scopes.Pop().Type == ScopeType.Array)

{

this._writer.Write("]");

}

else

{

this._writer.Write("}");

}

}

internal static string QuoteJScriptString(string s)

{

if (string.IsNullOrEmpty(s))

{

return string.Empty;

}

StringBuilder builder = null;

int startIndex = 0;

int count = 0;

for (int i = 0; i <>

{

char ch = s[i];

if ((((ch == '\r') || (ch == '\t')) || ((ch == '"') || (ch == '\''))) || (((ch == '\\') || (ch == '\r')) || ((ch < ' ') || (ch > '\x007f'))))

{

if (builder == null)

{

builder = new StringBuilder(s.Length + 6);

}

if (count > 0)

{

builder.Append(s, startIndex, count);

}

startIndex = i + 1;

count = 0;

}

switch (ch)

{

case '\'':

{

builder.Append(@"\'");

continue;

}

case '\\':

{

builder.Append(@"\\");

continue;

}

case '\t':

{

builder.Append(@"\t");

continue;

}

case '\n':

{

builder.Append(@"\n");

continue;

}

case '\r':

{

builder.Append(@"\r");

continue;

}

case '"':

{

builder.Append("\\\"");

continue;

}

}

if ((ch < ' ') || (ch > '\x007f'))

{

builder.AppendFormat(CultureInfo.InvariantCulture, @"\u{0:x4}", new object[] { (int)ch });

}

else

{

count++;

}

}

string str = s;

if (builder == null)

{

return str;

}

if (count > 0)

{

builder.Append(s, startIndex, count);

}

return builder.ToString();

}

public void StartArrayScope()

{

this.StartScope(ScopeType.Array);

}

public void StartObjectScope()

{

this.StartScope(ScopeType.Object);

}

private void StartScope(ScopeType type)

{

if (this._scopes.Count != 0)

{

Scope scope = this._scopes.Peek();

if ((scope.Type == ScopeType.Array) && (scope.ObjectCount != 0))

{

this._writer.WriteTrimmed(", ");

}

scope.ObjectCount++;

}

Scope item = new Scope(type);

this._scopes.Push(item);

if (type == ScopeType.Array)

{

this._writer.Write("[");

}

else

{

this._writer.Write("{");

}

this._writer.Indent++;

this._writer.WriteLine();

}

private void WriteCore(string text, bool quotes)

{

if (this._scopes.Count != 0)

{

Scope scope = this._scopes.Peek();

if (scope.Type == ScopeType.Array)

{

if (scope.ObjectCount != 0)

{

this._writer.WriteTrimmed(", ");

}

scope.ObjectCount++;

}

}

if (quotes)

{

this._writer.Write('"');

}

this._writer.Write(text);

if (quotes)

{

this._writer.Write('"');

}

}

public void WriteName(string name)

{

if (string.IsNullOrEmpty(name))

{

throw new ArgumentNullException("name");

}

if (this._scopes.Count == 0)

{

throw new InvalidOperationException("No active scope to write into.");

}

if (this._scopes.Peek().Type != ScopeType.Object)

{

throw new InvalidOperationException("Names can only be written into Object scopes.");

}

Scope scope = this._scopes.Peek();

if (scope.Type == ScopeType.Object)

{

if (scope.ObjectCount != 0)

{

this._writer.WriteTrimmed(", ");

}

scope.ObjectCount++;

}

this._writer.Write("\"");

this._writer.Write(name);

this._writer.WriteTrimmed("\": ");

}

public void WriteValue(bool value)

{

this.WriteCore(value ? "true" : "false", false);

}

public void WriteValue(ICollection items)

{

if ((items == null) || (items.Count == 0))

{

this.WriteCore("[]", false);

}

else

{

this.StartArrayScope();

foreach (object obj2 in items)

{

this.WriteValue(obj2);

}

this.EndScope();

}

}

public void WriteValue(IDictionary record)

{

if ((record == null) || (record.Count == 0))

{

this.WriteCore("{}", false);

}

else

{

this.StartObjectScope();

foreach (DictionaryEntry entry in record)

{

string key = entry.Key as string;

if (string.IsNullOrEmpty(key))

{

throw new ArgumentException("Key of unsupported type contained in Hashtable.");

}

this.WriteName(key);

this.WriteValue(entry.Value);

}

this.EndScope();

}

}

internal static readonly DateTime MinDate;

internal static readonly long MinDateTimeTicks;

// Methods

static HackJsonWriter()

{

DateTime time = new DateTime(0x7b2, 1, 1, 0, 0, 0);

MinDateTimeTicks = time.Ticks;

MinDate = new DateTime(100, 1, 1, 0, 0, 0);

}

public void WriteValue(DateTime dateTime)

{

if (dateTime <>

{

throw new ArgumentOutOfRangeException("dateTime");

}

this.WriteCore(@"\@" + (((dateTime.Ticks - HackJsonWriter.MinDateTimeTicks) / 0x2710L)).ToString(CultureInfo.InvariantCulture) + "@", true);

}

public void WriteValue(double value)

{

this.WriteCore(value.ToString(CultureInfo.InvariantCulture), false);

}

public void WriteValue(int value)

{

this.WriteCore(value.ToString(CultureInfo.InvariantCulture), false);

}

public void WriteValue(object o)

{

if (o == null)

{

this.WriteCore("null", false);

}

else if (o is bool)

{

this.WriteValue((bool)o);

}

else if (o is int)

{

this.WriteValue((int)o);

}

else if (o is float)

{

this.WriteValue((float)o);

}

else if (o is double)

{

this.WriteValue((double)o);

}

else if (o is DateTime)

{

this.WriteValue((DateTime)o);

}

else if (o is string)

{

this.WriteValue((string)o);

}

else if (o is IDictionary)

{

this.WriteValue((IDictionary)o);

}

else if (o is ICollection)

{

this.WriteValue((ICollection)o);

}

else

{

this.StartObjectScope();

foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(o))

{

this.WriteName(descriptor.Name);

this.WriteValue(descriptor.GetValue(o));

}

this.EndScope();

}

}

public void WriteValue(float value)

{

this.WriteCore(value.ToString(CultureInfo.InvariantCulture), false);

}

public void WriteValue(string s)

{

if (s == null)

{

this.WriteCore("null", false);

}

else

{

this.WriteCore(QuoteJScriptString(s), true);

}

}

// Nested Types

private sealed class Scope

{

// Fields

private int _objectCount;

private HackJsonWriter.ScopeType _type;

// Methods

public Scope(HackJsonWriter.ScopeType type)

{

this._type = type;

}

// Properties

public int ObjectCount

{

get

{

return this._objectCount;

}

set

{

this._objectCount = value;

}

}

public HackJsonWriter.ScopeType Type

{

get

{

return this._type;

}

}

}

private enum ScopeType

{

Array,

Object

}

}

Saturday, December 06, 2008

SQL Server - "How do I convert a timespan, in seconds, to HH:MM:SS?"

Google doesn't always have the right answers.

I wanted a quick solution to How do I convert a timespan, in seconds, to HH:MM:SS?
and the hits that came back were all like this one.... most of them from "expert sexchange" types sites (I *hate* expert sexchange!)

However, this answer is just wrong... if you have a time as a float in seconds and you want a datetime object, then use dateadd:

dateadd(ss, @numSeconds, 0)

Glad I got that off my chest!

P.S. Sorry for the lack of blog posts recently - I do actually have loads to blog about!

Thursday, December 04, 2008

Linq continued...

I can't decide whether I'm in love or whether I hate Linq.

It's definitely:
  • much quicker and nicer writing Linq code than it is constructing dynamic sql text
  • much quicker to write Linq rather than to write stored procedures
  • much easier to debug Linq than it is SQL
  • much quicker to use var and anonymous types than it is to use IQueryable and the like...
  • really impressively well supported by the tools - I loved how easy Linq 2 SQL was!
However:
  • the syntax for join's and in clauses (use contains) is quite hard to find...
  • the SQL generated can be quite complicated and slow...
Overall... actually I'm loving it! It's just great discovering things like dragging an SQL scalar function to the Linq2Sql tools and having it then appear in my Linq Intellisense - really cool :)

Wednesday, December 03, 2008

2 more frustrations out the way....

First up I had a problem where I couldn't get IIS7 to work - the returned error was:

IIS Manager Error: The process cannot access the file because it is being used by another process. (Exception from HRESULT: 0x80070020)

I initially assumed this was because I had files open in Visual Studio 2008... but this was wrong - actually the error was caused by skype - it had booted up and had claimed port 80. There are two simple workarounds for this:
  1. Kill Skype
  2. Or move IIS's default website onto some other port (e.g. 8080)
...

Second frustration was.... Visual Studio 2008 SP1 kept exiting. It didn't crash so much as cleanly close down. And it was doing this every single time I opened my website project.

After lots of rooting around to try to work out what was causing it, I stumbled across some complaints about PowerCommands causing VS2008 crashes.... so I went to the Control Panel, uninstalled the PowerCommands for VS2008 (which I think were bundled by Microsoft in SP1?) and yippee - my problem was gone.

...

Beyond these frustrations I've been making lots of headway with XSD and with LINQ - really impressed with the Microsoft tools - must blog more on these soon!

Links to LINQ

I've just added Oakleaf to my list of blogs on the left hand side here - and to my RSS reader.

As well as publishing lots of content himself, this blogger (bloggers?) seems to be doing an excellent job in tracking information about data access in dotnet - about LINQ, about Azure, etc.

Looks like a great resource.

Tuesday, December 02, 2008

The gurus of DotNetNuke

So as I start back on the DNN path after a year away, I've reacquainted myself with some old useful information sources, plus I've found some new ones as well:
  • www.dotnetnuke.com itself is an infuriating resource - it contains some really useful information, and yet it always feels like I won't find it when I'm looking for it.... maybe this is a lesson for me too - when I'm looking at DNN I must remember that search is not the easiest thing to do in DNN land!
  • Michael Washington's DNN help (including his blog items on the main DNN site) is a superb new addition to the DNN scene - thanks!
  • Mitchell Sellers site looks to have changed a lot in the last year, but I remember him as a very helpful, knowledgeable and intelligent contributor - his posts often fitted exactly a problem I was looking at on DNN at the time. I'll add his blog to my RSS reader.
  • www.learnmsnet.com looks very useful - I think it's Chris Blankenship behind the scenes - added to my RSS reader!
  • Nina and team at http://www.xd.com.au/ have good free skins and a great history and overview section too
  • These blog entries from Chris Cant serve a very useful introduction - thanks Chris
  • Somewhere behind this site (http://www.gooddogs.com/) is the main guy behind the very useful DNN repository module.
  • http://www.bi4ce.com/ are behind the DNN Map module (I think)
  • This blog from dnn gallery looks very very useful - I've added it to my reader
  • And dnn gallery itself lists the fact that it uses ventrian.com to achieve it's very nice looking blog - I could be tempted to use that myself! 'smcculloch' is the name I see a lot from here.
  • As alternative menus and indicies for a website house of nuke looks interesting
Note... I intend to keep updating this post...

IIS7, Visual Studio 2008 and Vista - debugging websites

I seem to be having a frustrating setup day today...

Seem to be hitting an endless churn of things to configure before I can actually get on with some work!

The latest problem was an error message from VS2008 which said:
To access local IIS websites, you must install the following IIS components:

IIS6 Metabase and IIS6 Configuration Compatibility
Windows Authentication

...


This was actually a remarkably accurate error message - it just took a while for me to work that out and to work out where to turn them on. This post on iis.net helped - thanks:
Follow these instructions to make these configuration changes:
  1. Windows Features
  2. In Windows Vista, open Control Panel and then click Programs and Features.
  3. In the right pane, click Turn Windows features on or off. The Windows Features dialog box opens.
  4. Select the Internet Information Services check box.
    NOTE: Certain necessary related features will automatically be selected when others are selected
  5. Double-click (or expand) Web Management Tools, double-click IIS 6 Management Compatibility, and then select the IIS 6 Metabase and IIS 6 Configuration Compatibility check box.
  6. Double-click (or expand) World Wide Web Services, double-click Application Development Features, and then select the ASP.NET check box.
  7. Double-click (or expand) Security, and then select the Windows Authentication check box. (this step may not be necessary depending on your security needs).
  8. vii. Click OK

I'm sure I used to be able to use DNN....

For a year or so I used DNN quite a lot, especially in the development of the parkrun website.

This included portal installations and upgrades, skin, container and module development and installation, plus a whole load of custom javascript and css work... There were some painful moments, but generally it was quite straight forward.

However, I've clearly lost these skills - as I've just spent far too long just trying to get a single portal up and running... I think part of the problem has been dotnetnuke, but most of it has been my lack of familiarity with Vista and with IIS7. Plus maybe I was too ambitious - I started trying to install the RC2 of 5, then moved on to the development (source) version, ... and only eventually did I go for the simplest safest installation - the latest 4.09 install (non source) version running within the inetpub folder.

To save you time... try following these instructions to the letter - http://www.adefwebserver.com/DotNetNukeHELP/DNN4_DevelopmentEnvironment/DNNDevelopmentWindowsVista.htm

(And I also note that the same website offers some LINQ2SQL DNN module development tutorials - I may give these a try soon!)

Some current C# test snippets...

While coding, I normally have a spare devenv (VS2008) open with a simple winforms app where I add buttons in order to test little code snippets.

Nothing very exciting, but here are some of my current snippets:

Question: What does Int32.Parse do on string.Empty?

string test = string.Empty;
int i = Int32.Parse(test);

Answer:
FormatException


Question: Debug a regex for friendly urls?

Match m = Regex.Match("http://www.stuff.and.nonsense.com/Tag/the-tag", "Tag/(?.*)");
Debug.WriteLine(m);
Match m2 = Regex.Match("http://www.stuff.and.nonsense.com/Tag/", "Tag/(?.*)");
Debug.WriteLine(m2);
Match m3 = Regex.Match("http://www.stuff.and.nonsense.com/Tag/tagwith/slashes/and.dots", "Tag/(?.*)");
Debug.WriteLine(m3);
Match m4 = Regex.Match("http://www.stuff.and.nonsense.com/Tag/tagwith/Tag/and.Tag/in/it", "Tag/(?.*)");
Debug.WriteLine(m4);

Answer: Looks good


Question: Generate me a machinekey and a decryptionkey for web.config

Answer: (sorry - but I've lost the link I borrowed this from and Google returns a lot of hits for this code - so who knows where it came from originally?!)

int len = 32; //32 for decryptionkey, 64 for machinekey
byte[] buff = new byte[len / 2];
RNGCryptoServiceProvider rng = new
RNGCryptoServiceProvider();
rng.GetBytes(buff);
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < buff.Length; i++)
sb.Append(string.Format("{0:X2}", buff[i]));
Debug.WriteLine(sb);


Question: How do I send mail by smtp?

Answer:
SmtpClient client = new SmtpClient("mail.mydomain.com", 587);
client.UseDefaultCredentials = false;
client.Credentials = new NetworkCredential("donotreply@mydomain.com", "mypassword");
MailMessage msg = new MailMessage("donotreply@mydomain.com", "me@testdomain.com");
msg.Body = "Hello";
msg.Subject = "Test body";
client.Send(msg);

Aside:
There's also an HTML version of this.... uses
msg.Subject = "Test body";
msg.IsBodyHtml = true;


Question:Can I be lazy when using string.Substring?

Answer: No - if you pass in too long a string length, then it will exception (we're not in Python now)

string a = "aaaa";
string b = a.Substring(0, 10); // yup - this causes ArgumentOutOfRangeException


Question: Can I get the name of the current method?

Answer:
StackTrace stack = new StackTrace(true);
Debug.WriteLine(stack.GetFrame(0).GetMethod().Name);

Can also get the class name using:
Debug.WriteLine(stack.GetFrame(0).GetMethod().ReflectedType.FullName);



Question: A less than string for use in one particular LINQ query

(This probably needs a bit of explanation.... it's basically a StartsWith test - I have a whole load of strings like: AdamA, AdamB, AdamC, etc which I'd like to match - the first string I don't want to match is Adan. Also, it's worth noting that I'm working in a specific domain here where I don't care about outofbounds testing - i.e. I know Adam won't be ending with a char 0xFFFF)

Answer:
string lessThanString = compareString.Substring(0, compareString.Length - 1) + (char)(compareString[compareString.Length - 1] + 1);


This is then used in my Azure storage LINQ query as:

IEnumerable query = from c in svc.CreateQuery(_tableName)
where c.RowKey.CompareTo(compareString) >=0 && c.RowKey.CompareTo(lessThanClause) < 0
select c;


That's all for now

Monday, December 01, 2008

Extending facebook.net to work with the latest feed publishing API

When I wrote my first facebook applications last year I used the lovely and very convenient facebook.net platform.

However, facebook has changed a lot since then, and sadly it appears facebook.net isn't keeping up - in fact it looks quite dead as a project...

So I thought I'd have a go at hacking in some new functionality....

In particular, I wanted to extend my application to use the publishUserAction API - especially as the old publish APIs seem to have been deprecated and disabled by facebook.

To do this, I:

  1. Used the Feed Template Console to build a very simple template for me new message.

    • Basically I created a feed template with just a "Primary One Line Template" which was "{*actor*} {*rawText*}"
    • For all other options (short story, full story, etc) I just pressed "skip".
    • In the last step of this console, facebook gave me the template id, let's call this 1234.

  2. Using the excellent (essential!) reflector I broke apart facebook.net - please note, this is open source anyway so I wasn't doing anything naughty here!
    • From the facebook.net dll I took the JsonWriter class - plus I needed to borrow IndentedTextWriter and some constants from JsonReader.
    • From the facebook.net dll I looked at the existing code for FeedService.PublishTemplatedStory and decided to adapt this for my needs.

  3. I created a new method called HackPublishTemplatedStory and wrote it's content as:
    /// #summary
    /// New publish story mechanism - see http://slodge.blogspot.com for details
    /// #/summary
    /// #param name="body"
    /// #returns
    private bool HackPublishStory(string body)
    {
    // see http://wiki.developers.facebook.com/index.php/Feed.publishUserAction
    string method = "feed.publishUserAction";

    if (string.IsNullOrEmpty(body))
    {
    throw new ArgumentNullException("body");
    }

    FacebookRequest request = new FacebookRequest(myFacebookApp.Session);
    request.Parameters["template_bundle_id"] = "1234";

    Hashtable h = new Hashtable();
    h["rawText"] = body;

    StringBuilder sb = new StringBuilder();
    HackJsonWriter tempWriter = new HackJsonWriter(new System.IO.StringWriter(sb), true);
    tempWriter.WriteValue((IDictionary)h);
    request.Parameters["template_data"] = sb.ToString();

    FacebookResponse response = request.InvokeMethod(method);
    return ((response.Status == FacebookResponseStatus.Succeeded) && HackCoerceBoolean(response.Result));
    }
  4. Plus I added some simple code for HackCoerceBoolean:

    internal static bool HackCoerceBoolean(object o)
    {
    if (o is bool)
    {
    return (bool)o;
    }
    if (o is int)
    {
    return (((int)o) == 1);
    }
    if (o is string)
    {
    return (string.CompareOrdinal((string)o, "1") == 0);
    }
    return ((o is ArrayList) && ((bool)((ArrayList)o)[0]));
    }

  5. I could then call this in code using lines as simple as:

    HackPublishStory(string.Format("sent a Christmas Poke to #fb:name uid=\"{0}\" /#", targetFriendId));
    (please excuse the # marks in this line of code - I'm fighting blogger's HTML editor!)

  6. That's it!
If you want to see it in action try Christmas Poke for real on Facebook - although I must stress it was my first ever app! (And I'm sure there are still facebook bugs in there)

last xmas I gave you a poke....

I received an email yesterday which caught me by surprise... it was about a facebook application which I wrote last year - http://apps.facebook.com/christmaspoke/ - this user had tried to use it and found it broken.

To be honest, I'd forgotten all about it and assumed it was just lost forever as a plaything. However, I've just been to look at it and not only has it been used, it's even got it's own (small) fan page :)

The bad news, of course, is that it's not actually working very well any more - as I'm sure most people are aware there have been quite a few changes in facebook during the last year and it seems that many of the APIs I used a year ago are now quite deprecated and severely frowned upon....

I've fixed the app up a little right now... and now I'm also trying to get it 100% working.

Wednesday, November 26, 2008

Some notes for myself - connecting to Nike+

I'm thinking at the moment about extending my Hollybar GPS toy to allow it to talk with Nike+ devices and/or with the Nike+ website.

I've not done any real work on this so far, but here are some of the web hits which look useful:
Just saving these items here in my stream of consciousness - sorry to anyone who has to read it! (plus I need to remember http://coursemapper.com/ for Garmin stuff too)

A fix for the Azure TableStorageMemberProvider

I don't know if this is already known about or solved elsewhere... I couldn't spot it anywhere... but I'll post my fix here anyway - it's pretty trivial.

Problem: in my Azure I tried to implement a simple verification email scheme for user registration using a scheme similar to that described on http://www.aspcode.net/Requiring-email-verification-for-new-accounts.aspx

To do this I had to use the ProviderUserKey for each user.

However, when I looked at this, each user seemed to be coming back with a zero (empty) Guid.

Fix: Looking in the Azure supplied TableStorageMemberProvider, it seems that a providerKey is generated but is never actually stored in the table storage.

To fix this... look in
        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion,
string passwordAnswer, bool isApproved, object providerUserKey,
out MembershipCreateStatus status)

then look down to

if (providerUserKey == null)
{
providerUserKey = Guid.NewGuid();
}
newUser.Password = pass;
newUser.PasswordSalt = salt;
newUser.Email = (email == null) ? string.Empty : email;

and add a line

if (providerUserKey == null)
{
providerUserKey = Guid.NewGuid();
}
#warning Fix implemented for zero'd providerUserKey
newUser.UserId = (Guid)providerUserKey;
newUser.Password = pass;
newUser.PasswordSalt = salt;
newUser.Email = (email == null) ? string.Empty : email;


That should work - it does for me :)

While working on Azure - some ASP.NET stuff covered

So my ASP is a little rusty...

But working on my first Azure website - www.stacka.com - has helped me oil out quite a bit of rustiness.

Among the items covered (so far):
  • General ASP.NET structure - including master pages, pages, server controls, validation, viewstate, code behind, sending emails, using web.config, writing custom HTTPModules ...
  • Some specific ASP.NET nasties:
    • IIS7 integration - spent far too long working out why my HTTPModule wasn't being called in IIS7 - how was I to know all my web.config examples were IIS6 ones?
    • URL rewriting - this MSDN article was especially useful as was Scott Gu's blog - the post back issue took me a day to code around... but eventually it was solved using 2 url rewrites - the first in BeginRequest, the second in PostMapRequestHandler
    • AJAX general things - including some basic tutorials on things like UpdatePanels from asp.net
    • Using the AJAX Control Toolkit - kudos to all who've contributed to this excellent set of tools.
    • Integrating Google Custom Search - really this should have been easy - but a clash of forms meant some very odd errors appeared in my application (bizarrely I was seeing both Ajax and normal submit buttons work in Firefox, but break in IE) - eventually on the way to solving this using Scott Mitchell's notes on dotnetslackers.
    • CSS integration - really, I've given up on this - it's just too hard integrating CSS and using things like the Login control... I'll try again later (maybe I'll use ASP.Net MVC and then I'll not use the standard login code?)
    • Adding email verification to an ASP.Net user registration - easy - see this link on aspcode.net.
  • Some specific Azure issues/challenges:
    • How to live in a data store without JOINs
    • How to keep data consistent without Transactions
    • How to effectively use Partition and Row Keys as data lookup mechanisms
    • How to implement aggregate methods (counts and averages) in the Azure Table storage
    • What to do when optimistic locking is too optimistic (still working on this one!)
    • How to put larger tasks on background threads (worker roles)
    • Some bug fixes in the supplied samples for (for example) TableStorageMembershipProvider - really these samples are a godsend - I'm so glad that Microsoft supplied these!
Will post more details when I get my full invitation token from Microsoft - at that stage hopefully I will be able to upload my webapp and show it off a little...

Saturday, November 22, 2008

An awful name for a very cute coding toy

Went to DDD at Msoft today - one thing that caught my attention was one catchy little number called "Inversion of control" - that's a really dire name, but underneath it's quite cute.

Basically:
  • you take a system like a NetworkConnector which is built using tightly coupled objects - e.g. it references a NetworkMonitor, a Logger and a WirelessAdapter
  • you rewrite it so that the NetworkConnector is an INetworkConnector, and so it uses an INetworkMonitor, an ILogger and an IWirelessAdapter - you then also add a simple constructor so that an external builder can pass each of these references in,
  • then at runtime you can use an "Inversion of control container" object to completely wire your system together. There are a few implementations around - with one from "the Castle project" being the main one people seem to be using. Basically what happens is in your system construction you write code that looks a bit like:
Container theContainer = new Container();
theContainer.Register(INetworkConnector, typeOf(NetworkConnector));
theContainer.Register(ILogger, typeOf(Logger));
// etc ...

then at any time later you can ask theContainer for an instance of some interface - e.g. an INetworkConnector - and theContainer will then create it and it's dependent objects and wire them together... (to do this it uses a lot of reflection under the covers)

I know it may not sound that exciting, but what it means is that it's really quite easy to soft-code the relationships between classes - decoupling the classes and so making them easier to plug-in - especially useful for things like test harnesses - especially when used with Mock objects which seem to have come a long way since NMock (RhinoMock running inside TestDriven.net looked particularly neat).

Friday, November 21, 2008

Visual Studio 2008 SP1 Vista Ultimate Crash - Choose Items.... dialog

It seems this is a common callingrash....

I lived with it for a while.

But have finally found a good workaround - manually install the Power Commands for VS2008 (I think these might have been installed as part of SP1 but reinstalling them certainly does no harm and a lot of good!)

More info and other solutions at http://www.imaginativeuniversal.com/VisualStudio2008SP1ToolboxCrash.aspx

Thursday, November 20, 2008

Azure data challenges

My first app on azure - stacka - is progressing quite well.

However, I keep hitting problems... especially when I try to do things which are very simple and straightforward if I was working with conventional SQL database - but they often prove somewhat more tricky in the distributed Azure storage.

Here's an example.

For stacka, I've got a StackTable structure - within this each StackRow is indexed by:
  • PartitionKey - userid
  • RowKey - a creation tickcount based number - similar to that used by smarx in his ongoing blogger worked example
My thinking behind this was that the partitioning would make it easy to search by user (which is a common task to do) which the row key would ensure that results were returned in time order.

However.... what I hadn't understood was that the sort order returned by the azure table storage is not rowkey - but rather is the tuple (partitionkey, rowkey) with normal lexicographical (alphabetical) ordering defined.

And (unlike in SQL) what I can't now do is reorder the stacks easily in the query - i.e. I cannot simple add a global "" to the query

So....... now I'm looking at:
  • possibly rewriting this indexing, so that the Stacks are not stored with any preference for userid, but so that time based returning is still preserved. This will make it quicker to get the most recent N entities - but will make it slower to get all Stacks associated with one particular user.
  • possibly adding an additional (manually maintained) table which stores the most recent StackRows with a time based ordering. Because this table doesn't need to store everything - just the top N entities - it doesn't need to be particularly large.
There are definitely some interesting patterns that will arise while coding with Azure....

Sunday, November 16, 2008

Azure Invitiation code received :)

Just in case anyone else is waiting, it looks like the invitation lag on Azure at present is about 10 days - I've just got mine - so now I can start publishing Cloud Apps to Microsoft (US) for real.

This is what I just received:

"

Greetings from the Azure Services Platform Team!

Thank you for applying for a Microsoft® .NET Services and Microsoft® SQL Services invitation code on Microsoft Connect Website. (http://go.microsoft.com/fwlink/?LinkID=129453). Your invitation code for Microsoft .NET Services and Microsoft SQL Services will be HIDDEN-FROM-VIEW. Please note that this invitation code is NOT activated yet and cannot be used to create a solution at the portal at this moment. We will send you another email to this account once your invitation code is activated, but we will not send you your invitation code again. Please keep this email in a safe place.

Sincerely,
Jenny Lo
Azure Services Platform Team

"

25th November - Update to this post - what this actually was... was only an invite to the .NET and SQL bits of the Azure CTP preview - so I still can't upload Azure applications :( - most upsetting! Will let you know when the real invite arrives (please let it be soon!)

Friday, November 14, 2008

Outing to cloudcamp


Went along to London cloudcamp last night - http://www.cloudcamp.com.

Very interesting to see and hear a few of the cloud companies - big players like Sun, Amazon, Microsoft, VMWare, ... and smaller companies including http://www.zimory.com, http://www.flexiscale.com, http://www.witsbits.com and many many more besides.

It's clear from listening to a lot of the presentations and comments that this is still really early days for clouds.

However, it's also clear that people have been working in cloud tech for a significant length of time already - this is early tech but it's tech that can and is being used - with Amazon's AWS still the big leader.

I did find one thing particularly interesting - there seemed to be a lot of discussion about green-ness and about the environmental impact of clouds. I'm sure this will still mainly be a driver as an economic influence (i.e. if clouds work out cheaper then that will be the main driver) but it's nice to know that the cloud effect *could* be good for the planet too...

Outside of talking... my own cloud experiments are now mainly Microsoft Azure based. I've been running on a new Vista install and I've got some experimental code up and running. Also, using some recent Google read-ups I've been thinking about some of the ways to get around some of my problems with lack of "count" type operations in the non-SQL cloud - and I really do think offline incremental calculation may be the way... more "soon".

Monday, November 03, 2008

Ongoing Cloud Investigations

September was a busy month.

That may sound like an excuse... and I guess it is. However, my last few weeks of work were very busy, plus I managed to get away at the end of the month - managed to spend some time unwinding on holiday in Cuba.

But I've not forgotten this blog entirely - and now I'm back to give a brief update.


Firstly I've carried on reading about and playing with Google Apps Engine. The limits in the data storage continue to vex me. I kind of like statistics... and most of the web apps I want to write seem to involve data manipulation - including the use of GROUP BY type SQL clauses - and the use of COUNT, MAX, MIN, SUM. Because of the semi-structured nature of Google's BigTable (which are based on very good principles designed to help applications be extremely quick and scalable), these functions simply aren't available. The workarounds I've looked into for these problems are:
  • Calculating all the required COUNT, MAX, MIN, SUM type stats in Python code when required - but these calculations are certainly not ideal - and not really scalable as a solution.
  • Storing all the required stats in special BigTable entries - and updating these stats whenever a web request arrives to add new data, or to update or delete existing data. This is more efficient that the first solution - but requires a lot of additional code to be written and I'm worried it might be a particularly error prone set of code...
  • Storing all the required stats in special BigTable entries - and updating these stats offline using some batch process - but this is quite awkard as the data store API is not particularly friendly for batch processes (e.g. the upper 1000 limit in the number of rows returned although this can be worked around) and because the Apps Enginer does not support batch processes directly - so you have to mimic them yourself using special web hits (but noting that each web hit can only consume at most 3 seconds of processing and that Google might block these requests for reasons of load balancing...)
In short, I'm more and more frequently considering reverting back to non-cloud application models for many of my planned apps.

Secondly, in the last week (while I was away), Microsoft has finally and fully unveiled it's Azure platform - it's answer to Cloud computing - to Google Apps Engine and to Amazon S3 and EC2 services. I've downloaded the Community Technolody Preview of this and got it partially working - good old Microsoft - they're only supporting Vista as a development platform initially! To speed up my curve I've also spent some time watching some of the PDC speeches - especially:
  • Developing and deploying your first service - http://channel9.msdn.com/pdc2008/ES01/
  • Best practices - http://channel9.msdn.com/pdc2008/ES03/
I'm also planning to watch:
  • The keynote at some point soon - given by Don Box who's always good for talking, coding and *most importantly* not using Powerpoint -
  • Some storage tech talks - http://channel9.msdn.com/pdc2008/ES04/ and http://channel9.msdn.com/pdc2008/ES07/
From what I've seen so far... microsoft have really hit the ground running hard here:
  • Their solution looks very polished
  • Their feature set is already more extensive in some places than Google's - e.g. they have provided a queue feature and further have provided facilities for background "worker" processing too.
  • There are a few places, however, where there support is slightly less than finished - e.g. there were questions in some of the tech sessions about lack of secondary data indexing and enterprise developers were already pushing hard for more advanced transaction support and for more private features (how suitable is the cloud for private or enterprise data?)
  • Their tool support is superb - there are some gaps where you have to drop down to command line tools, but already the level of Visual Studio integration is very significant - plus the team are clearly looking to push the newer MVC ASP application pattern into use for the cloud apps.
  • Behind the generic Azure web application hosting service, there are further Microsoft services in production - including the Live services (for user login), and the SQL Data Services which would provide massively scalable (but not massively gigantically BigTable scalable) full SQL data services - which would specifically help with my COUNT, MIN, MAX, AVG stats.
  • The marketing team really are already pushing hard - Microsoft is definitely 100% behind the cloud.
So what does this mean about my direction? Well, actually, having looked at the Microsoft products I am kind of tempted especially as:
  • Having spent so long working on Windows and with Microsoft code and tools, then Visual Studio just feel like "home" and the Intellisense is so much better than Python's in Ellipse.
  • The fact that Microsoft is so new gives me a chance to establish more of an early uniqueness - perhaps a chance to make a bit of a splash/name for myself (although my lack of .NET 3.5 experience, of LINQ. of WCF, of ASP.NET MVC, etc will all leave me quite some way behind at the start.
  • I think there'll be lots of opportunities in both the .NET Cloud and in the Google Cloud (and beyond - Amazon are still a pioneer here, plus I haven't mentioned Rackspace and Mosso yet!) so it doesn't harm to try to use them
However, at a purely functional level I'm not convinced that I'll have any less problems with achieveing functional websites with Microsoft's Cloud solution instead of Google's - I think both are fairly similar and will show similar problems.

And, of course, I'm also very disappointed that the development tools for the Microsoft Cloud aren't yet Windows XP compatible...