According to a group in the US, the price of gold has ben kept artificially low for the last few years.  This would be good news for us :-)  Linkage



Awesome article from Matthew Hurst, who works at Microsoft Live Labs. 


So I wrote most of the display engine for the new blog system last night and today.  Visually it looks exactly as it did before, but the technology behind it is different.  I am now faced with an interesting decision though.  I have the new engine written in LINQ as I said in the previous post, but I'm wondering if this is the most efficient way of doing it.  I have no idea of the memory and processor footprint of LINQ, nor do I know how its performance compares against ADO.Net.  These are things that'll have to be figured out soon enough I guess.  So, on to the code :

In the previous post you saw that I used a LINQ expression to query the data from the SQL Server.  At the end of that code I kept the connection.  Its used now to get the contents of the blog as below.  Again, it is terribly easy stuff, no real thought required (except for syntax as writing C# from scratch is new to me).

   1: // Get the data from the database using LINQ
   2: var topTen =
   3:     (from bl in lpcsql.Blogs
   4:      where bl.PostType == 1
   5:      orderby bl.ID descending
   6:      select bl).Take(10);
   7: // Bind data to the repeater.
   8: objBlog.DataSource = topTen;
   9: objBlog.DataBind();
  10: // Check if its me, and if so activate the Edit and Delete controls
  11: if (User.IsInRole("Administrators"))
  12:     {
  13:     for (int post=0; post < objBlog.Items.Count; post++)
  14:         {
  15:             Label lbl = (Label)objBlog.Items[post].FindControl("EditControl");
  16:             if (lbl.Visible == false)
  17:                 lbl.Visible = true;
  18:             if (lbl.Enabled == false)
  19:                 lbl.Enabled=true;
  20:         }
  21:     }
  22: lpcsql.Connection.Close();

And that's the bulk of the LPC display engine.  If you can see a better / more efficient / cleaner way of doing it then please let me know.  I'm new to this language :-)


So as well as getting a more in-depth look at C#, I've also started looking at LINQ.  One word sums it up - wow! LINQ stands for "Language INtegrated Query" and it is basically a way of extending programming languages by providing them with classes to natively deal with queries.  So, instead of building a SqlConnection, SqlDataAdapter, SqlCommand you simply use a native command to get the data.  Its easier to show you than to describe, so I'll do just that.  The following is the original VB.Net code to pull out the links on the left of the page for previous entries.  It runs a stored procedure to get the data back and then pumps that out to a dataset. For brevity there are about fifty lines of code missing, but this is as small as I could cut it down...

   1: Dim sqlConn As ConnectionStringSettings
   2: sqlConn = ConfigurationManager.ConnectionStrings("SQLServer")
   3: Dim objConn As New SqlConnection(sqlConn.ConnectionString)
   4: objConn.Open()
   5: Dim objRecords As New SqlDataAdapter
   6: Dim objCommand As New SqlCommand
   7:  
   8: Dim objSetPrevLinks As New DataSet
   9: Dim objSetBlog As New DataSet
  10: Dim strMonth, strYear, strSQL As String
  11: Try
  12:     strSQL = "exec GetPrevPostCount"
  13:     objCommand = New SqlCommand(strSQL, objConn)
  14:     objRecords.SelectCommand = objCommand
  15:     objRecords.Fill(objSetPrevLinks)
  16:     objPrevEntries.DataSource = objSetPrevLinks
  17:     objPrevEntries.DataBind()
  18: Catch ex As Exception
  19:     BlogFucked.Text = ex.Message
  20: End Try
  21: objConn.Close()

This routine has worked for a couple of years now on the front page of DaveWhite.Net.  Next we'll look at the LINQ version. The first four lines are where the connection string gets setup, but the real work of executing the stored procedure is done on line 8. The stored procedure GetPrevPostCount has been converted to a native C# command.

   1: System.Configuration.Configuration rootWebConfig =
   2:     System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("/csblog");
   3: System.Configuration.ConnectionStringSettings connString;
   4: connString = rootWebConfig.ConnectionStrings.ConnectionStrings["SQLServer"];
   5: try
   6: {
   7:     lpcDataContext lpcsql = new lpcDataContext(connString.ToString());
   8:     objPrevEntries.DataSource = lpcsql.GetPrevPostCount();
   9:     objPrevEntries.DataBind();
  10:     lpcsql.Connection.Close();
  11: }
  12: catch (Exception ex)
  13: {
  14:     FuckedBlog.Text = ex.Message;
  15: }

Now I'm sure there are even better ways to do this - after all this is my first day playing with LINQ.  Still, I have to admit I'm liking it so far.  I have no idea of the speed implications involved yet, but for this site that wouldn't make such a big difference anyway as the audience is so small. 


Over the last few years, I have re-written the software behind this blog several times.  Originally it was done with static HTML, where I updated the text on the page and added new pages when necessary.  That was in March of 99, and maybe for a month or so.  Then I re-wrote it in ASP, using an access database, and then a SQL Server in December 2000. I moved to VB.Net sometime after that, and have gone through versions 1 and 2 of Microsoft's .Net framework.  Now version 3 is out, and I think its time to make a change to a language called C#.  Its one I have been using for quite a while now, but never actually written anything in it from the ground up.  That's going to change though.  You won't notice anything different, as the UI isn't going to change.  The engine will be different though.  More about this as it gets developed, including new code snippits and some new features such as Infocard and also OpenID integration.  Its going to take a while though... while its the next thing to happen here, it also has to take a back seat to my finding a job ;-)


   1: If Not Roles.IsUserInRole("administrator") Then
   2:     Session("AccessDenied") = True
   3:     FormsAuthentication.SignOut()
   4:     Response.StatusCode = 403
   5:     Response.Redirect("/errorpages/401.aspx?page=nothing.aspx")
   6: End If

Going through a complete review, and I still can't find why I used a session variable here.


You'd think that it was safe to sit behind a keyboard and type, right? Well not anymore it would seem. This diagram illustrates the danger of blogging. Be warned, the problem is growing ever bigger. There are two results here, but the problem is growing as we speak. When I checked before posting this warning, we were at 463! Let this be a warning!!!


dangers


While I'm usually very critical (at least publicly) about anything Apple releases, some good may actually come of the iPhone.  It may just move the entrenched carriers enough in the US that the rest of the world gets good phones.  It seems like we've been trolling around with the same phones for a few years now, and that has mainly been because we use mostly the same handsets that are out in the US.  They have for many years been severely limited by the US carriers, who traditionally had complete control over their networks.  The iPhone changed the paradigm however, and now manufacturers and users get to chose what features they want.  This can only be good for Europe too.  A great article on Wired News sums it up very nicely from the American perspective.


OK, so I'm making some small changes to how this site handles posts from external sources.  Specifically how I handle text that comes in from Windows Live Writer, now that it supports proper XHTML.  Now that I look at it, there are actually a bunch of fuckups in this code, and I'll re-write it soon enough.  It doesn't have to be scalable though, and it is one of those pieces of code that is completely one-use only.  If there were any other parameters thrown in to the mix, such as other users, or indeed anything other than THIS blog, then I couldn't do it like this. As I get more done, and this is admittedly a long term project as I have a bunch of other stuff to do beforehand (like find a job!), I will post the new code here too.  So, without further ado, I present my concoction of the Metaweblog API in VP.Net
   1: <%@ WebHandler Language="VB" Class="MetaWeblogAPI" %>
   2:  
   3: Imports System.IO
   4: Imports System.Data
   5: Imports CookComputing.XmlRpc
   6: Imports System.Data.SqlClient
   7: Public Class MetaWeblogAPI
   8:   Inherits XmlRpcService : Implements IHttpHandler
   9:     
  10:   Private Shared Sub Authenticate(ByVal username As String, ByVal password As String)
  11:     ' Check the login and password in the database.
  12:     Dim iResult As Integer
  13:     Dim sqlConn As ConnectionStringSettings
  14:     sqlConn = ConfigurationManager.ConnectionStrings("SQLServer")
  15:     Dim objConn As New SqlConnection(sqlConn.ConnectionString)
  16:     Dim objCommand As SqlCommand
  17:     Dim objParam As SqlParameter
  18:     Try
  19:       objConn.Open()
  20:       objCommand = New SqlCommand("DBAuthenticate", objConn)
  21:       objCommand.CommandType = Data.CommandType.StoredProcedure
  22:       objParam = objCommand.Parameters.AddWithValue("RETURN_VALUE", 8)
  23:       objParam.Direction = Data.ParameterDirection.ReturnValue
  24:       objCommand.Parameters.AddWithValue("@username", username)
  25:       objCommand.Parameters.AddWithValue("@password", password)
  26:       objCommand.ExecuteNonQuery()
  27:       iResult = objCommand.Parameters("RETURN_VALUE").Value
  28:       ' The return value is going to be either -2 for an incorrect password, 
  29:       ' -1 for an incorrect username or the UserID if it validates.
  30:       If (iResult < 0) Then
  31:         If (iResult = -1) Then
  32:           Throw New System.Security.Authentication.InvalidCredentialException("Incorrect UserName, please try again.")
  33:         Else
  34:           Throw New System.Security.Authentication.InvalidCredentialException("Incorrect Password, please try again.")
  35:         End If
  36:       End If
  37:     Catch ex As Exception
  38:       Throw New System.Security.Authentication.AuthenticationException("Authentication Error : " & ex.Message)
  39:     Finally
  40:       objConn.Close()
  41:     End Try
  42:   End Sub
  43:  
  44:   <XmlRpcMethod("blogger.getUsersBlogs")> _
  45:    Public Function getUsersBlogs(ByVal appKey As String, ByVal username As String, ByVal password As String) As XmlRpcStruct()
  46:     ' This is hardcoded to return only the ID of LPC.  As its the only blog on this server, 
  47:     ' it makes sense to do it this way instead of accessing the database.
  48:     Authenticate(username, password)
  49:     Dim rpcstruct As XmlRpcStruct = New XmlRpcStruct
  50:     rpcstruct.Add("blogid", "2")
  51:     rpcstruct.Add("blogName", "LPC")
  52:     rpcstruct.Add("url", "http://davewhite.net")
  53:     Dim datarpcstruct As XmlRpcStruct() = New XmlRpcStruct() {rpcstruct}
  54:     Return datarpcstruct
  55:   End Function
  56:   
  57:   <XmlRpcMethod("metaWeblog.setTemplate")> _
  58:   Public Function setTemplate(ByVal appKey As String, ByVal blogid As String, ByVal username As String, ByVal password As String, ByVal template As String, ByVal templateType As String) As Boolean
  59:     ' Unused - there's only one template
  60:     Authenticate(username, password)
  61:     Throw New System.NotImplementedException("SetTemplate is not implemented")
  62:   End Function
  63:  
  64:  
  65:   <XmlRpcMethod("metaWeblog.getCategories")> _
  66:   Public Function getCategories(ByVal blogid As String, ByVal username As String, ByVal password As String) As XmlRpcStruct()
  67:     ' This returns a list of categories.  Although you don't see them on the front page, they are
  68:     ' actually there.  They're fairly limited and I don't have any real use for them, but I may one day
  69:     ' expand their use.  In looking through this I just found a huge error caused by writing this and then
  70:     ' re-using some of it without re-writing.  Bad Dave, BAD.  To be fixed.
  71:     Authenticate(username, password)
  72:     Dim sqlConn As ConnectionStringSettings
  73:     sqlConn = ConfigurationManager.ConnectionStrings("SQLServer")
  74:     Dim objConnection As SqlConnection
  75:     Dim objRecords As SqlDataReader
  76:     Dim strSQL2 As String = "select count(iID) from Categories"
  77:     objConnection = New SqlConnection(sqlConn.ConnectionString)
  78:     objConnection.Open()
  79:     Dim strSQL As String = "select * from Categories"
  80:     Dim i As Integer = 0
  81:     Dim iRecCount As Integer
  82:     Dim objcommand2 As New SqlCommand(strSQL2, objConnection)
  83:     iRecCount = objcommand2.ExecuteScalar
  84:     Dim categories(iRecCount - 1) As XmlRpcStruct
  85:     Dim objCommand As SqlCommand
  86:     objCommand = New SqlCommand(strSQL, objConnection)
  87:     objRecords = objCommand.ExecuteReader()
  88:     Try
  89:       If Not objRecords.HasRows Then
  90:         Throw New Exception("Oh oh... no categories!")
  91:         Exit Try
  92:       End If
  93:       While objRecords.HasRows AndAlso i < iRecCount
  94:         objRecords.Read()
  95:         Dim rpcstruct As New XmlRpcStruct
  96:         rpcstruct.Add("categoryid", objRecords("iID"))
  97:         rpcstruct.Add("title", objRecords("strCategory"))
  98:         rpcstruct.Add("description", objRecords("strDescription"))
  99:         categories(i) = rpcstruct
 100:         System.Math.Min(System.Threading.Interlocked.Increment(i), i - 1)
 101:       End While
 102:     Catch ex As Exception
 103:       If Not ex.Message = "Invalid attempt to read when no data is present." Then
 104:         Throw New Exception("Error : " & ex.Message)
 105:       End If
 106:     Finally
 107:       objRecords.Close()
 108:       objConnection.Close()
 109:     End Try
 110:     Return categories
 111:   End Function
 112:   
 113:   <XmlRpcMethod("metaWeblog.getRecentPosts")> _
 114:   Public Function getRecentPosts(ByVal blogid As String, ByVal username As String, ByVal password As String, ByVal numberOfPosts As Integer) As XmlRpcStruct()
 115:     ' This is the routine that is called when WLW opens the Recent Posts from the blog.  Firstly we need
 116:     ' to authenticate that the username and password are indeed authorised...
 117:     Authenticate(username, password)
 118:     ' Then open the database and get the last numberOfPosts entries from the table.
 119:     Dim sqlConn As ConnectionStringSettings
 120:     sqlConn = ConfigurationManager.ConnectionStrings("SQLServer")
 121:     Dim objConn As New SqlConnection(sqlConn.ConnectionString)
 122:     objConn.Open()
 123:     Dim objRecords As SqlDataReader
 124:     ' Here we need posts of type 0 and 1, which are Drafts and Published Posts.  There's a type 2 which
 125:     ' is deleted posts, but you never get to see that.
 126:     Dim objCommand As New SqlCommand("select top " & numberOfPosts & " * from Blog where PostType in (0,1) order by ID desc")
 127:     objCommand.Connection = objConn
 128:     objRecords = objCommand.ExecuteReader()
 129:     Dim posts(numberOfPosts - 1) As XmlRpcStruct
 130:     Dim i As Integer = 0
 131:     Try
 132:       While objRecords.Read AndAlso i < numberOfPosts
 133:         ' Loop and build out the XML structure
 134:         Dim rpcstruct As XmlRpcStruct = New XmlRpcStruct
 135:         rpcstruct.Add("title", objRecords("Title"))
 136:         rpcstruct.Add("link", "http://davewhite.net/default.aspx?id=" & objRecords("ID"))
 137:         rpcstruct.Add("description", LPC.Functions.TextFromDBToLiveWriter(objRecords("Text")))
 138:         rpcstruct.Add("dateCreated", LPC.Functions.GetDateTimeforBlogs(objRecords("DateAdded")))
 139:         If objRecords("Guid") Is DBNull.Value Then
 140:           rpcstruct.Add("guid", Guid.NewGuid.ToString)
 141:         Else
 142:           rpcstruct.Add("guid", objRecords("Guid").ToString)
 143:         End If
 144:         rpcstruct.Add("postid", objRecords("ID"))
 145:         rpcstruct.Add("author", "Dave White")
 146:         posts(i) = rpcstruct
 147:         System.Math.Min(System.Threading.Interlocked.Increment(i), i - 1)
 148:       End While
 149:     Catch ex As Exception
 150:       Throw New Exception("oh oh..." & ex.Message)
 151:     Finally
 152:       objRecords.Close()
 153:       objConn.Close()
 154:     End Try
 155:     Return posts
 156:   End Function
 157:  
 158:   <XmlRpcMethod("metaWeblog.getTemplate")> _
 159:   Public Function getTemplate(ByVal appKey As Stri