Never be clever
When programming, if you don't fully understand some technique, do not use it. And don't mistake intellisense for intelligence. Case in point. I wanted to know whether a particular barcode was contained in a list of barcodes. If not in that list, add to another list: string barcode = "12345"; int index = barcodeList.BinarySearch(barcode); if (index < 0) { otherList.Add(barcode); }
Ooh, binary search. Visual Studio intellisense reports:
List<T>.BinarySearch(T item): Searches the entire sorted List<T> for an element using the default comparer and returns the zero-based index of the comparer.
Sounds good to me. Sign me up!
One teary-eyed debugging session later, as barcodes are being reported NOT found that SHOULD be found, I learn the full truth from MSDN:
BinarySearch() return value is the zero-based index of item in the sorted List, if item is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of Count.
Oh... shazbot.
Why didn't I just use List.Contains?
string barcode = "12345"; if (!barcodeList.Contains(barcode)) { otherList.Add(barcode); }
Because. I am an idiot.
Webpublisher FORCE_CRASH
While perusing the Inmagic Webpublisher 10.00 input schema today (yes, that *is* how I like to spend my leisure time), I saw an undocumented action enumeration I've never seen before. Most actions are self-explanatory, like "query" and "insert". So when I saw this: AC=FORCE_CRASH how long do you think I sat there, resisting temptation? Yeah. A fearful age when measured in nanoseconds. So I hit Webpublisher with FORCE_CRASH a few times to no discernable effect. I was kind of disappointed. Expected sparks to fly out of my hard drive or *something*.
Miraculously clean field names for Inmagic Webpublisher XML
A few months ago I posted a C# routine that replaces diacritic characters with their non-diacritic equivalents. It helped me create valid field names for use with XML requests to Webpublisher, but it wasn't the whole story. There are other commandments that must be followed to get valid field names: spaces shall be replaced with underscores, hyphens shall be replaced with underscores, thou shalt not suffer a digit to start a field name without preceding it with an underscore, etc. Here's the whole deal, wherein you pass in your unclean field names and get purified field names on the other side. public static string[] CleanFieldNames(string[] fields) { string[] output; int len = fields.Length; string exprStartsWithDigit = @"^\d"; List<string> list = new List<string>(); for (int i = 0; i < len; i++) { string field = fields[i]; // Replace spaces with underscores field = field.Replace(" ", "_"); // Replace hyphens with underscores field = field.Replace("-", "_"); // Precede digit at start of field name with underscore if (Regex.IsMatch(field, exprStartsWithDigit)) { field = string.Concat("_", field); } // Replace extended chars with non-diacritic equivalent field = RemoveDiacritics(field); list.Add(field); } output = new string[list.Count]; list.CopyTo(output); return output; } public static string RemoveDiacritics(string input) { string stFormD = input.Normalize(NormalizationForm.FormD); int len = stFormD.Length; StringBuilder sb = new StringBuilder(); for (int i = 0; i < len; i++) { UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(stFormD[i]); if (uc != UnicodeCategory.NonSpacingMark) { sb.Append(stFormD[i]); } } return (sb.ToString().Normalize(NormalizationForm.FormC)); }
N.B. I always adhere to what Inmagic calls "SOAP Format" when making XML requests to Webpublisher. This means I follow one set of rules as regards field names, and don't have to worry about output schema differences between CS Webpublisher and Dbtext Webpublisher. (CS Webpublisher must adhere to SOAP Format but it's optional with Dbtext Webpublisher.)
Tip: comment out textbase locations in dbtwpub.ini
Comment out a textbase location line in dbtwpub.ini by preceding it with an exclamation point. E.g. !catalog=C:\Program Files\Inmagic\WebPubPro\Textbases catalog=D:\Clients\Andornot\Textbases Handy when you want to make a temporary change but want to switch back easy later. UPDATE: Apparently the semicolon is what folks at Inmagic use to comment out lines in INI files, and is considered standard.
Quest for the perfect IM client
Some while ago, shortly after MSN/Windows Live Messenger stopped acting like an IM/chat client and more like a blinkity ad delivery platform*, I decided to look for an alternative. Trillian http://www.ceruleanstudios.com/ Trillian supports AIM, ICQ, MSN, Yahoo Messenger and IRC. It's small and fast and I used it for a year before it finally annoyed me for the last time by not remembering my window preferences. It also tended to lose my MSN password more often than I thought reasonable. Still better than MSN by a long chalk. Pidgin http://pidgin.im/ I just switched to Pidgin this week. It supports *many* (16) protocols including MSN, Yahoo, AIM, ICQ, GoogleTalk, and others I've never heard of, plus it has a fat pigeon icon, which counts for a lot. Pidgin runs on Windows and *nix, and there is a port for OS X. It's an open source project released under the GNU General Public License, and I like to support that kind of initiative. The interface is as fresh and clean as a Spring morning. * Not just the ads. The following pile of of dung is from the Windows Live Messenger home page. Make a difference by getting advertised at? Uh... no. ![CropperCapture[2]](http://www.andornot.com/about/developerblog/uploaded_images/QuestfortheperfectIMclient_925F/CropperCapture2_thumb.png)
A picture is worth 214748360.8 words
Recently Ted put up an awesome Subsonic-powered auto-scaffold of a database so some quick and dirty entry could be done. Since each field has a maximum size, he shows the end user how many characters they have remaining, as they type. Nice! So Bridget and I are going through the scaffold, testing it out, and we come across this Description field: Hmm... only 1 billion characters remaining... give or take 73 million... On a rough guesstimate of 5 characters per word on average, and 70 WPM typing speed, it would take someone, oh, 5.8 years of typing 24 hours a day, 7 days a week, with no holidays or bathroom breaks, to hit that limit. Probably should change the field label to "FULL Description".
Another reason why validation matters.
In a recent article by Chris Maunder of www.codeproject.com, he points out another essential reason to run a validator on your site: Google indexing. (Yes, you might notice that our out-of-date current Andornot site fails...a complete rewrite utilizing Umbraco is actually nearing the finish line - and it does validate). You can read more in the original article (in fact, please do so), but basically, setting the wrong DOCTYPE led to Google reporting the page as not found: The problem we faced was that we had specified HTML 4.01 in our DOCTYPE but were trying to use XHTML style tags (i.e. self closing meta tags). IE had no problem with it. Firefox, Opera and even my blackberry had no problem with it (or if they did have a problem they were polite enough not to say anything). Yahoo didn't have a problem. But Google did. Google saw the DOCTYPE as being HTML 4.01. It then saw meta tags with a trailing "/>". It became scared and confused and decided that the only thing to do was report the page as not found. Our custom error page had no meta tags so it was fine. Our article about templates had <> in the title which caused Googlebot enough confusion that it forgot about the self-closing meta tags and indexed the article. We removed the "/"s from the meta tags and within 24hrs we were reindexed. There were several other issues in addition to the above that they had to remedy as well, but again, to quote Chris: W3C has HTML validators that we knew we failed but there was always a feeling of "so what?" The site rendered perfectly fine on the browsers we had access to. Why should this cause a problem? As the Code Project found out first hand, paying attention to validation errors helps catch so many problems and potential pitfalls. It's not the be-all-and-end-all, but if you mistakenly think validation is just a theoretical exercise with no real-world implications, think again. Labels: validation, W3C
Google is your friend: "Cannot implicitly convert type 'string' to 'System.Web.UI.WebControls.TextBox'"
Fortunately, I didn't waste too much time on this runtime error before googling and finding http://www.wackylabs.net/2005/12/strange-aspnet-20-error/. It turns out a compile time error is not thrown when there's a naming collision between ASP.NET 2.0's Page.Title property and a server-side control given the ID of "Title". Instead, the page in question will build no problem, but won't run. In my case I had given the ID of "Title" to one of a TON of new textboxes on a page I was working on in an ASP.NET 2.0 application. Why this is able to make it past the compiler is beyond me.
A Webpublisher Xml class library and polymorphism
Last fall I wrote a new class library to handle Inmagic Webpublisher XML API calls with elegance and grace. That was the motivation anyway. One might well grasp the wind with chopsticks. Each XML request type (query, insert, update, etc.) has some mandatory elements (e.g. textbase) and some optional elements (e.g. sort fields), so I used the Decorator Pattern to attach optional bits. The pattern provided the solution I was looking for, but threw me roughly into the world of OOP inheritance and polymorphism, where I felt at every turn like a man lost in a thorny thorn field without his pants. I just found a way to remove a particularly sharp and prickly thorn. The problem - Unable to access properties specific to a derived class Consider this snippet of code: // XmlInput is abstract base class, XmlQuery is derived from XmlInput XmlInput query = new XmlQuery(tn, commandQuery, page, mr); // Decorator wraps an XmlInput object query = new SortFieldsDecorator(query, "Name|Product_Number".Split("|".ToCharArray()));
It's classic Decorator. The query object is at all times an XmlInput (the base class) type. You can pass it in to multiple decorators, and because it was instantiated as an XmlQuery, a class that derives from XmlInput, it gets some special XmlQuery-specific behaviour too (e.g. commandQuery, page, mr).
Trouble is, because the query object must be passed to decorators as XmlInput, there is no way to access the XmlQuery-specific public members:
// Cannot access XmlQuery-specific CommandQuery property! This doesn't work: query.CommandQuery = "find blah =blah";
One consequence of this for me was that the derived classes such as XmlQuery had to jam every conceivable property into the constructor as a parameter, because they couldn't be set after the object was initialized. Not so bad with XmlQuery, as it happens, but a nightmare with XmlBatchModify which has many many properties:
// OMG!!! That's not even all of them! public XmlBatchModify(string textbase, string password, ValidationType validation, BatchModifyActionType batchModifyAction, BatchModifyEntryType batchModifyEntry, bool matchCase, bool matchWholeWord) {}
Clearly something had to be done.
The fix? - Casting derived classes to base classes
I can go with the XmlQuery derived class from the get-go, set public properties after the constructor is called, and then cast the query object to the XmlInput base class before passing it to a decorator:
XmlQuery query = new XmlQuery(tn, commandQuery, page, mr); // Now I can access XmlQuery-specific CommandQuery property query.CommandQuery = "find blah =blah"; // Cast query object to XmlInput XmlInput q = (XmlInput) query; // Decorator accepts q = new SortFieldsDecorator(q, "Name|Product_Number".Split("|".ToCharArray()));
This works: properties specific to XmlQuery are not lost. I'm just not sure that it's entirely legal, or what consequences it may have in the long run. Labels: C#, Webpublisher
Webpublisher XML Updates
This truth just hit me with the force of a hammer blow to the forebrain: The Update action in the Webpublisher XML API mimics the Inmagic import function. It is not an update by query. O merciful god in heaven. Import matches on one or more fields which have to be unique (in combination), which generally means a record id. If you don't know in advance which records might be affected you have to do a query to examine the results then perform the update. Two HTTP requests. What I really want is the equivalent of a WHERE clause: update field X for every record found by the query. UPDATE: Wait a minute. There *is* a batch modify option showing in the Webpublisher schema documentation, but not in the online help. The investigation continues...
Virtual Machines, Differencing Disks, and Virtual Goodness
The credit for this post goes to Peter, who close to a year ago sent out instructions on how to use differencing disks in a different way to save significant hard drive space when using virtual machines (Virtual PC 2007), while keeping your pristine baseline image...well...pristine (read-only no less). As I'm tired of looking up the old email every time I forget the steps (going senile in my old age), I'm putting the steps here: - In the VPC console, highlight your VPC > Click Settings > Select Undo Disks > Click Enable > undo disks > Click OK
- Turn on, and then immediately turn off the VPC. Choose to save changes but not commit them. The whole point is to get an Undo disk (file with extension of .vud).
- Find the undo disk (.vud) and rename the VUD extension to VHD. Tricky!
- Return to VPC console, highlight your VPC and choose newly renamed VHD as Hard Disk 1.
- Answer "Continue" when warned about the undo disk.
- In Settings dialog > Select Undo Disks > Clear the check-box to disable undo disks > Click OK (and blissfully ignore the catastrophic warning).
- At file system, set parent disk to 'read-only'.
- Boot VPC and work as normal, but with the knowledge that your pristine baseline image is safe.
- I like to take it one step further for data and hook it up with a separate virtual data drive (easier for incremental backups).
This tip is originally from Invirtus VM Optimizer (now vOptimizer it seems). If you're interested in a further explanation, in their words: Microsoft Differencing Disks As you may have read in our differencing disk article on our website, differencing disks are wonderful tools for extending virtual machines, but are terribly inefficient in disk space concerns. If you intend to use a differencing disk more than a few times and you intend your differencing disks to have a moderate shelf life, consider using undo disks instead. Not in the classic sense, however. To ensure the parent image is held pristine we recommend a new way of using undo disks. Instead of having your writes chained to the back-end of a VHD by way of undo, or VUD, we recommend creating then renaming the VUD to VHD and using it on the front-end to reduce ambiguity. Doing this will save you tons of disk space, guaranteed and you will not lose any functionality. To get there, however, you have to follow a few "new" steps. And of course, do all of the above at your own risk. I'm not responsible for any chaos that ensues: Peter is.
Visual Studio and the non-sensical "Apply Cut Copy commands to blank lines where there is no selection" option.
Quoting some comments in Jeff Atwood's "Revinventing the Clipboard" blog post: What I dislike the most, about the clipboard, is the really bad behaviour in VS. Say you cut something, then you try to past it elsewhere but you hit the C instead of the V (without any text selected) and bravo, you've lost your clipboard content! You now have to undo 3 or 4 times to re-start the manipulation. This is anti-productive at the most. Fabian on January 22, 2008 03:01 AM Fabian: That one gets me too, but the good news is that you can turn off that behaviour in Visual Studio. Go to Tools->Options...->Text Editor->All Languages->General and untick "Apply Cut Copy commands to blank lines when there is no selection" (I guess this goes towards what Jeff was saying about software having reasonable defaults). GrahamStw on January 22, 2008 03:10 AM It's one of those things that I never remember how to reset so here it is. Glory Hallelujah. And btw, using ClipX and loving it (and yes, the beta seems to work fine in Vista). Labels: tips, tools, utilities, Vista, Visual Studio 2005
|