My former position of New Media Manager at MikroBitti Magazine is now open, "only" three months after I left. For you Finnish-reading people, you can check out the job ad.
If you're a technologically competent IT project manager or a lead programmer/architect with interest in the magazine business and no fear for corporate politics and marketing, you might consider it. If you can take the stress and enjoy learning, it just might be fun.
ScottGu of MSDN has resurfaced in the blogging world, giving us two excellent articles on ASP.NET 2.0 development processes. First "Whidbey Update" covering some of the internal metrics and scheduling issues, and then "Testing ASP.NET 2.0 and Visual Web Developer" about the testing procedure and platform in general.
Some of the that stuff is truly well-polished and that testing farm of 1,200 computers is impressive. While none of that is a guarantee for quality, at least there is a tremendous amount of testing effort behind the scenes. And perhaps there's something to be learned from the processes as well (even though most of us don't work with software projects of that size).
I was particularly delighted by Scott's note on reviews: "All code checkins must always be peer-reviewed prior to checking in (this is even true when the most senior developer checks in)." The more experienced I get, the more I believe in peer reviews. For many sorts of issues, good review policy beats (not "replaces", though) many testing mechanisms hands down.
In my world, I cannot avoid writing. Even if I could avoid blogging (hey, I'm becoming pretty good at it at least by view of the entry dates - not that I'd be particularly proud here) I'd still have to write a lot of stuff daily. Email, both in Finnish and in English. Code, of course - but that's not really writing. But then: documentation and meeting minutes, the bane of all IT workers!
Actually, I like writing them both, too. I don't expect anyone else to, but I have to wonder about the lax attitude towards them. Pretty often, I see professional developers, managers, consultants etc. produce really crappy documents. I mean people who I deeply respect because of their technical aptitude or attitude in general. But then, it isn't uncommon to be forced to spend two hours deciphering the vague scribblings from a one-hour meeting. Multiply by the amount of people who have to read the minutes... yes, that's really effective. Really.
Get to the point. Don't aim at excessive brevity; you're writing in a natural language, not C++. Use full sentences and flowing text (paragraphs) instead of bulleted lists with a few words on each line. Use proper headings to guide the reader about the structure of the discussion. Cover backgrounds if they're not obvious. Consider having all the information in an easy referable format (numbered lists or paragraph numbering do fine).
Why don't people spend time teaching themselves skills this basic? Why don't job interviewers ask about this? Why is it acceptable to have "Finnish: mother tongue" in your CV and get away with it, even if that mother tongue may actually mean really sloppy written command of the language? The point of this rant is not about people who can't write. It's about people who don't bother really trying. Documentation and meeting notes is not primarily a challenge for your writing ability, it's a challenge for your desire to your job well.
Need to parse CSV (Comma Separated Values) files in C#? There are many solutions starting from the OLE DB adapter, but here's an easy-to-use CSV Parser written in pure C#: CSVReader.cs. Now, here's a quick tutorial.
First, let's recite the rules of CSV: Each line in a text file represents a record. The fields on each line are separated by commas. If a field starts by a double quote ("), the field ends when the next quote is encountered. If you need to embed a quote inside a quoted field, use a double quote (""). Take for example the next trivial CSV file:
my fields,go,here John said: "Don't move","""I won't"", he replied"
The first line parses into three separate fields ("my fields", "go", "here"). The second one is trickier, but it produces two values. You need to note that the quotes in the first field (John said: "Don't move") do not mean field boundaries. The behavior would be different if a double quote started the field, as it does for the second field ("I won't", he replied). This is why the quotes don't need doubling for the first field.
Now, the CSVReader class can be used to read the file like this:
using (CSVReader csv = new CSVReader(@"c:\myfile.csv")) {
string[] fields;
while ((fields = csv.GetCSVLine()) != null) {
Console.WriteLine("New CSV line begins");
foreach (string field in fields)
Console.WriteLine("CSV field: " + field);
}
}
And as you can guess, the code produces output like this:
New CSV line begins CSV field: my fields CSV field: go CSV field: here New CSV line begins CSV field: John said: "Don't move" CSV field: "I won't", he replied
As usual, feedback and/or bug reports are welcome.
Yet another snippet from my evergrowing code library: How to programmatically upload (possibly multiple) files to an HTML form. I won't post the full code here, but you can download a zip file with a Visual Studio solution including a test app. The upload code is in a separate file (HttpFileUpload.cs), so it's easy to use even without VS.
The HttpFileUpload class provides three methods: UploadFile, UploadByteArray and Upload. The first two call the last one and are just shortcuts to the generic behavior of the upload code. Let's only take a look at the generic method here; the other two are trivial to figure out by reading the in-code documentation.
The Upload method takes four parameters: the target url, a cookie container, credential cache for passing logon information and the objects to be uploaded (as a params array of UploadSpec objects). UploadSpec objects can be constructed by passing either the pathname or the byte array. Note that even with the byte array form, you still have to specify a fictional filename for the receiving end (although leaving it empty isn't usually fatal). Also, you'll have to specify the name of the form field to which the uploaded data should be stuffed into.
An example call:
byte[] someByteData = GetSomeBytes(); HttpFileUpload.Upload( "http://localhost/myupload.cgi", null, null, new HttpFileUpload.UploadSpec(@"c:\windows\win.ini", "file1"), new HttpFileUpload.UploadSpec(someByteData, "myfile.exe", "file2") );
I acknowledge there are still some additional features that might be needed in the future. In case I end up enhancing the library, I'll post an update. Meanwhile, feel free to use the code for whatever needs you have; feedback is naturally welcome.
Edit 2005-03-20: The code has been updated to support form variable posting. See the new post on the subject.
Ooph. A rididculously busy two-week period is turning to its end, and not a single day too early. It's been work, studies and lots of pre-scheduled free-time activity in a fairly rough mixture. I really need the weekend this time.
One thing I'd like to pick and show from the spare hours of the last week: Dundas Gauge was released. I was part of the beta program, so I've been toying around with the product for quite some time already. If you don't already know Dundas, go take a look. They produce data visualization components - first charts and now gauges. The software is from the high end of both the price and quality scale.
As for me, I was direly in need of a spare time coding project. Even though we've already got a load of all sorts of system visualization apps, I just had to write my own. This one has full XML configurability and can bind any sorts of gauges to any sorts of performance counters and other data sources. Ah, if I only had the time to finish it. If I will, you can be certain I'll let you know.
Came across an interesting CodeProject article: a .NET application that takes a database connection string and deletes all information from all tables except those specifically marked with IsStatic attribute. Take a look at
Database Resetter.
The application is not finished yet, and crash on a table's self-reference is pretty serious... but I like the concept. The ability to easily wipe out your test DB and start over again with an empty DB is really valuable at times. Unfortunately for some, it only works on SQL Server.
For some perverted reason, I HAD to try to write the best propercasing algorithm on Earth. This one does all of the following (highlights bolded):
jouni heikniemi -> Jouni Heikniemi
jouni von lederhosen -> Jouni von Lederhosen
THE EYE OF THE TIGER -> The Eye of the Tiger
1250 MHZ -> 1250 MHz
RoNaLD MCDoNaLD, USa -> Ronald McDonald, USA
Enough babble, the code is up next.
// CONFIGURATION:
// The following words will always be in lower case (except in the start of the string)
static string[] lowerCaseWords = { "of", "the", "and", "or", "a", "an", "von" };
// The following prefixes will cause their next character to be uppercased
// Note: Keep the first character uppercase when defining these; all else must be in lowercase
static string[] upperCasePrefixes = { "Mc", "O'" };
// The following words will be always presented in the case they have here.
static string[] fixedCaseWords = { "USA", "NATO", "MHz" };
/// <summary>
/// Converts the given string into ProperCase.
/// </summary>
/// <param name="original">The original string, f.e. "THE EYE OF THE TIGER"</param>
/// <returns>The string converted into ProperCase, f.e. "The Eye of the Tiger"</returns>
public static string ProperCase(string original) {
if (original == null || original.Length == 0) return "";
// Run the original through the massage word-by-word
string result =
Regex.Replace(original.ToLower(), @"\b(\w+)\b", new MatchEvaluator(HandleSingleWord));
// Always uppercase the first character
return Char.ToUpper(result[0]) + (result.Length > 1 ? result.Substring(1) : "");
}
// This helper method properizes (sp?) the case of a single word (regex match)
// NOTE: The input is in all lowercase as forced by the ProperCase method.
private static string HandleSingleWord(Match m) {
string word = m.Groups[1].Value;
// Is this word defined as all-lowercase?
foreach (string lcw in lowerCaseWords)
if (word == lcw)
return word;
// Is this word defined as a fixed-case word?
foreach (string fcw in fixedCaseWords)
if (String.Compare(word, fcw, true) == 0)
return fcw;
// Ok, this is a normal word; uppercase the first letter
if (word.Length == 1)
return Char.ToUpper(word[0]).ToString();
word = Char.ToUpper(word[0]) + word.Substring(1);
// Check if this word starts with one of the uppercasing prefixes
// Note: Only one of the uppercasing prefixes is applies
foreach (string ucPrefix in upperCasePrefixes)
if (word.StartsWith(ucPrefix) && word.Length > ucPrefix.Length)
return word.Substring(0, ucPrefix.Length) +
Char.ToUpper(word[ucPrefix.Length]) +
(word.Length > ucPrefix.Length + 1
? word.Substring(ucPrefix.Length + 1)
: "");
return word;
}
Afterwards, I spotted a tiny programming error. I don't think it's going to be seen in any production application, but it can produce slightly wrong result in a certain situation. Can you spot it?
Starting new tasks (other programs) in .net is actually very easy. You just have to find the Process class, which somewhat surprisingly resides in the System.Diagnostics namespace. Once you're there, a simple call will go far: Process.Start("notepad.exe") does what you'd expect. And because the Windows shell has some built-in logic for various cases, Process.Start("http://www.google.com/") pops up your default browser, too. Now that's cool.
What if you want to run something as if you had typed that into the command interpreter's prompt? The solution is equally easy, you just have to know a couple of things. First, the location (full pathname) of the command interpreter is located in the environment variable COMSPEC. Second, cmd.exe (the default command interpreter) accepts a /c parameter meaning "Run rest of the command line and then exit" - exactly what we want. The following helper method packs up this functionality:
public static void ExecThroughCmdShell(string command) {
System.Diagnostics.Process.Start(
Environment.GetEnvironmentVariable("COMSPEC"),
" /c " + command
);
}
Call the method with syntax like ExecThroughCmdShell("dir /s /p c:\\windows"); to have a new shell window pop up. If you need to wait for the task to return before continuing, the Start method returns a Process object which has a WaitForExit method just for this purpose.
Last week I spent one afternoon listening to Redmondian propaganda at the Microsoft Challenge event (sorry, the site is in Finnish only). The MSDN track was mostly web services - not particularly surprising when considering .NETs fundamentals and the whole Connected Systems hype.
My personal surprise came in when one of the speakers asked the audience about who had used web services for more than a trivial demo application. The result of a quick count was 30% - so that means we have 70% of people who still haven't seriously touched SOAP, WSDL and whatever WS related stuff. I was genuinely surprised - I mean, the event was aimed at ISVs developing for MS platform, mostly even MS Partners. While it would be exaggeration to say the developers in that room were the cream of the Finnish crop, it was a fairly professional bunch with considerable experience in various fields of software development. Microsoft has been pushing hard for web services for quite some time, and we're still seeing numbers like this.
Usually developers grab new technologies as their toys first. RSS is now passing that stage: while it still remains a hip thing to play with, it also has huge number of practical apps. In a few years RSS will be as mundane and pervasive as HTML is now. But despite the long history of SOAP, why isn't playing with web services cool? Despite the fact that basic web services are very easy to create on almost any net-capable platform, most people seem to circle around them. Open source projects rarely contain web service interfaces despite their potential (Bugzilla's lack of them pains me greatly, both in practice and in principle). People write their own blog engines, forum software, image galleries... Yet still, I very rarely see anything that would enable other systems to connect to this brand spanking new application. Hmm.
Is it because WS world is driven by capitalistic entities such as Microsoft, BEA and IBM? Or is it because the amount of WS-* standards is so baffling? Agreed, the hassle around modern web service enhancements has buried the fact that basic SOAP has worked quite well for years already. Stuff like StrikeIron's web services are a small but nice example of what can be easily done with WS.
But I believe in the toy-first propagation model. And despite the multi-billion efforts by huge enterprises, what the world needs is more stuff like QuotesService. Practically useless, but simple enough to give others a starting point.