I had a number of musings and thoughts that I had written down in various places over the past 6 months, and I wanted to collect them and organize them into some sort of blog form.

So which blog engine should I use? After looking around for a while, I decided to build my own. I know that will surprise and dismay a number of people (including myself) – but hear me out. The reason why I am doing this is because I am *not* a web developer. Wait a minute – why would this make me *more* likely to develop a web application? Because I need to hone my skills of course. Part of being a good developer is working on lots of different types of applications, and frankly it has been quite a while since I have played over in the web world. I could use a standard blog engine, and invent some other sort of project for myself, but why not kill two birds with one stone?

I chose an older more lengthy one as the first entry to port. Which as it turns out may have been a mistake. For this particular entry, I had a large amount of code that I needed to annotate in addition to colorize. I wanted to make it easy to post the entry and have it look like it does inside Visual Studio automatically. After looking around a bit I found a place that had a code control, but not one that enabled you to annotate the code (e.g. highlight certain sections, or cross out certain lines that weren’t needed anymore). I didn’t spend a ton of time looking, because as I mentioned one of the goals was to see what was involved in writing a bigger web application.

The ultimate goal was to paste in a block of code like this:

<code>
class Program
{
	static void Main(string[] args)
	{
		Console.WriteLine("Hello world");
	}
}
</code>

and it would look great when rendered.

However if I wanted to I could talk about how the args parameter wasn’t needed I could indicate this by surrounding the args with a span containing some CSS class, like this:

<code>
class Program
{
	static void Main(<span style="text-decoration: line-through;">string[] args</span>)
	{
		Console.WriteLine("Hello world");
	}
}
</code>

and it would render in the correct coloring, but with a strikeout of the args like this:

class Program
{
	static void Main(string[] args)
	{
		Console.WriteLine("Hello world");
	}
}

In order to support this I had to come up with an easy way to parse/recognize code. I didn’t need a professional grade parser, I just wanted a simple coloring mechanism. Here I decided to use some simple regular expressions to do the trick. These regular expressions are based on a set of keywords read from a config file like so:

	  <add key="C#Keywords" value="#region.*n,#endregion.*n,abstract,event,new,struct,
explicit,null,switch,base,extern,object,this,bool,false,operator,
throw,break,finally,out,true,byte,fixed,override,try,case,float,
params,typeof,catch,private,uint,char,foreach,protected,ulong,
checked,goto,public,unchecked,class,readonly,unsafe,const,
implicit,ushort,continue,return,using,decimal,sbyte,virtual,
default,interface,sealed,volatile,delegate,internal,short,void,
sizeof,while,double,lock,stackalloc,else,long,static,enum,
namespace,string,ref,int,for,if,else if,do,is,in,as"/>

The other requirement that I had (because I chose this initial entry to port) was the need to do the same set of annotations for xml files. This just meant supporting two different languages.

As with most things I was able to get 80% of the functionality in 20% of the time, but the last 20% of the functionality took a while, but here is the final result. It is weird to think about but it is actually colorizing itself :)
[Update: when I switched to using WordPress I also changed any code that did not have bolding or line-through to use SyntaxHighlighter]

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI;
using BrainHz.Blog;

[ParseChildren(true)]
public partial class CodeControl : UserControl
{
	private Regex regex;
	private string[] groupNames;

	string language;
	public string Language
	{
		get { return language; }
		set { language = value; }
	}

	string textContent;
	[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
	[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
	public string Content
	{
		get { return textContent; }
		set { textContent = value; }
	}

	private List<KnownType> knownTypes;
	public List<KnownType> KnownTypes
	{
		get { return knownTypes; }
		set { knownTypes = value; }
	}

	string spanString;

	protected void Page_Load(object sender, EventArgs e)
	{
		spanString = string.Format("(?<{0}></*{0}[^>]*>)|", "span");
		if (language == "csharp")
		{
			regex = CreateCodeRegex();
			groupNames = new string[] {"comment", "quotated", "keyword", "knownType"};
		}

		if (language == "xml")
		{
			regex = CreateXmlRegex();
			groupNames = new string[] { "elementName", "attributeName", "attributeValue" };
		}
	}

	private Regex CreateXmlRegex()
	{
		StringBuilder exp = new StringBuilder(spanString +
			"&lt;/?(?<elementName>[\S]+)|" +
			"(?<attribute>(?<attributeName>\w+)=(&quot;|")(?<attributeValue>[^&"]*)(&quot;|"))"); //+ "|" +
			//"(?<elementName>[\S]+)&gt;");
		return new Regex(exp.ToString());
	}

	/// <summary>
	/// This method creates the regular expression which will be used to
	/// identify special words.
	/// The keywords are read from the application configuration file.
	/// The knownTypes configured per control use
	/// </summary>
	/// <returns>Regex object</returns>
	private Regex CreateCodeRegex()
	{
		StringBuilder expression = new StringBuilder(spanString + "(?<quotated>(\".*\"))|(?<comment>(//.*))");
		string keywords = ConfigurationManager.AppSettings["C#Keywords"];

		string[] splitKeywords = keywords.Split(',');
		string keywordExpression = GetRegexForSpecificWords("keyword", splitKeywords);
		expression.Append(keywordExpression);

		if (knownTypes != null && knownTypes.Count > 0)
		{
			List<string> types = new List<string>();
			foreach (KnownType type in knownTypes)
				types.Add(type.Name);
			string knownTypeExpression = GetRegexForSpecificWords("knownType", types);
			expression.Append(knownTypeExpression);
		}

		return new Regex(expression.ToString());
	}

	private static string GetRegexForSpecificWords(string collectionName, ICollection<string> words)
	{
		if (words == null) return string.Empty;
		if (words.Count == 0) return string.Empty;

		StringBuilder exp = new StringBuilder();
		exp.AppendFormat("|(?<{0}>\b(", collectionName);

		bool needsPipe = false;
		foreach (string s in words)
		{
			if (needsPipe)
				exp.Append("|");
			exp.Append(s);
			needsPipe = true;
		}
		exp.Append("\b))");
		return exp.ToString();
	}

	class CaptureInfo
	{
		public string GroupName;
		public Capture Capture;
		public CaptureInfo(string groupName, Capture capture)
		{
			GroupName = groupName;
			Capture = capture;
		}
	}

	/// <summary>
	/// This method takes an input string from a source file and
	/// outputs the string with the spans and classes.
	/// </summary>
	/// <param name="writer">place to write to</param>
	/// <param name="line">single line of source code</param>
	private void Colorize(HtmlTextWriter writer, string line)
	{
		int idx = 0;
		Match m = regex.Match(line);

		while (m != null && m.Success)
		{
			writer.Write(line.Substring(idx, m.Index - idx));
			idx = m.Index;

			// create a sorted list of captured info
			SortedDictionary<int, CaptureInfo> captures = new SortedDictionary<int, CaptureInfo>();
			foreach (string groupName in groupNames)
			{
				Group group = m.Groups[groupName];
				if (!group.Success)
					continue;
				foreach (Capture cap in group.Captures)
					captures[cap.Index] = new CaptureInfo(groupName, cap);
			}

			foreach (KeyValuePair<int, CaptureInfo> kv in captures)
			{
				string groupName = kv.Value.GroupName;
				Capture cap = kv.Value.Capture;

				if (idx != cap.Index)
				{
					// write any non-formatted stuff
					writer.Write(line.Substring(idx, cap.Index - idx));
					idx = cap.Index;
				}

				writer.AddAttribute(HtmlTextWriterAttribute.Class, groupName);
				writer.RenderBeginTag(HtmlTextWriterTag.Span);
				writer.Write(cap.Value);
				idx += cap.Length;
				writer.RenderEndTag();
			}

			// write out remaining
			writer.Write(line.Substring(idx, m.Index + m.Length - idx));
			idx = m.Index + m.Length;

			m = m.NextMatch();
		}

		writer.Write(line.Substring(idx));
	}

	protected override void Render(HtmlTextWriter writer)
	{
		writer.AddAttribute(HtmlTextWriterAttribute.Class, "code");
		writer.RenderBeginTag(HtmlTextWriterTag.Pre);

		string[] lines = textContent.Split(new string[] {"rn"}, StringSplitOptions.None);
		foreach (string line in lines)
		{
			Colorize(writer, line);
			writer.WriteLine();
		}
		writer.RenderEndTag();
	}
}

I have had the pleasure (or excruciating pain depending on the point of view) of interviewing a lot of candidates for the various technical positions that are open at my company. One of the questions I often ask, “If I have two machines and I want to communicate between them, what are some of my options in .NET?” Far too often when the candidate mentions .NET Remoting as one of the answers, they throw in a side comment like: “although it is an obsolete technology,” or “even though you aren’t supposed to use that anymore,” or “with WCF coming out I believe web services are the preferred method.”

Having played with both .NET Remoting *and* WCF, I fundamentally disagreed with the candidates. I think there is a pretty easy upgrade path between the two. So I set out to prove:

  1. Which technology would be easier to upgrade to WCF?
  2. What are the guidelines that one should follow when choosing a inter-machine communications mechanism (assuming you can’t use WCF, yet)?

I was pre-biased so I guess I should mention that in case I skewed the results in some subconscious way. I felt that the answer to #1 would be a wash, and that #2 would depend solely on things like whether .NET was guaranteed to be running on both endpoints, and whether there was a firewall between the machines.

.NET Remoting

First I created a .NET Remoting solution. I used a Console application for the server, and a Windows forms application for the client, just to make sure that it wasn’t easier to upgrade one versus the other. Per best practices I split the interface into a separate class library which I referenced from both. I also stored the Remoting configuration in the app.config files for both the client and the server.  I chose the standard calculator interface, because that had about the right number of methods (according to Juval Lowy of IDesign, a good number of methods for an interface is 3-5 and if you reflect on .NET itself, that is what you will find). So my interface had the standard Add, Subtract, Multiply and Divide methods.  Once I had that working, I started changing it to use WCF, while taking careful notes of what I had to change.

First things first, I modified the interface by adding the service and operation contract attributes. The interface now looked like this (with new lines in bold):

using System;
using System.ServiceModel;

namespace Calculator
{
    [ServiceContract]
    public interface ICalculator
    {
        [OperationContract]
        int Add(int n1, int n2);
        [OperationContract]
        int Subtract(int n1, int n2);
        [OperationContract]
        int Multiply(int n1, int n2);
        [OperationContract]
        double Divide(int n1, int n2);
    }
}
  Lines Changed Lines Added Lines Removed
here 0 6 0
total 0 6 0

Next on the server I had to change both the config file and the server class itself. I also had to add the ServiceModel project reference again.

using System;
using System.Runtime.Remoting;
using System.ServiceModel;

namespace Calculator
{
    class Program
    {
        static void Main(string[] args)
        {
            RemotingConfiguration.Configure("CalcServer.exe.config", true);
            ServiceHost serviceHost = new ServiceHost(typeof(CalcServer))
            serviceHost.Open();
            Console.WriteLine("Calculator running (press any key to close)");
            Console.ReadKey();
        }
    }
}

The line that actually fetches the configuration information changed from Configure to a line starting the host, something that was implied before.

  Lines Changed Lines Added Lines Removed
here 2 1 0
total 6 7 0

In the server config file the damage was:

<configuration>
  <system.runtime.remoting>
  <system.serviceModel>
    <application name="CalcServer">
    <services>
      <wellknown mode="SingleCall" objectUri="Calc" 
        type="Calculator.CalcServer, CalcServer" />
      <service name="Calculator.CalcServer">
        <endpoint address="net.tcp://localhost:123/CalcServer/Calc"
                  binding="netTcpBinding"
                  contract="Calculator.ICalculator" />
      </service>
      <channels>
        <channel ref="tcp server" port="123" />
      </channels>
    </application>
    </services>
  </system.runtime.remoting>
  </system.serviceModel>
</configuration>

Notice the near 1-1 mapping.

system.runtime.remoting -> system.serviceModel

application -> services

wellknown[@type] -> service[@name]

channel -> endpoint

Actually wellknown and channel morphed into a concept of service and endpoint.  WellKnown is really representing the type of the service being hosted, as is service.  But it includes two other pieces of information: the “mode”, which is described in WCF using a behavior, and the objectUri which is shifted into part of the endpoint address in WCF.  Channel describes the rest of the features present on the endpoint element.  This was a tricky one to count, and I guess this is were my biases come into play.   Because the lines were semantically the same I counted them as changes per the transforms listed above, the add of an end service tag, and the removal of the begin and end channel tags.

  Lines Changed Lines Added Lines Removed
here 6 1 2
total 12 8 2

One more line of code should change on the server side.
The CalcServer object itself also no longer needs to derive from MarshalByRefObject.

public class CalcServer : MarshalByRefObject, ICalculator

This brings the totals to:

  Lines Changed Lines Added Lines Removed
here 1 0 0
total 13 8 2

On to the client. At this point you should be getting the idea.
Almost everything in .NET Remoting has a corresponding object in WCF.

For the client we need to change the buttonAdd_Click event to look like this:

private void buttonAdd_Click(object sender, EventArgs e)
{
    double num1 = double.Parse(textBox1.Text);
    double num2 = double.Parse(textBox2.Text);

    RemotingConfiguration.Configure("CalcClient.exe.config", false);
    WellKnownClientTypeEntry[] entries = RemotingConfiguration.GetRegisteredWellKnownClientTypes();
    ICalculator calc = (ICalculator)Activator.GetObject(typeof(ICalculator), entries[0].ObjectUrl);

    ChannelFactory<ICalculator> factory = new ChannelFactory<ICalculator>("Calc");
    ICalculator calc = factory.CreateChannel();

    double result = calc.Add(num1, num2);
    textBoxResult.Text = result.ToString();
}

Two lines were changed and one was removed. Really the 3 lines in remoting were morphed into the 2 lines of WCF.

  Lines Changed Lines Added Lines Removed
here 2 0 1
total 15 8 3

The client configuration file changed almost exactly like the server one did, other than the endpoint having a name.

<configuration>
  <system.runtime.remoting>
  <system.serviceModel>
    <application name="CalcClient">
    <client>
      <endpoint name="Calc" address="net.tcp://localhost:123/CalcServer/Calc"
                binding="netTcpBinding" contract="Calculator.ICalculator" />
      <wellknown url="tcp://localhost:123/CalcServer/Calc" type="Calculator.ICalculator, CalcInterface" />
      <channels>
        <channel ref="tcp client" />
      </channels>
    </client>
    </application>
  </system.runtime.remoting>
  </system.serviceModel>
</configuration>
  Lines Changed Lines Added Lines Removed
here 4 0 4
total 19 8 7

And there we have it, so a total of (19+8+7) = 34 changes in all…

ASMX Web Services

Most people don’t really use interfaces for WebServices (although it is possible), so I will show the default (although sub-optimal) implementation.

First I need to change the attribute names:

[WebService(Namespace = "urn:brainhz:calc")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ServiceContract]
public class CalcService : System.Web.Services.WebService
{
    [WebMethod]
    [OperationContract]
    public int Add(int n1, int n2)
    {
        return n1 + n2;
    }

    [WebMethod]
    [OperationContract]
    public int Subtract(int n1, int n2)
    {
        return n1 - n2;
    }

    [WebMethod]
    [OperationContract]
    public int Multiply(int n1, int n2)
    {
        return n1 * n2;
    }

    [WebMethod]
    [OperationContract]
    public double Divide(int n1, int n2)
    {
        return (double)n1 / (double)n2;
    }
}

So the question is: does WebService or WebServiceBinding to ServiceContract count as removing and adding or a changed line?  I decided that the intent was not the same, and in fact Indigo has attributes for [ServiceBehavior] to control things like the namespaces.  Then the same question for WebMethod to OperationContract.  Here I decided that the intent was the same (opting in methods) so I counted them as changed lines.  I counted 2 lines removed, one line added and 4 lines changed.

  Lines Changed Lines Added Lines Removed
here 4 1 2
total 4 1 2

Next the config file:

<configuration>
  <system.web>
    ...
  </system.web>
  <system.serviceModel>
    <services>
      <service name="CalcService" behaviorConfiguration="mex" >
        <endpoint address=" " 
          binding="basicHttpBinding" 
          contract="CalcService"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="mex">
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Notice that because we don’t have an interface assembly and therefore can’t add a .NET reference to the assembly, we also have to turn on the metadata exchange in the service behavior.

  Lines Changed Lines Added Lines Removed
here 0 14 0
total 4 15 2

The last thing we need to do is remove the asmx file and replace it with a service file. This too was a tricky one to count.
Technically you are deleting and asmx file and adding a svc file, so I am going to count them as adding a line and removing a line, even though the contents of the file barely changed at all, so if the file was staying the same I might count it differently.

<%@ WebService Language="CSharp" Class="CalcService" CodeBehind="~/App_Code/CalcService.cs" %>
<%@ ServiceHost Language="CSharp" Service="CalcService" CodeBehind="~/App_Code/CalcService.cs" %>

OK so for the service that brings us to

  Lines Changed Lines Added Lines Removed
here 0 1 1
total 4 16 3

And now onto the client…

We need to remove the old reference and add a reference to the new service.  Again I will count this as an add and a remove even though hundreds of lines are changing in the background.

  Lines Changed Lines Added Lines Removed
here 0 1 1
total 4 17 4

For the client we need to change the buttonAdd_Click event to look like this:

using ClientForm.localhost;

private void buttonAdd_Click(object sender, EventArgs e)
{
    double num1 = double.Parse(textBox1.Text);
    double num2 = double.Parse(textBox2.Text);
    CalcService calc = new CalcService();
    CalcServiceClient calc = new CalcServiceClient();
    double result = calc.Add(num1, num2);
    textBoxResult.Text = result.ToString();
}
  Lines Changed Lines Added Lines Removed
here 1 0 0
total 5 17 4

Results

OK, so that brings the comparison to:

  Lines Changed Lines Added Lines Removed
Remoting 18 7 9
WebService 5 17 4

34 to 26. It appears I was wrong – WebServices were a little easier to change.

Of course if there *was* an interface, and you wanted to keep your namespace, that would have brought the totals higher by a couple of lines.

And if you were using ASP.NET features in your web service (which is probable) and you wanted to keep using those in WCF that would have added 3 more lines.
First in the config file

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

Then in code

using System.ServiceModel.Activation;

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]

This would bring the totals to: 34 to 31.

This would put it in the too close to call category, especially considering counting irregularities like the fact the we regenerated web references using asmx web services.

Editorial

Ultimately it is your requirements that dictate whether you go down the .NET Remoting or the ASMX web services route, not the fact the WCF is going to release soon. The real power of WCF is its ability to support both ways of communicating rather than forcing you to pick one over the other. It is really saving you from a later requirements change like, “I know we need the speed for our internal clients but we now need to support an external client as well, and they happen to be using Java.”