tag:blogger.com,1999:blog-17921880156599139392024-03-08T20:38:01.397-05:00The Essential Geek“Part of the inhumanity of the computer is that, once it is competently programmed and working smoothly, it is completely honest.” — Isaac AsimovUnknownnoreply@blogger.comBlogger19125tag:blogger.com,1999:blog-1792188015659913939.post-24591813716975485152007-06-07T10:46:00.001-05:002007-06-07T10:55:28.152-05:00Finally, Someone Dares to Say it<p>From an <a title="Is there life out there? Almost definitely, say UK scientists" href="http://www.guardian.co.uk/space/article/0,,2096473,00.html" target="_blank">article</a> highlighted on <a title="Digg Article Discussion" href="http://digg.com/world_news/Is_there_life_out_there_Almost_definitely_say_UK_scientists/blog" target="_blank">Digg</a>: </p> <blockquote> <p>Intelligent extra-terrestrials almost certainly exist on distant planets beyond our solar system, leading British astronomers told the government yesterday.The scientists expect that the first evidence of primitive alien life, such as microbes and vegetation, will emerge within 10 years, with more substantial finds following future space missions.</p></blockquote> <p>I can't believe that anyone with a brain larger than the size of a peanut believes that in a universe the size of ours, we're the only planet with life on it. Statistically speaking, the chances are astronomically remote that we're the only planet that harbors life. (Yeah, that's a cruddy choice of words. Sue me later.) It would be the height of human arrogance to think that our world is the pinnacle of evolution throughout the known universe, and to place ourselves at the top of the evolutionary food chain.</p> <p>But hey, what do you expect from a bunch of talking apes? (Especially a world where the biggest, loudest, most idiotic chimp of all was "elected" the ruler of the most powerful tribe of apes.)</p> <p>Anyway, when you take into account the size of the universe, and the number of worlds that we're starting to find, the chances of finding one with primitive life begin to rapidly increase. Every time we find a world with primitive life, the chances of finding one with more evolved life forms similarly increases. The further away from our world we move, the greater the chances of finding one with life forms similar to or more advanced than our own.</p> <p>Does that mean they'll come zipping across the interstellar spaces in vast fleets of gleaming starcrafts to invade or embrace us? Hell no. They'll be just as constrained by the laws of physics as we are. But they're <em>out there. </em>Somewhere. </p> <p>In my mind, the laws of statistics and probability are every bit as valid as the laws of physics.<br><br><a href="http://www.guardian.co.uk/space/article/0,,2096473,00.html">read more</a> | <a href="http://digg.com/world_news/Is_there_life_out_there_Almost_definitely_say_UK_scientists">digg story</a></p>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-1792188015659913939.post-64751003488586039342007-06-06T10:57:00.001-05:002007-06-06T11:04:05.444-05:00Dew is to Water As Want is to Need<p></p> <p>On today's <a href="http://www.codinghorror.com/blog/archives/000880.html" target="_blank">Coding Horror entry</a>, Jeff Atwood brings up a really interesting point about the power of observing users versus asking them. Paraphrased, what users actually want is typically not what they think they want or tell you they want.</p> <p>It's funny and sad because it's true.</p> <p>It took me a long time to understand the difference between want and need. I may want a nice, tall fizzy bottle of Mountain Dew, but my body needs water. It doesn't need Mountain Dew. Sure, Mountain Dew tastes better, and I like the fizz, and I look way more cool when I'm holding it, but I don't need it. I need water to hydrate my body and keep me alive. There's a big difference. </p> <p>Similarly, when users tell you they want a piece of software that does X, Y, and Z, what they usually need is something that does A and B. (Usually, A and B are something on the order of "It works well" and "It doesn't corrupt my data.")</p> <p>Nonetheless, trying to get users to tell you what they need is akin to extracting molars from a chicken. It's nearly impossible. They'll give you something like this:</p> <ul> <li>It has to look really good. You know, like SILF. (Software I'd Like to @#$%) <li>It has to be fast. Really fast. Like, it has to be so fast that I get whiplash when it starts up. <li>It can't hoard memory. Cuz I'm using Windows 98. In fact, can you make it use no memory at all? <li>It has to be secure. Really secure. Like, Fort Knox secure. Oh, but I want to be able to pass it around on the Internet and share it with all my friends. Or on a USB drive. Or whatever. Ooh! BitTorrent! <li>It has to be a Web app to. With Flash. In fact, do it all in Flash. But I have to be able to use it on my cell phone. And on my XBox. And FireFox. FireFox totally pwnz Micro$oft. <li>Everything has to be done in my company's colors: Black and brown. I want all the text in this really cool dark brown color, and the background all has to be black! It's bitchin'! And flames everywhere! And I have this cool soundtrack I want to play throughout the whole thing! And every button should be a different color, and they should make machine gun sounds when you click them! And then explode with a giant fireball! <li>I've only got a budget of $500.</li></ul> <p>Oh, God. The horror. The humanity!</p> <p>While this list is obviously heavily laden with hyperbole, it's not too far from the truth. A completely user-driven set of application specifications, expressing their <em>wants</em> would likely provide nothing that they <em>needed</em>. At some point, you have to realize that users are, in fact, dreaming about <em>toys</em>.</p> <p>In the end, software is about enabling users to get their work done faster and easier with a minimal amount of hassle. But users don't know that. Largely, they think it's about fun. Not all software is a first-person shooter or a massively multiplayer online role-playing game. (Sadly.) As a member of a software team, it's our job to identify their needs, and create software that meets those needs without getting sidetracked.</p> <p>If you're starting a new system from scratch, don't waste your time <em>asking </em>the users what they want. Ask them what they <em>need</em>. Explain the difference to them. (Start by making sure you understand it yourself.) Explain that everything that isn't needed that is added to a product incurs additional cost and delays the delivery of the product, and that someone has to pay for it.</p> <p>Build systems into your products to monitor which features are being used the most. Don't trust users to tell you. They aren't thinking about it. This kind of information is <em>very </em>useful for determining where to spend your time improving the product. It's also useful for identifying features that you think are critical (in business applications, especially) that should be used, but aren't being used. You can figure out why they aren't being used and target those areas for resolution.</p> <p>If you write a solid subsystem like that for your software, you won't have to rely on user feedback, which isn't always reliable. A solid monitoring system will not lie to you. And that information will help you make much more intelligent decisions in the future. It's a question of silently observing the users as they actively use the product, instead of asking them about it after they've done so. All of us have a pretty short attention span when it comes to software use. I have no idea what the most used commands are in any given software package based on my own usage scenarios. For that reason, you couldn't ask me to tell you what commands or features I use and get a meaningful answer from me.</p> <p>So think about whether or not you <em>need </em>it. If you do, invest the time to do it. But don't do it because you <em>want </em>it. </p> <p>Dew? Or water?</p>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-1792188015659913939.post-79367098028209959402007-04-24T15:08:00.001-05:002007-04-24T15:08:22.148-05:00To Flash, or Not to Flash<p>Considering the design of an all-Flash website? Struggling with the difficult choices involved? Risks got you stumped? Consult <a title="All Flash Website Flowchart" href="http://www.thegooglecache.com/?p=46" target="_blank">this handy chart</a> to help you out. </p> <p>It'll vastly simplify your decision making process!</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1792188015659913939.post-38208094874181023082007-04-10T15:43:00.001-05:002007-04-10T15:46:18.368-05:00Yet Another Amateur Opinion<p>So, here I am, reading <em><a title="The Braidy Tester" href="http://blogs.msdn.com/micahel/default.aspx" target="_blank">The Braidy Tester</a></em>, where I frequently lurk, because, well, I like the way the guy thinks. And recently, he posted an article that I agreed with so I finally mustered up the courage to kick down the closet door and post a response.</p> <p>The basic premise of the article (which you can find <a title="Let's Go Bust Some Silos!" href="http://blogs.msdn.com/micahel/archive/2007/04/04/LetsGoBustSomeSilos.aspx" target="_blank">here</a>) was that we should occasionally entertain the notion of organizing project teams around features instead of skill sets. I agreed. In my response, I said:</p> <blockquote> <p>Going against the grain is scary but sometimes vital in our industry. It takes courage, and always involves risk. But calculated risk isn't always a bad thing.</p> <p>I'm not a fan of blindly adhering to "established best practices," "the Next Big Methodology (TM)," or established team models. Just because something is a best practice at Acme Corporation doesn't mean it's a best practice at Real World Inc. The business model is different, the staffing patterns are different, the problem domains are different, *everything* is different. You have to be willing, at some point, to accept he idea that a best practice for *them* isn't necessarily a best practice for *you.* </p> <p>Be flexible. Do something different. Experiment. Find out what actually works, and makes you more effective. Make the leap. At the end of it all, if you find out that it didn't work, you'll at least come out of the experiment knowing something that you didn't know before. And the acquisition of knowledge is never a wasted effort.</p></blockquote> <p>(Note that that's not a vanity quote. It's done for clarity.)</p> <p>The operative words in that post are "blindly" and "calculated." However, a subsequent poster referred to my opinion as "amateur."</p> <p>Pecker-waving aside, how does one define an "amateur opinion"? I doubt very highly that the poster has any idea what my level of experience is. I also doubt that he carefully read my post, and skipped right over the key words, <em>blindly </em>and <em>calculated.</em></p> <p>For whatever it's worth, I'm always willing to accept the fact that there are an innumerable number of people out there that know far more than I do. They have scads more experience than I do. But the last time I checked, you didn't have to have a license or a degree to have an opinion. </p> <p>So, despite the fact that I apparently lack the credentials to post an opinion, I'll simply refer the reader to the 1st Amendment. And then I'll ask the reader to <em>read </em>what I said, and then <em>think </em>about what I said, and then <em>ask</em> about my experience before labeling me an amateur.</p> <p>I assure you, sir, I am not. I am familiar with the phenomena that have led me to this opinion.</p> <p>I have worked for several companies where a methodology or practice was adopted simply because it was <em>popular at the time; </em>no thought was given to whether or not it was suitable for the environment. This was a failure on the part of management: they should have conducted a proper risk assessment beforehand to determine its feasibility and and suitability. An estimate of the ROI would have been handy. But instead, utter chaos ensued, schedules slipped, projects failed, and employees left because there was a sense that <em>no one knew what the hell was going on and no one was in control. </em></p> <p>Exceptions to the rule? Undoubtedly. But realistic examples nonetheless. It happens. But it shouldn't. </p> <p>Many of the mainstream, established methodologies and best practices are popular and widely implemented because they do, in fact, work. But they don't always work everywhere. I refer you to the old adage, "There is no silver bullet." You have to find the one that works for you; you don't just pick one out of thin air (at least, I hope not): you do your homework, and find one that is suitable for your business model, and maximizes the return on your investment. Maximum return for the least amount of risk.</p> <p>Case in point: at one firm where I've worked, post mortems were ruled out as a bad political move. The development team was so small that the only ones who would provide meaningful input was the <em>customer. </em>Management didn't want the customer critiquing the software development process. Yet a post mortem is a valuable tool, and an established best practice for improving your overall process when it's done right.</p> <p>What about code reviews? If you've only got one developer in your company, they're not feasible because that developer can't be truly trusted to review his own code objectively. Who's going to do it? Are you going to outsource it?</p> <p>Truly small companies can't always adopt methodologies and practices designed to support larger development teams. It doesn't make any sense. Companies with extremely short development cycles can't adopt the methodologies appropriate to those who have the time to implement BUFD, and so forth. You have to be selective. And sometimes, you have to develop a custom methodology that works specifically for your firm, that may entail bits and pieces borrowed from other established methodologies.</p> <p>Trying to shoe-horn your company into a methodology that doesn't fit you will only give you calluses in the most uncomfortable places. I am certainly not advocating that every company in the world should do its own thing. I <em>never </em>once advocated that. What I did do was denounce <em>blind </em>adherence to TNBT and best practices that weren't well-suited to your business model. When the need arises to deviate from what everyone else is doing because what they're doing doesn't work for you, it makes sense to entertain the notion of doing something different. Even then, you should only engage a different way of doing things if the risk involved in doing so is <em>manageable </em>and <em>acceptable.</em></p> <p>But hey, if that kind of an opinion makes me an amateur, so be it. I can live with that.</p> <p align="right">—Mike</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1792188015659913939.post-84085697802323640572007-04-05T13:59:00.001-05:002007-04-05T13:59:09.906-05:00Refactoring Garbage Disposal<p>One of the most common tasks in my data layer is cleaning up my connections, transactions, and data readers. I do it a <em>lot. </em>The established code block for cleaning up a disposable object looks something like this: </p><pre class="code" style="font-size: 8pt"> <span style="color: rgb(0,0,255)">Public</span> <span style="color: rgb(0,0,255)">Sub</span> <span style="color: rgb(0,0,0)">ExecuteUpdate</span>()<br /><br /> <span style="color: rgb(0,0,255)">Dim</span> <span style="color: rgb(0,0,0)">connection</span> <span style="color: rgb(0,0,255)">As</span> <span style="color: rgb(0,0,0)">SqlConnection<br /></span> <span style="color: rgb(0,0,255)">Dim</span> <span style="color: rgb(0,0,0)">transaction</span> <span style="color: rgb(0,0,255)">As</span> <span style="color: rgb(0,0,0)">SqlTransaction<br /></span> <span style="color: rgb(0,0,255)">Dim</span> <span style="color: rgb(0,0,0)">command</span> <span style="color: rgb(0,0,255)">As</span> <span style="color: rgb(0,0,0)">SqlCommand<br /><br /></span> <span style="color: rgb(0,0,255)">Try<br /></span> <span style="color: rgb(0,0,0)">connection</span> = <span style="color: rgb(0,0,255)">New</span> <span style="color: rgb(0,0,0)">SqlConnection<br /></span> <span style="color: rgb(0,0,0)">transaction</span> = <span style="color: rgb(0,0,0)">connection</span>.<span style="color: rgb(0,0,0)">BeginTransaction<br /><br /></span> <span style="color: rgb(0,0,0)">command</span> = <span style="color: rgb(0,0,0)">connection</span>.<span style="color: rgb(0,0,0)">CreateCommand</span>()<br /> <span style="color: rgb(0,0,0)">command</span>.<span style="color: rgb(0,0,0)">CommandType</span> = <span style="color: rgb(0,0,0)">CommandType</span>.<span style="color: rgb(0,0,0)">StoredProcedure<br /></span> <span style="color: rgb(0,0,0)">command</span>.<span style="color: rgb(0,0,0)">CommandText</span> = <span style="color: rgb(128,0,0)">"UpdateSomeData"<br /></span> <span style="color: rgb(0,0,0)">command</span>.<span style="color: rgb(0,0,0)">ExecuteNonQuery</span>()<br /><br /> <span style="color: rgb(0,0,0)">transaction</span>.<span style="color: rgb(0,0,0)">Commit</span>()<br /><br /> <span style="color: rgb(0,0,255)">Catch</span> <span style="color: rgb(0,0,0)">ex</span> <span style="color: rgb(0,0,255)">As</span> <span style="color: rgb(0,0,0)">SqlException<br /></span> <span style="color: rgb(0,0,0)">transaction</span>.<span style="color: rgb(0,0,0)">Rollback</span>()<br /><br /> <span style="color: rgb(0,0,255)">Finally<br /><br /></span> <span style="color: rgb(0,0,255)">If</span> <span style="color: rgb(0,0,255)">Not</span> <span style="color: rgb(0,0,0)">command</span> <span style="color: rgb(0,0,255)">Is</span> <span style="color: rgb(0,0,255)">Nothing</span> <span style="color: rgb(0,0,255)">Then<br /></span> <span style="color: rgb(0,0,0)">command</span>.<span style="color: rgb(0,0,0)">Dispose</span>()<br /> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">If<br /><br /></span> <span style="color: rgb(0,0,255)">If</span> <span style="color: rgb(0,0,255)">Not</span> <span style="color: rgb(0,0,0)">transaction</span> <span style="color: rgb(0,0,255)">Is</span> <span style="color: rgb(0,0,255)">Nothing</span> <span style="color: rgb(0,0,255)">Then<br /></span> <span style="color: rgb(0,0,0)">transaction</span>.<span style="color: rgb(0,0,0)">Dispose</span>()<br /> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">If<br /><br /></span> <span style="color: rgb(0,0,255)">If</span> <span style="color: rgb(0,0,255)">Not</span> <span style="color: rgb(0,0,0)">connection</span> <span style="color: rgb(0,0,255)">Is</span> <span style="color: rgb(0,0,255)">Nothing</span> <span style="color: rgb(0,0,255)">Then<br /></span> <span style="color: rgb(0,0,0)">connection</span>.<span style="color: rgb(0,0,0)">Dispose</span>()<br /> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">If<br /><br /></span> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">Try<br /><br /></span> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">Sub<br /></span></pre><a href="http://11011.net/software/vspaste"></a><br /><p>If you work with a lot of disposable objects (and I'm guessing that most developers do), you get to do a lot of this kind of stuff. The checks for <font face="Consolas" color="#800000">Nothing</font> (<font face="Consolas" color="#800000">null</font> in C#) are mandatory--if you think that a command or transaction object won't ever be null/nothing, boy, are you in for a surprise. Just wait until you try to invoke <font face="Consolas" color="#800000">Dispose</font> on one of those objects and it's not there. </p><br /><p>It didn't take long for me to realize that the <font face="Consolas" color="#800000">Finally</font> block in my data access layer was ripe for refactoring. (The drawback was that much of the initial code was generated by a tool. Sucky part, that. But newer code uses the refactored stuff I'm about to show you.)</p><br /><p>I hate repeating myself. I do. I really, really do. So I looked at that code and decided that I needed a class that would help me to safely dispose of objects. I needed a garbage disposal--kind of like your in-sink erator, where you can simply toss vegetables, egg shells, ice cubes, or whatever suits you, and it safely whisks them down the drain.</p><br /><p>There were numerous questions. Should it be a separate class, or a base class? A base class implementation is problematic. It means that anyone that wants access to the methods needs to derive from it; I could see lots of classes that would want to use it but I didn't want to insert the class into the inheritance chain. </p><br /><p>(Global modules were ruled out right off the bat. Don't even suggest it. Global functions, like global variables, leave a really bad taste in my mouth. Let's not pick hairs about what the compiler does behind my back. Just let it go, okay? Leave me to my AROCCF* ways.)</p><br /><p>I settled on a separate class with <font face="Consolas" color="#800000">Shared</font> (C# <font face="Consolas" color="#800000">static</font>) methods. That way, you could access them on demand, as needed from anywhere. After all, the methods needed to maintain no class state. Everything they need is passed in. </p><br /><p>The resulting class provides four overloads of a single method: <font face="Consolas" color="#800000">DisposeOf</font>. Two of the overloads are type-safe versions designed to provide specific handling for the <font face="Consolas" color="#800000">SqlDataReader</font> and <font face="Consolas" color="#800000">SqlConnection</font> objects, ensuring that they're properly closed before being disposed of. One overload takes a paramarray of <font face="Consolas" color="#800000">IDisposable</font> objects, iterates over it, and invokes the last <font face="Consolas" color="#800000">DisposeOf</font> overload.</p><br /><p>All of the <font face="Consolas" color="#800000">DisposeOf</font> overloads invoke the one basic implementation, which takes an <font face="Consolas" color="#800000">IDisposable</font> parameter. That method simply ensures that its argument isn't null, thereby avoiding a <font face="Consolas" color="#800000">NullReferenceException</font>. If the argument isn't null, it invokes <font face="Consolas" color="#800000">IDisposable</font>.<font face="Consolas" color="#800000">Dispose</font> on it. </p><br /><p>The net effect is that the <font face="Consolas" color="#800000">Finally</font> block is reduced to this:</p><pre class="code"> <span style="color: rgb(0,0,255)">Finally<br /></span> <span style="color: rgb(0,0,0)">Disposer</span>.<span style="color: rgb(0,0,0)">DisposeOf</span>(<span style="color: rgb(0,0,0)">command</span>)<br /> <span style="color: rgb(0,0,0)">Disposer</span>.<span style="color: rgb(0,0,0)">DisposeOf</span>(<span style="color: rgb(0,0,0)">transaction</span>)<br /> <span style="color: rgb(0,0,0)">Disposer</span>.<span style="color: rgb(0,0,0)">DisposeOf</span>(<span style="color: rgb(0,0,0)">connection</span>)<br /> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">Try<br /></span></pre><a href="http://11011.net/software/vspaste"></a><br /><p> Or, in the best-case, scenario, to this:</p><pre class="code"> <span style="color: rgb(0,0,255)">Finally<br /></span> <span style="color: rgb(0,0,0)">Disposer</span>.<span style="color: rgb(0,0,0)">DisposeOf</span>(<span style="color: rgb(0,0,0)">command</span>, <span style="color: rgb(0,0,0)">transaction</span>, <span style="color: rgb(0,0,0)">connection</span>)<br /> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">Try<br /></span></pre><a href="http://11011.net/software/vspaste"></a><br /><p>There is one caveat to using this class to dispose of data access objects: you should always dispose of your transaction and connection last, and <em>always </em>in that order. Then again, I don't think that's due to this class. I think that's just the proper order of disposal. The overloaded version that takes the paramarray disposes of the objects in the order that you pass them.</p><br /><p>If you have other <font face="Consolas" color="#800000">IDisposable</font> objects that you frequently use that require special handling, you can easily extend this class to help you out. This class was built for VB .NET 1.1, since we don't have the <font face="Consolas" color="#800000">using</font> keyword. The class makes it easier to clean up objects that should be cleaned up.</p><br /><p>The code for this class follows below. You are free to take this code and use it or modify it however you wish. You aren't required to mention me, credit me, or even acknowledge that I exist. However, if it blackens your eye, bloodies your nose, or blows your foot off, remember that I don't exist. :)</p><br /><p align="right">—Mike</p><pre class="code" style="font-size: 8pt; background-color: whitesmoke"><span style="color: rgb(0,0,255)">Option</span> <span style="color: rgb(0,0,255)">Strict</span> <span style="color: rgb(0,0,255)">On<br /><br />Imports</span> <span style="color: rgb(0,0,0)">System</span>.<span style="color: rgb(0,0,0)">Data</span>.<span style="color: rgb(0,0,0)">SqlClient<br /><br /></span><span style="color: rgb(0,128,0)">' Provides methods to assist in the safe disposal of objects.<br /></span><span style="color: rgb(0,0,255)">Public</span> <span style="color: rgb(0,0,255)">NotInheritable</span> <span style="color: rgb(0,0,255)">Class</span> <span style="color: rgb(0,0,0)">Disposer<br /><br /></span> <span style="color: rgb(0,0,255)">Private</span> <span style="color: rgb(0,0,255)">Sub</span> <span style="color: rgb(0,0,255)">New</span>()<br /> <span style="color: rgb(0,128,0)">' Prevents instantiation<br /></span> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">Sub<br /><br /></span> <span style="color: rgb(0,128,0)">' Safely disposes of an object. Avoids a <br /></span> <span style="color: rgb(0,128,0)">' NullReferenceException.<br /></span> <span style="color: rgb(0,0,255)">Public</span> <span style="color: rgb(0,0,255)">Shared</span> <span style="color: rgb(0,0,255)">Sub</span> <span style="color: rgb(0,0,0)">DisposeOf</span>(<span style="color: rgb(0,0,255)">ByVal</span> <span style="color: rgb(0,0,0)">item</span> <span style="color: rgb(0,0,255)">As</span> <span style="color: rgb(0,0,0)">IDisposable</span>)<br /> <span style="color: rgb(0,0,255)">If</span> <span style="color: rgb(0,0,255)">Not</span> <span style="color: rgb(0,0,0)">item</span> <span style="color: rgb(0,0,255)">Is</span> <span style="color: rgb(0,0,255)">Nothing</span> <span style="color: rgb(0,0,255)">Then<br /></span> <span style="color: rgb(0,0,0)">item</span>.<span style="color: rgb(0,0,0)">Dispose</span>()<br /> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">If<br /></span> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">Sub<br /><br /></span> <span style="color: rgb(0,128,0)">' Safely disposes of a SqlDataReader. Avoids a <br /></span> <span style="color: rgb(0,128,0)">' NullReferenceException. If the reader is open, <br /></span> <span style="color: rgb(0,128,0)">' it is first closed.<br /></span> <span style="color: rgb(0,0,255)">Public</span> <span style="color: rgb(0,0,255)">Shared</span> <span style="color: rgb(0,0,255)">Sub</span> <span style="color: rgb(0,0,0)">DisposeOf</span>(<span style="color: rgb(0,0,255)">ByVal</span> <span style="color: rgb(0,0,0)">item</span> <span style="color: rgb(0,0,255)">As</span> <span style="color: rgb(0,0,0)">SqlDataReader</span>)<br /> <span style="color: rgb(0,0,255)">If</span> <span style="color: rgb(0,0,255)">Not</span> <span style="color: rgb(0,0,0)">item</span> <span style="color: rgb(0,0,255)">Is</span> <span style="color: rgb(0,0,255)">Nothing</span> <span style="color: rgb(0,0,255)">Then<br /></span> <span style="color: rgb(0,0,255)">If</span> <span style="color: rgb(0,0,255)">Not</span> <span style="color: rgb(0,0,0)">item</span>.<span style="color: rgb(0,0,0)">IsClosed</span> <span style="color: rgb(0,0,255)">Then<br /></span> <span style="color: rgb(0,0,0)">item</span>.<span style="color: rgb(0,0,0)">Close</span>()<br /> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">If<br /></span> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">If<br /></span> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">Sub<br /><br /></span> <span style="color: rgb(0,128,0)">' Safely disposes of a SqlConnection object. Avoids a <br /></span> <span style="color: rgb(0,128,0)">' NullReferenceException. If the connection is opened, <br /></span> <span style="color: rgb(0,128,0)">' it is first closed.<br /></span> <span style="color: rgb(0,0,255)">Public</span> <span style="color: rgb(0,0,255)">Shared</span> <span style="color: rgb(0,0,255)">Sub</span> <span style="color: rgb(0,0,0)">DisposeOf</span>(<span style="color: rgb(0,0,255)">ByVal</span> <span style="color: rgb(0,0,0)">connection</span> <span style="color: rgb(0,0,255)">As</span> <span style="color: rgb(0,0,0)">SqlConnection</span>)<br /> <span style="color: rgb(0,0,255)">If</span> <span style="color: rgb(0,0,255)">Not</span> <span style="color: rgb(0,0,0)">connection</span> <span style="color: rgb(0,0,255)">Is</span> <span style="color: rgb(0,0,255)">Nothing</span> <span style="color: rgb(0,0,255)">Then<br /></span> <span style="color: rgb(0,0,255)">If</span> <span style="color: rgb(0,0,255)">Not</span> <span style="color: rgb(0,0,0)">connection</span>.<span style="color: rgb(0,0,0)">State</span> = <span style="color: rgb(0,0,0)">ConnectionState</span>.<span style="color: rgb(0,0,0)">Closed</span> <span style="color: rgb(0,0,255)">Then<br /></span> <span style="color: rgb(0,0,0)">connection</span>.<span style="color: rgb(0,0,0)">Close</span>()<br /> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">If<br /></span> <span style="color: rgb(0,0,0)">connection</span>.<span style="color: rgb(0,0,0)">Dispose</span>()<br /> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">If<br /></span> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">Sub<br /><br /></span> <span style="color: rgb(0,128,0)">' Safely disposes of an array of disposable objects.<br /></span> <span style="color: rgb(0,0,255)">Public</span> <span style="color: rgb(0,0,255)">Shared</span> <span style="color: rgb(0,0,255)">Sub</span> <span style="color: rgb(0,0,0)">DisposeOf</span>(<span style="color: rgb(0,0,255)">ByVal</span> <span style="color: rgb(0,0,255)">ParamArray</span> <span style="color: rgb(0,0,0)">items</span>() <span style="color: rgb(0,0,255)">As</span> <span style="color: rgb(0,0,0)">IDisposable</span>)<br /> <span style="color: rgb(0,0,255)">For</span> <span style="color: rgb(0,0,255)">Each</span> <span style="color: rgb(0,0,0)">item</span> <span style="color: rgb(0,0,255)">As</span> <span style="color: rgb(0,0,0)">IDisposable</span> <span style="color: rgb(0,0,255)">In</span> <span style="color: rgb(0,0,0)">items<br /></span> <span style="color: rgb(0,0,255)">If</span> <span style="color: rgb(0,0,255)">TypeOf</span> <span style="color: rgb(0,0,0)">item</span> <span style="color: rgb(0,0,255)">Is</span> <span style="color: rgb(0,0,0)">SqlConnection</span> <span style="color: rgb(0,0,255)">Then<br /></span> <span style="color: rgb(0,0,0)">DisposeOf</span>(<span style="color: rgb(0,0,255)">DirectCast</span>(<span style="color: rgb(0,0,0)">item</span>, <span style="color: rgb(0,0,0)">SqlConnection</span>))<br /> <span style="color: rgb(0,0,255)">ElseIf</span> <span style="color: rgb(0,0,255)">TypeOf</span> <span style="color: rgb(0,0,0)">item</span> <span style="color: rgb(0,0,255)">Is</span> <span style="color: rgb(0,0,0)">SqlDataReader</span> <span style="color: rgb(0,0,255)">Then<br /></span> <span style="color: rgb(0,0,0)">DisposeOf</span>(<span style="color: rgb(0,0,255)">DirectCast</span>(<span style="color: rgb(0,0,0)">item</span>, <span style="color: rgb(0,0,0)">SqlDataReader</span>))<br /> <span style="color: rgb(0,0,255)">Else<br /></span> <span style="color: rgb(0,0,0)">DisposeOf</span>(<span style="color: rgb(0,0,0)">item</span>)<br /> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">If<br /></span> <span style="color: rgb(0,0,255)">Next<br /></span> <span style="color: rgb(0,0,255)">End</span> <span style="color: rgb(0,0,255)">Sub<br /><br />End</span> <span style="color: rgb(0,0,255)">Class</span></pre><a href="http://11011.net/software/vspaste"></a><br /><p><font size="1">* AROCCF = Anal Retentive Obsessive Compulsive Control Freak</font></p>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-1792188015659913939.post-57974824961831471562007-04-02T12:46:00.001-05:002007-04-02T12:46:31.864-05:00Refactoring Your Way to Enlightenment<p>Take a moment to stock of where you are now. What skills do you have? How can you improve them? Every day of your career, you should be learning something, improving something, refining something. Your skill set should be undergoing constant refactoring. This can only make you more efficient. If the stuff you're learning isn't making you more efficient, discard it. </p> <p>At some point, you have to have the guts to go against the grain. Just because a "best practice" works for someone else at some other company doesn't necessarily make it a "best practice" for you and your company. A "proven methodology" isn't necessarily going to be a "proven methodology" for you. Have the guts to challenge the status quo. If it's not making you more efficient, it's likely hindering you. Refactor it out. </p> <p>If your team doesn't have the funds to learn some new technique, seek that knowledge personally. There is <em>no reason </em>that your company's inability to fund team education should hold you back. Buy books and read. Search the Internet. Read blogs and programming newsgroups. Experiment with code. Ask your peers. Never stop seeking knowledge. Never stop learning. </p> <p>Take some of your old code, copy it, and then refactor the hell out of it. You'll be surprised what you can learn by simply refactoring code: more efficient ways to implement things that you did before (and will likely do again), better algorithms that work faster, use less resources, and are easier to maintain. Refactoring improves your skill set. Refactoring your own code, on your own time, is a personal competition against yourself to improve your own skill set. </p> <p>You don't need to compete against anyone else. Coding cowboys, platform fanboys, methodology purists, conspiracy theorists...you shouldn't be worrying about them. You should worry about yourself. Make yourself as good as you can possibly be. Every day, ask yourself this essential question: "How can I improve myself today?" Find that way, and then do it. Set aside a little time every day to refactor your skill set. </p> <p>Each day is an opportunity to make yourself a little bit better, a little more efficient than you were the day before. With each passing day, you have the opportunity to become smarter, faster, wiser, more valuable. But that means taking care to constantly revise your skill set. Have the wherewithal to discard habits and ideas that simply don't work. If you suspect you're doing something one way simply because you've always done it that way, or because that's the way everyone else does it, <em>question it. </em>If you can't see a tangible benefit to it, refactor it out.</p> <p>Look, I'm not Gandhi or anything. But I can tell you this: I firmly believe that the key to success in this field is a personal commitment to growth. Don't trust anyone to just hand you knowledge, or to stumble across the skills you'll need. You have to actively reach out and <em>take </em>the skills and knowledge you need to be successful. It's an active task. It's not going to be something you just acquire through osmosis. </p> <p>We all have to get to a point where we realize that we're not as efficient, not as smart, not as skilled, and nowhere near as good as we could be. There's always someone out there who's better than we are. </p> <p>Our goal isn't to compete with them. Our goal is to constantly aspire to be better than we are right now, at this very moment. </p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1792188015659913939.post-9965799391786296772007-03-29T12:46:00.001-05:002007-03-31T02:51:33.537-05:00What Have You Learned Over the Last Year?<p>Software development is a dynamic field. It's also a vast field. We deal with a plethora of technologies that baffle most folks; when we start talking shop around our nontechnical friends, they tend to look at us with blank faces, blinking eyes, and that curious bit of spittle leaking out the side of their mouths.</p> <p>And yet, we persevere. We work grueling hours, under often impossible schedules, with vaguely defined specifications ("What? Specifications you say? Hah!"), and management that is frequently more concerned with ship dates than code or product quality. None of these things is surprising to anyone who's worked more than six months in software development.</p> <p>If you've had the pleasure to work in a company that actually <em>cares </em>about product quality and developer sanity, count yourself one of the lucky few. And do everything in your power to hold onto that job. </p> <p>But this post isn't about our job quality. It's about what we developers do while we're solving incredibly complex problems with less than optimal tools, less time than we need and fewer hands and minds than we'd like.</p> <p>We think, we create, we innovate. </p> <p>Give some serious thought to some of the really tough problems you've faced over the last year. Not the ones where you had the right tools to solve the problem, or where you had the knowledge you needed to do it. Think about the ones where you were completely out of your depth, where you had no idea what you were doing, or how the heck you were going to to get out alive. You didn't have the knowledge. You lacked the tools. The clock was ticking. And you had a product to deliver.</p> <p>And somehow, you survived and saved the day.</p> <p>Experiences like this aren't new. They happen every day, in companies all around the world. Seemingly impossible demands are placed on developers, designers, DBAs, architects, testers, release engineers, technical writers, project managers, and everyone else involved in getting the product out the door. It's a monumental effort. In a lot of cases, development shops are severely understaffed, and the folks who work in them have to wear several hats--in the worst case scenario, one poor bastard gets the lucky job of wearing <em>all </em>the hats. </p> <p>And somehow, through all that mess, a product gets delivered. If it didn't, the company wouldn't be afloat. Sure, sometimes it doesn't happen. There are slippages, defects, embarrassments. But in the end, the product ships. Problems are solved. Work goes on. And the folks doing the work are <em>learning. Evolving. Improving. Honing their craft.</em></p> <p>If they're not, there's something seriously wrong.</p> <p>At the end of every project, every major milestone, regardless of how well or poorly executed it was, how close to the predicted ship date it was, how riddled with defects it may have been, there's a chance to look back and reflect on what you learned from the experience. We learn from our mistakes and our successes. We learn what worked, what didn't, what can be improved, and what should be left alone.</p> <p>This last year has been a whirlwind for me. I've learned a lot. For instance, a product that I thought was stellar when I first built it turned out to have <em>lots </em>of room for improvement. Sure, it was pretty, but it wasn't all that usable. And it was notoriously difficult to debug and maintain. And it was far too easy for users to enter invalid data. I'm pretty much coding in a vacuum here (and not by choice), so I didn't have the benefit of being able to ask my coworkers for advice. There were no Web designers to ask for input; no testers to rely on to catch my mistakes; no other developers to seek guidance from regarding better class models or unit testing methodologies. </p> <p>But there were the users and their feedback. So I watched them, listened to them, and learned from them. I studied other user interfaces that I admired, and that users were passionate about. I studied other respected developers, designers and engineers. And I applied their philosophies to the subsequent releases. </p> <p>The lessons I learned were simple, but breathtaking. Simplicity in design invariably leads to simplicity in testing. If it just works, it just works. Smaller, simpler interfaces are easier to test. Pages with thirty fields and all the validators to ensure they contain solid data are orders of magnitude harder to test. But smaller pages that contain fewer fields are faster, easier to use, easier to test, better encapsulated, and once you've determined that they're working, you're <em>done </em>with them. It radically changed my UI designed philosophy. And I'm happy to report that the software is far better as a result of it. It just works.</p> <p>Every day, someone somewhere learns something that changes their skill set for the better. When things are looking down, and they think they want to get the hell out of Dodge, it helps to remember all the ways in which we've improved. </p> <p>The learning never stops. The improvement never stops. But how often do we stop and think about what we've learned? Do we ever give ourselves credit for what we've become? Taking a moment to reflect on where we've been is a crucial part of directing our future; we can't just keep stumbling around repeating the same mistakes. So take a moment, look back, and ask yourself this very simple question: What have I learned over the last year?</p> <p>You might just be surprised.</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1792188015659913939.post-32560808004550621482007-03-12T15:51:00.001-05:002007-03-12T15:51:50.322-05:00Wrap Those Session Variables!<p><a title="Session Variable naming conventions" href="http://groups.google.com/group/microsoft.public.dotnet.framework.aspnet/browse_frm/thread/c3334332d7604fc2/58719e2451602478#58719e2451602478" target="_blank">An interesting topic</a> came up on the ASP.NET newsgroup forums today. The question arose: "Is there a standard naming convention for session variables?"</p> <p>While the debate meandered around the various conventions, and their suitability, and whether or not one even applied to session variables, my chief thought was whether or not one should even <em>see </em>the names of the session variables.</p> <p>Enter the basic premise of <a title="Information hiding" href="http://en.wikipedia.org/wiki/Information_hiding" target="_blank">information hiding</a>. The ASP.NET <a title="HttpSessionState Class" href="http://msdn2.microsoft.com/en-us/library/system.web.sessionstate.httpsessionstate.aspx" target="_blank">Session</a> object is, essentially a keyed collection. (Let's not bicker about it's internal implementation. List, hash table, whatever. It's essentially a keyed collection, accessed by a string key.)</p> <p>My point is that the individual keys used to access the items inside should not be replicated throughout your software. What if you choose to change the name of the key? What if the key's name isn't descriptive enough, and needs to be refined? </p> <p>Sure, you could use a constant, but where do you put it? In a global constants file? That's rather icky. As anyone knows, a global constants file can quickly grow quite large, and navigating it can become a nightmare in and of itself. Then there's the problem of <a title="Scope (programming)" href="http://en.wikipedia.org/wiki/Scope_(programming)">scoping</a>. What if you have two constants with very similar names, and you want them both to have global scope? Now you get to mangle the name of one of them. Good luck choosing.</p> <p>The ideal solution is to encapsulate the session variables in a class that manages them. This isn't as difficult as it seems, and it provides a host of benefits. The class is relatively straightforward, as shown below.</p> <p></p> <div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:efba2161-c454-4b52-ae2a-1234dbf3caa1" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">Option</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Explicit</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">On</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">Option</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Strict</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">On</span><span style="color: #000000; "><br /><br /></span><span style="color: #0000FF; ">Imports</span><span style="color: #000000; "> System.Web.SessionState <br /><br /></span><span style="color: #0000FF; ">Friend</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">NotInheritable</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Class</span><span style="color: #000000; "> MySession <br /><br /> </span><span style="color: #0000FF; ">Private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Const</span><span style="color: #000000; "> CompanySessionKey </span><span style="color: #0000FF; ">As</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">String</span><span style="color: #000000; "> </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">"</span><span style="color: #000000; ">Company</span><span style="color: #000000; ">"</span><span style="color: #000000; "> <br /><br /> </span><span style="color: #0000FF; ">Private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Sub</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">New</span><span style="color: #000000; ">()<br /> </span><span style="color: #0000FF; ">End Sub</span><span style="color: #000000; "><br /><br /> </span><span style="color: #0000FF; ">Private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Shared</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">ReadOnly</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Property</span><span style="color: #000000; "> Session </span><span style="color: #0000FF; ">As</span><span style="color: #000000; "> HttpSessionState <br /> </span><span style="color: #0000FF; ">Get</span><span style="color: #000000; "> <br /> </span><span style="color: #0000FF; ">Return</span><span style="color: #000000; "> HttpContext.Current.Session <br /> </span><span style="color: #0000FF; ">End</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Get</span><span style="color: #000000; "> <br /> </span><span style="color: #0000FF; ">End Property</span><span style="color: #000000; "> <br /><br /> </span><span style="color: #0000FF; ">Public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">ReadOnly</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Property</span><span style="color: #000000; "> IsValid() </span><span style="color: #0000FF; ">As</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Boolean</span><span style="color: #000000; "><br /> </span><span style="color: #0000FF; ">Get</span><span style="color: #000000; "><br /> </span><span style="color: #0000FF; ">Return</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Not</span><span style="color: #000000; "> Session </span><span style="color: #0000FF; ">Is</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Nothing</span><span style="color: #000000; "><br /> </span><span style="color: #0000FF; ">End</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Get</span><span style="color: #000000; "><br /> </span><span style="color: #0000FF; ">End Property</span><span style="color: #000000; "><br /><br /> </span><span style="color: #0000FF; ">Public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Shared</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Property</span><span style="color: #000000; "> Company </span><span style="color: #0000FF; ">As</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">String</span><span style="color: #000000; "> <br /> </span><span style="color: #0000FF; ">Get</span><span style="color: #000000; "> <br /> </span><span style="color: #0000FF; ">Return</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">DirectCast</span><span style="color: #000000; ">(Session(CompanySessionKey), </span><span style="color: #0000FF; ">String</span><span style="color: #000000; ">) <br /> </span><span style="color: #0000FF; ">End</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Get</span><span style="color: #000000; "> <br /> </span><span style="color: #0000FF; ">Set</span><span style="color: #000000; ">(</span><span style="color: #0000FF; ">ByVal</span><span style="color: #000000; "> value </span><span style="color: #0000FF; ">As</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">String</span><span style="color: #000000; ">) <br /> Session(CompanySessionKey) </span><span style="color: #000000; ">=</span><span style="color: #000000; "> value <br /> </span><span style="color: #0000FF; ">End</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">Set</span><span style="color: #000000; "> <br /> </span><span style="color: #0000FF; ">End Property</span><span style="color: #000000; "> <br /><br /></span><span style="color: #0000FF; ">End Class</span><span style="color: #000000; "> <br /></span></div></pre></div><br /><p></p><br /><p>A few things to note about this class: <br /><ul><br /><li>The class has <strong>Friend </strong>scope. It can't be accessed outside of the Web application. That's just sound security. <br /><li>The <strong>Private</strong> constructor prevents instantiation. Since all of the other methods are marked <strong>Shared</strong>, it makes no sense to instantiate an instance of this class. <br /><li>The <strong>Session</strong> property returns an instance of the current session. This property is marked <strong>Private </strong>and <strong>Shared</strong>, and completely hides how we're getting the session information. <br /><li>The <strong>IsValid</strong> property returns <strong>True</strong> if we have a valid session object. This helps us to avoid calling the various properties on the <strong>MySession</strong> class if there isn't a valid session to begin with. This might be the case in an exception handler. <br /><li>The <strong>Company</strong> property is marked <strong>Public</strong> and <strong>Shared</strong>, and is responsible for getting a value out of the session, and putting it into the session. It uses a class-scope constant to reference the session variable, ensuring that both the getter and setter reference the same session variable. Further, the property is <em>strongly typed.</em> When you call this property, the session variable is already converted to the appropriate data type. </li></ul><br /><p>Creating this class provides a clean, straightforward way to reference session variables in your code. For example, consider the following (not uncommon) code sample: <br /><p></p><br /><div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:e34cc9b8-f1da-4449-9ba8-dd4a0c4dbc15" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">lblCompany.Text </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">DirectCast</span><span style="color: #000000; ">(Session(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">company</span><span style="color: #000000; ">"</span><span style="color: #000000; ">), </span><span style="color: #0000FF; ">String</span><span style="color: #000000; ">)</span></div></pre></div><br /><p></p><br /><p>Using the class described above, this code is reduced to the following: <br /><p></p><br /><div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:58febf5b-5ddf-4e0f-b881-255afbdd53a4" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"><pre style="background-color:White;"><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">lblCompany.Text </span><span style="color: #000000; ">=</span><span style="color: #000000; "> MySession.Company</span></div></pre></div><br /><p></p><br /><p>Now, that may not look like much to you now, but imagine what it will save you when you have lots of session variables. And imagine how much easier it will be to refactor those variables should the need arise to do so. <br /><p>Finally, you can provide code comments in a centralized place that document what the session variables mean and are used for. That, in and of itself, is a huge boon to productivity. A little clarity goes a long way. <br /><p>And just to show that I eat my own dog food, I use this very class in my own production software. And it does work, and it does save lots of time. I don't have to remember what that cryptic string is that retrieves a specific session variable. Instead, I type "MySession." and IntelliSense presents me with a list of available session variables. <br /><p>You might be wondering where the exception handling code is. There isn't any, and that's by design. If a session variable is missing in a getter, it doesn't do me any good to catch that condition and rethrow it--I won't be telling the caller anything that the exception won't already be telling them. My exception handling code in the pages and the fallback exception handler in global.asax are responsible for cleanly handling those exceptions. <br /><p>It also doesn't do me any good to handle the <strong>NullReferenceException</strong> that will be thrown if I try to reference the <strong>Session</strong> and it's not there. Again, the global exception handler will take care of it. I could, of course, wrap the exception in a custom <strong>ApplicationException</strong>, and you always have that option. Then again, I could always perform the check by calling <strong>MySession.IsValid</strong> before attempting to retrieve any properties, and avoid the exception altogether. <br /><p>So there you have it. It's not a hard class to implement, but it pays off pretty well. For a little upfront effort, you get a decent return on your investment. Your code's readability and maintainability improve remarkably, and you know that you can refactor it with a high degree of safety. Further, you can document those session variables in the code, close to the statements that get and set them. And if you decide that you no longer want to use certain session variables, you can easily deprecate them by applying the <strong>Obsolete</strong> attribute to the properties to quickly identify every point in the code that's using them. <br /><p>So think about it. And if it's worth your time, implement it. I think you'll be glad you did.</p>Unknownnoreply@blogger.com7tag:blogger.com,1999:blog-1792188015659913939.post-65223794616183809962007-02-01T21:54:00.001-05:002007-02-02T07:13:58.129-05:00On Self-Control and Software Development<p>Just today, as I was working to deliver a major release on a product I'm working on, I found myself sidetracked by a little project of my own. </p> <p>You see, there's this little problem with one of the data fields in the database. It's not major, just an annoyance, like a four year old poking you in the ribs for an hour, asking repeatedly, "Does this bug you?"</p> <p>Well, it's been bugging me for ages. And I found myself today doing database queries and pasting data into Excel to have Excel build update queries for me using formulas (nice little time saver that is) so that I could include those statements in the SQL script to accompany the next major release.</p> <p>And then it hit me: no one asked for this. It's not included in the test plan for this release. It's gold plating. I'm doing this because <em>I </em>want to, not because the customer asked me to. </p> <p>Whoa, there, cowboy. Get a grip on yourself. Set that stuff aside, and focus on what you need to do, and not what you want to do. There are far more important deliverables to worry about, and you don't have time to waste on unauthorized features or fixes. Especially when those fixes are for issues that don't negatively impact the application. (It was a display issue--first name before last name.) It's just fluff.</p> <p>In reflection, I find myself experiencing these kinds of monumental self-control issues all the time. I get really excited about the things I could do for the customer, and I really want to do them for them. But the truth is that just because I <em>can</em> do something for them, it doesn't mean that I <em>should</em> do it.</p> <p>Any change that I make to the product has the potential to introduce new defects into the system. That's why every change that I make to it must be tested. It's why there's so much testing involved in software. (And if there isn't, something's seriously wrong.) And the testing doesn't just occur here, at my desk. It happens at the client. The product undergoes rigorous user acceptance testing. And testing isn't cheap--it consumes precious man hours, which equates to someone's hourly wages. And if I haven't gotten it right, it has to be fixed and retested. It can amount to massive amounts of money in man hours of testing.</p> <p>Lets not forget the impact that the change has on updating the test plan, the release notes, requirements documentation, and user guides. Plus any associated costs with reprinting and redistributing them. </p> <p>And what happens if the customer decides that my unauthorized change needs to be taken out? What if its impact on the system is so drastically negative that it <em>must </em>be removed? Can it be easily rolled back? And if it must be removed, what are the costs associated with doing so, and republishing all the updated documentation and builds?</p> <p>Are you getting my point yet? The cost of a simple change isn't just what it takes me to code and test it at my desk. That's just the tip of a massive iceberg.</p> <p>It takes a lot of self-control to prevent myself from adding features to a system when those features aren't (1) requested by the customer, (2) included in the project plan, and (3) absolutely critical to the current release.</p> <p>The problem, I think, is that a lot of developers out there, myself included, don't get sufficient mentoring in the discipline of self-control when it comes to software development. </p> <p>For example, we're all hailing the virtues of refactoring code to improve its maintainability, and I agree that that's a good and useful thing. But how many developers know that just because you <em>can</em> refactor a piece of code doesn't mean that you <em>should</em>? How many developers are out there bogging down project schedules because they're busy refactoring code when they should be developing software that meets the requirements for the project deadline?</p> <p>(And here, I will sheepishly raise my hand.)</p> <p>It occurs to me that before I ever modify a piece of code, before I ever touch that keyboard and write any new class or method, or create any new window or Web page, I should be asking myself, "Is this in the project plan? Is it critical to the current release?" If it doesn't satisfy one of those questions, I shouldn't be doing it.</p> <p>The key to getting that product out the door on time is staying focused, and not getting sidetracked by fluff. Take it from someone with experience: it's <em>easy </em>to get sidetracked by fluff. Adding cool features is easy to do, because you're excited about it, and motivated to do it. Working on the required deliverables is hard work; it requires discipline and self-control. You have to stay focused and keep your eyes on the target. (You thought I was going to say "ball," didn't you?)</p> <p>But we, as human beings, don't want to do what we <em>need </em>to do, we want to do what <em>interests </em>us, and what <em>excites</em> us. It takes an act of sheer will to resist that urge, to restrain ourselves, and get the real work done. I would imagine that one of the things that separates a mature developer from a novice developer is quite likely his or her ability to resist that urge to introduce fluff into software. </p> <p>In the end, I think it might be a good idea if programming courses included curricula on self-control as a discipline for developers. And I mean that quite seriously. We need to have it drilled into our heads that we shouldn't be adding anything to the product that only serves our own sense of what's cool or useful. That's not to say that sometimes developers can't predict useful features before the users do; but they cannot and should not be introduced haphazardly into a product: they should be included as a planned feature as part of a scheduled release, so that they can be adequately tested and documented, and not just suddenly sprung upon someone as an easter egg.</p> <p>There's a time and a place for everything. Gung-ho initiative has its proper place; software isn't one of them. </p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1792188015659913939.post-81893475595734424562007-01-28T03:38:00.001-05:002007-01-28T11:17:26.886-05:00Coding on Self-Destruct<p>The title of this post might lead you to believe that I'm going to write about bad coding practices. Depending on your point of view, you might be right. If you're a product or project manager, that might be your position. If you're a developer, it might not. But I'm going to ramble for a bit about my personal approach to software development.</p> <p>I basically have two philosophies when it comes to software development:</p> <ul> <li>A software developer's job is not to write software. It is to service the customer. The fact that he writes software to do it is entirely coincidental. <li>In order to do his job, a software developer must write code that is so well written and so well documented that he or she can be replaced at any time without impacting the project (or, by extension, the customer). </li></ul> <p>You may think that the second one is a lofty goal. But a goal should be lofty; it's something to aim for. If it was easily attainable, anyone could do it.</p> <p>Basically, it comes down to this: I'm obsessive about software quality. Not just coding, but the entire process: everything from the initial requirements gathering to the delivery of the product. I have to be; where I work, I'm a one man software development shop. So the attitude that I have to take when it comes down to it is that my job is to put myself out of work. I have to do work that is of such high quality that I am eventually no longer needed. That is what I mean by coding on self-destruct. So my personal promise to the customer is to deliver high quality software to the customer on time within budget.</p> <p>When I prepare a user guide, I know that real people have to use it. They have to be able to read it. I know how frustrated I get when I pick up some piece of software and it refers me to the user guide and its some poorly slapped together rich text file or help file that isn't properly indexed or cross-referenced. So I take the time to make sure that the user guide is up to date, has table of contents and an index, that it has full coverage of the software, lots of illustrations and how-to guides, and everything the users might need to know. I use plain English, not a bunch of mumbo job that only tech geeks would enjoy. The users are not developers. The same thing holds true for the requirements specifications and test plans. You have to know your target audience and use the language appropriate for them. </p> <p>(Right now, however, there seems to be a big push in the industry to move away from what they are scathingly referring to as BUFD (Big Up-front Design). I take issue with the idea that we shouldn't invest large amounts of time up front with the customer to determine their needs and wants. I think that we're treading on thin ice when we slap a system together and foist it upon them without sufficiently planning it out; I don't know that we need to invest up to a year in analysis and design, but I think that we need to spend <em>enough </em>time in analysis and design to ensure that the system's scope doesn't grow out of control, and to ensure that everyone knows what the system is supposed to be and what it isn't. The costs associated with ripping out unwanted features and adding new ones that weren't identified due to a lack of unwanted planning are enormous. Every time you do that, you introduce the risk of requiring full regression testing, system down time for redeployment, reprinting of the manuals, and retraining of the users. It's expensive. Adequate planning can mitigate those costs.)</p> <p>When I write the code, I write it for developers. But even then, I don't have any idea what the experience level of my successor will be. Will he or she have my level of experience? If not, they're going to have a tough time picking up my code and maintaining it unless I make it pretty darned easy to understand and maintain. That means self-documenting code, using a standard naming convention, a consistent coding model, and thorough use of comments. </p> <p>If I get hit by a bus, the company can't afford to have the project come to a stand-still for six months while someone tries to learn what the heck I was doing. It's my responsibility to mitigate the amount of time it takes my successor to get up to speed. So I do that. And any responsible programmer should be doing that as well.</p> <p>The payoff isn't just for my successor. It benefits me as well. I find that my own code is far easier to understand and maintain as I go back in to make defect corrections and add new features or remove those that have become obsolete. It takes me far less time to understand what a particular piece of code is doing and why if I've coded it consistently and commented it, than if if I haven't.</p> <p>Everything that is required to build or deploy the software is stored in the source code repository: source code files, images, build files, batch files that prepare the build, SQL scripts, etc. I also store the release notes (Word documents), the user guide, test plan, requirements documents, and related documentation in it. It's a myth that you can't store these files in a source code repository. They're just binary files, and you'll get full version control on them.</p> <p>I am always looking for ways to improve the visibility of the project. High project visibility gives the stakeholders a sense of involvement; they don't feel like they're sinking lots of money into a black hole and just hoping that something will come out in the end. To that end, constant communication with them is vital. They must always feel like their input is important; after all, it's <em>their </em>baby, and whatever I'm producing is for them, not me. They need to know that it's alive and kicking, that it's growing, a living breathing thing. Emails, phone calls, conference calls, a project web site, and on-site visits with demonstrations of the product go a long way towards keeping them abreast of its state. The customer's sense of involvement is an important part of the software's quality. They provide important feedback throughout the development of the product that will prevent me from making potentially costly mistakes in the design of the product that weren't caught in the initial analysis. Further, their review of the system might alert them to needs that they weren't aware of earlier--some of them critical, and some that can be slated for future releases.</p> <p>We use a Web-based defect tracking system so that our customers can report defects as they find them. We categorize them and tackle critical defects first. Critical defects are those that result in a system crash or data corruption. After that we deal with high-priority defects: those that generate error messages. Next come medium-priority defects: features that don't generate error messages, but don't behave the way they should, but have workarounds. Then it's Low-priority defects: cosmetic issues, such as font problems, spelling errors, and so forth. Feature requests are an entirely different category.</p> <p>Our goal is to have zero defects in the database at all times. If I find a defect that isn't in the database, I report it, and then I strive to address it in the current release on the development server. The smallest defects irritate me. Some folks don't understand why. But they do. When that software goes out the door, it's something that I made, the work of my hands. It bothers me when I <em>know </em>that it's going out there with a defect in it. And it's funny, because I <em>know </em>that there's no such thing as defect-free software. </p> <p>Still, I am reminded of a passage from David Eddings' book, <em>Pawn of Prophecy</em>, in which the young Garion asks Durnik the smith why he bothered to fix a broken part on a wagon. It was in a location that no one would ever see. No one would know it was there, Garion had told him. "But I'll know," Durnik had replied.</p> <p>And that's how I feel about defects. No one else will know these little defects are there. But I do. They may never crop up, but I'll know they're there. And it's my job to get rid of them. I will never be satisfied until every last one of them is gone. Because in my eyes, my promise to the customer, to deliver high-quality software on time and within budget, hasn't been met until those defects are gone.</p> <p>A high quality application, whether it runs in a browser or on the desktop, needs to have an interface that is clean, consistent, visually appealing, and easy to use. It should provide lots of visual cues to the user about the task at hand. It shouldn't leave them guessing about what they're doing. If it's a data entry form, it should provide plenty of immediate data validation that <em>helps </em>them to enter good data rather than fights them in their efforts to do so. It should never make it easy for them to lose their work. ("You are about to discard your changes. Are you sure you want to do this?") It should use plain English or language from their particular domain to describe the tasks at hand, and not technical jargon. </p> <p>An application should never assume that the user's vision is as good as the developer's or the designer's. Too many applications out there rely solely on color to distinguish between records, forgetting about colorblind users. Or they use very small fonts or window/page sizes, forgetting about users that run their screens at very low screen resolutions because they have poor vision. This only creates an unwieldy application that requires lots of scrolling for users of Web applications, or the use of obscure key commands to manipulate windows and get them to move around the screen.</p> <p>I can't cover everything in one post. But I think you get the point. Software quality covers the full spectrum of the development process. There's room for improvement in the entire process. And improving it is an iterative process. You do it, then you review your process, and seek to improve it. With each iteration, you get a little better at it. </p> <p>I don't write software for a living. I work to ensure that my customers can get their jobs done as quickly as possible, and with a minimal amount of hassle. It is purely a coincidence that tools of my trade happen to be a compiler and a computer. All of these processes that I have described are merely ways in which I strive to ensure that my customers are happy, so that I can eventually walk away from that project, knowing that they don't <em>need </em>me anymore, that the project will run just fine without me.</p> <p>When that day gets here, then I'll know my job is done.</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1792188015659913939.post-52644639523933028612007-01-25T17:21:00.001-05:002007-01-25T17:24:54.734-05:00Debugging JavaScript in Visual Studio.NET<p>I've often been frustrated by the difficulty of testing client-side script in my .NET Web applications. So, being the Google-savvy user that I am, I set out to find a solution and stumbled across <a href="http://waltritscher.com/blog/ramblings/archive/2004/09/19/211.aspx" target="_blank">Walt Ritscher</a>'s post, which I will shamelessly quote here, because I like to have this kind of information handy:</p> <blockquote> <ol> <li>Enable client-side script debugging in Internet Explorer <ol> <li>Open Microsoft Internet Explorer. <li>On the <em>Tools</em> menu, click <em>Internet Options</em>. <li>On the <em>Advanced</em> tab, locate the <em>Browsing</em> section, and uncheck the <em>Disable script debugging</em> check box, and then click <strong>OK</strong>. <li>Close Internet Explorer.</li></ol> <li>In your JavasSript function add the keyword <strong>debugger</strong> . This causes VS.NET to switch to debug mode when it runs that line. <li>Run your ASP.Net application in <em>debug</em> mode. </li></ol></blockquote> <p>I've enthusiastically tested this little tidbit, and determined that it works exactly as he describes. Thanks, Walt! This tip is a life-saver.</p> <p>However, I ran into an interesting problem: when I place the <strong>debugger</strong> keyword inside an .aspx file, Visual Studio .NET loads the <em>wrong page </em>into the debugger. Instead of stepping through script code, I'm stepping through HTML. It's quite peculiar. If I remove the <strong>debugger</strong> keyword from inline-script (that is, script that occurs between <font face="courier new"><SCRIPT></font> and <font face="courier new"></SCRIPT></font> tags in the page itself) and put it inside an included script file, everything works fine.</p> <p>Apparently, Visual Studio is having some difficulty with this sort of thing. The solution, of course, is to write a simple JavaScript include file that invokes the debugger, and include it whenever I want to invoke it. It's a simple (and yet inconvenient) workaround. It also complicates my build process, since it's one more file I have to make sure I remove from the shipped product.</p> <p>But, I am able to debug script, and that's a godsend in and of itself. Just being able to step through JavaScript code, and watch the variables in the local window is more than enough to make up for the hassles of an include file and an additional line in my build script.</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1792188015659913939.post-57228315447503682402006-11-11T19:26:00.000-05:002006-11-12T18:36:11.770-05:00A Comedy of Errors<p>This is one long, tragic story. <br> <p>I own a Pavillion m7480n multimedia PC, which I purchased around April of 2006. I purchased it at CompUSA, in an emergency, because I was in the middle of a serious project deadline and my personal computer at home died in a violent, flaming blaze of glory. I needed a computer fast. One that was fast, with a lot of memory and disk space. I had a limited budget, but wanted one that was going to last, and suited my needs as a software developer and a gaming enthusiast. The Pavillion multimedia PC seemed like a good choice at the time, and it has been, up until quite recently. <br> <p>I keep a clean machine, in an effort to get the best performance out of it. I removed all the additional games and Norton Internet Security once the OS was installed. Believe me, these HP machines come with a <em>lot </em>of junk. Norton Internet Security has got to be one of the biggest pieces of junk in the galaxy. We're talking about a piece of software that reduces a 3GHz processor equipped with 2GB of RAM to a crawl. Combine that with the fact that the machine ships with about 30 games, two personal finance packages (both MS Money and Quicken), a trial version of Microsoft Office, tons of media software, extra theming software, and all kinds of excess <em>crap </em>to give you the oohs and ahhs, and you've turned what should be a screaming machine into a clay tablet and a stylus. And let's not forget that HP ships with its own updating software, which is highly intrusive. <p>They also don't provide you with recovery discs, or original Windows discs. You have to create your own recovery CDs off of a hidden partition on the hard disk, and even that doesn't create genuine Windows CDs for you. If you perform a recovery from those CDs, you get the whole shebang or nothing. Which means removing the whole assortment of useless and system-degrading software all over again. Out of the box, the machine has well in excess of 40 processes running, and the mouse often stutters as you drag it across the screen. <br> <p>Needless to say, I do a lot of cleanup when I first get a machine. I remove the software I don't use. Especially tons of little kiddie games. Personal financial software goes, internet service offers, Office trials, Quicken & Money go, and the dreaded Norton Internet Insecurity. Once it's cleaned, I shut down non-essential services, and defragment the hard disk, making sure it's clean. I keep a clean, fast machine. By the time I'm done, I'll have, on average, somewhere around 27 to 30 processes running, very low memory consumption, and a blazingly fast machine. Which is what one would expect. I sit behind a hardware firewall, and keep the machine clean of viruses, adware, and spyware as well. Regular maintenance on the machine keeps it in tip-top shape. <br> <p>Everything was going fine, and the machine was a gem for about 6 months. One morning, I was browsing the Internet, reading the news, as is my normal habit, and everything was peachy. When I came home that night, I moved the mouse, and suddenly the machine froze. <br> <p>The behavior was peculiar. You could move the mouse, and the selection rectangle would appear, but it would not respond to mouse clicks. In addition, it wouldn't respond to keyboard input. I rebooted the machine, and everything came up just fine. But after about 30 minutes of use, it happened again. Reboot and it happened again, after about 10 minutes. Reboot and it happened again, about 5 minutes later. You get the picture. <br> <p>Eventually, it got to where the machine would lock within minutes or seconds of full system startup. Once Windows was loaded, the system would freeze. I could bring up the Task Manager and see the processes list; you could watch as the CPU cycles for each process and their memory consumption remained active right up until the keyboard and mouse "went dead." The CPU consumption and memory usage just froze. <br> <p>Now, obviously the USB port hadn't died--the mouse was still working. Swapped it out to prove it. And the CPU was working just fine. The system works beautifully in safe mode, with a USB keyboard and mouse. I even swapped in a PS/2 mouse and keyboard, and the same issue occurred. Something else was happening. <br> <p>I was fairly stumped. Being a software professional with 20 years experience, that was fairly hard to admit. So, in an act of total humility, I packaged it up and lugged it to CompUSA, where I had purchased the machine. I had paid for the warranty, so I figured I might as well let someone else wrack their brains over it. It was probably something simple, anyway, and I was just overlooking it. So I explained what I had done, what I knew, and gave them my number, telling them to call me if they needed any further information. <br> <p>What a mistake that was. After telling me it would take three days, they held the machine for three weeks. I never got a call. I had to call them and ask them about the status and whereabouts of my machine. At one point, a week into the ordeal, they said it was in process, and should be ready in a day or so. Obviously, that didn't turn out to be true. When they finally called to tell me it was ready, the technician told me that my problem was that I was running too many processes. <br> <p>You can imagine my immediate reaction. I told him, not so subtly, that he was blowing air out of his ass. There was no possible way that that was the problem. It was running the same number of processes that it had been running since I had originally configured the machine 6 months earlier. No new applications had been installed. That could not possibly have been the problem. He argued with me about it for twenty minutes. Finally, I told him that he should just pack up the machine and I would take it to the Geek Squad for a second opinion, and I'd get my money back from CompUSA since they had not resolved the issue. He said, "Well you can take it there, but I used to work there, and they'll tell you the same thing." <br> <p>Personally, I was relieved he didn't work there anymore. It bolstered my confidence in getting a more intelligent response from Geek Squad. <br> <p>Unfortunately, I never got to take it to Geek Squad. The situation got worse when I arrived at CompUSA. I contacted the store manager and explained the situation to him, and told him why I wanted my money back. Naturally, the store manager has <em>no </em>technical savvy whatsoever. So he brought in the manager of the technical repairs department. And he grabbed the kid that had "repaired" my machine. They asked the kid to explain what he had done. <br> <p>This kid had the audacity to speak to me like I was an idiot. He began his diatribe by trying to explain to me the difference between hardware and software--badly. He spoke slowly, softly, like I was a four year old, and he was trying to explain why you don't touch a hot burner on the stove. I stopped him immediately and disabused him of the notion that I had no technical knowledge. <br> <p>I explained to him that I know the difference between a hardware interrupt and a software interrupt. Then I asked him a series of questions. Did you scan the hard disk for errors? Is it properly defragmented? Did you perform memory diagnostics? Are all the interrupts working properly? Did you check for hardware conflicts? Did you perform a virus scan? Is it free of spyware and adware? Did you ensure that all the device drivers were up to date? To each question, he nodded slowly and answered, "Yeah." <br> <p>Then, he explained to me that the machine worked fine in safe mode and that they concluded that it was not a hardware problem. That was fine. I could accept that. But then, to my utter horror, he said that they had performed a system restore from the hidden recovery partition on my machine. <br> <p>"You did a what?" <br> <p>He went on a rapid speech then about why they did it and that restoring the system seemed to have solved the problem. He claimed it was a nondestructive restore. I was flabbergasted. I told them to bring the machine out, set it up, and show me. They did so, and when it booted, you could see all the original software reinstalled, and the applications that I used for work were <em>no longer installed</em>. I asked them to bring up task manager, and count the number of running processes. It was, of course higher than the number that had previously been running. More importantly, I noticed that the additional user accounts that had been on the machine <em>were gone, and so was their data!</em> <br> <p>"You lost my data." <br> <p>They insisted it was still there. I was irate at this point. I wanted to know why no one had called me to ask any questions. They'd had my number for the full three weeks, and no one had called me to ask any questions before they'd taken this catastrophic step. The kid stammered for a second before I looked at him and said, "Look, I'm in software development, and I have to be in constant contact with my customer to do my job. How can you do your job without calling the customer? You had my number for three weeks. Why didn't you call me before you made this kind of a decision?" He couldn't answer the question. <br> <p>Then I asked him if he had bothered to check the graphics driver before he did the restore. I reminded him that in safe mode, Windows uses a default, safe 640x480 graphics driver--not the 3rd party driver that you may have installed. He sidestepped the question, but I was so ticked off now that I grilled him. "Did you check the graphics driver or not?" He sidestepped three or four times before I made him answer it. "Just answer the question. Did you check that before you wiped my machine or not? It's a yes or no question." He finally admitted that he did not check the graphics driver to see if it was causing the problem. "So there it is. You didn't do your job. You erased my hard disk, lost my data, and didn't solve this problem. I can guarantee you that it will reoccur. I want my money back." <br> <p>The store manager disagreed, believing that they had solved the problem. He settled for half the fee back. I counted my losses at that point, because I was ready to do violence. I packed up the machine, and left. <br> <p>(I immediately went down the street to Best Buy and picked up a new laptop for $1300 bucks. I've sworn off of CompUSA. Their arrogance, lack of thoroughness, and the way they handled that whole ordeal has led me to boycott them. I've always had bad experiences with them in the past, but this was simply the last straw. It's sad that they happen to be the only place you can buy Apple computers in my neck of the woods.) <br> <p>Two days later, the problem came back. <br> <p>I restored the system again, hoping it might solve the problem. I wasn't sure at this point if they'd done a full restore or not. This time, I wanted to be sure. Within days, it reoccurred. <br> <p>So there I was, sitting at home with a machine that didn't work. In the meanwhile, I borrowed a laptop from the office so I could continue to work from home, and used it to research the issue. I couldn't find anyone on the Internet reporting a similar issue. Frustrated, I described the problem to the CIO and IT tech at work. Their first guess had been a bad USB controller; that was obviously not the problem. All the USB devices worked fine in safe mode. So they invited me to bring in the machine and let them take a look at it. <br> <p>At first, the were boggled by the problem itself. They'd never seen anything like it. I described the problem to them again to refresh their memory, then explained how they could get the task manager up and watch the processes freeze. Sure enough, there it was. The mouse cursor would move, and the selection rectangle would sometimes appear, but you couldn't click on anything, and the system didn't respond to the keyboard. The only thing you could do was reset it via the power button. (And I abhor shutting the machine down that way.) <br> <p>They looked at it for a day and a half. Finally, using MSCONFIG and process of elimination, they were able to pin down the culprit: Windows Update. Essentially, they disabled every service and startup program, and then rebooted the machine. Then they brought each one online, and waited to see if the problem would reappear. It only reappeared if you turned on Windows Update. And it reappeared <em>every </em>time you turned on Windows Update. <br> <p>Well, that fairly sucked. <br> <p>I <em>need </em>Windows Update. I have to be able to get critical security updates for the system. So I had to do something. We created the recovery CDs, and embarked on a plan. We were going to fully reinstall the system, and see if it worked. <br> <p>On the way home, I go another brainy idea. What if I replaced Windows XP Media Center Edition with Windows XP Professional? After all, I don't need all that extra multimedia stuff. I would remove the multimedia components from the box, and install XP Pro on it. Then I'd have a clean install, without all the crap that ships on the recovery CDs. And it should work. My theory is that the problem is with something that's shipping on those Recovery CDs, or something that HP Update is pushing onto the machines, and breaking Windows Update. Ultimately, Windows Update <em>has </em>to work. <br> <p>So I stopped by Staples, forked over $300 bucks for a Windows XP box, and went home to set up the machine. <br> <p>To my chagrin, the software won't accept the product keys when I run Setup from inside the existing Windows session. Bummer. I call Microsoft, they get me a new CD key, and those don't work either. I call Microsoft back. Apparently, I have to boot from the CD to install, because even if you specify full installation, Windows will treat it as an upgrade if you launch Setup from within an existing Windows session, and you cannot upgrade Media Center Edition to Professional Edition. (Apparently, that would be a downgrade, which is why the keys are invalid.) <br> <p>So I try to run Setup from the disk, booting the machine from the CD. Lo and behold--XP needs SATA RAID drivers. I don't have them, and HP won't provide them for use with Windows XP Pro. Isn't that nice? Further, when you install 3rd party RAID drivers in Windows XP, it expects to get them from a floppy drive. I don't know if Microsoft has noticed lately, but computers these days don't have floppy disk drives. My mom's machine has one, but that's only because it's a number of years old, and we slapped it in because we had one on hand and wanted to close an open slot on the front of her machine that was letting dust into the chassis. (And no, I didn't have a bay cover on hand.) The Microsoft tech support guy on the line had the nerve to tell me to buy one. <br> <p>"You're telling me I have to buy additonal hardware to install your operating system?" <br> <p>"Well, no." <br> <p>"That's good. Because this machine doesn't have anywhere to put one. Now how the heck am I supposed to install a RAID driver on this machine?" <br> <p>So Microsoft decides that the answer is to bring in HP in a 3-way conference call. So they put me on perpetual hold, and finally connect HP. Just as HP is answering the line, my wireless provider (you know, the most reliable wireless network in the country) drops the call. I chalk it up, and wait for Microsoft to call me back. They took my number. They'll do that. Right? <br> <p>Foolish me. <br> <p>So I call them back. I get someone else. Back to square one. They reach the same conclusion. Back on perpetual hold. Get the HP individual on the line this time. I actually hear voices! Yay, a possible resolution is coming. Verizon drops the call again. Now I'm getting ticked. It's midnight, and that's twice. I wait again. No return call from Microsoft. <br> <p>I call them back. I give the case number again. This time I explain in detail that the customer support individual could do a lot of good if he would have called me back. Now I'm upset, and have to spend another half-hour going through the whole thing again, even though it's supposedly all logged in the case file. And you <em>never </em>get the same tech support guy. <em>Never. Ever.</em> But I make it clear that I know it's not <em>his </em>fault, because he's not the guy who didn't call me back. So he goes through the same process, dial up HP, and starts the conference. Again, call dropped. Three times. And does he call me back? Hell no. <br> <p><strong>Note to Microsoft: </strong>If you want to provide better customer service, listen to your customers. Wireless companies drop calls. Call the customer back if they suddenly disappear from the line. We didn't wait all that time in line, trying to get our problem resolved, and then just hang up on you. Especially if we've been really polite and helpful. Call us back. What you guys did to me last night, three times in a row, was shameful, and only irritated me to the point where I wanted to curse you up and down. Your customer service department needs a serious upbraiding for that type of behavior. You guys are supposed to pride yourself on excellent customer care. How long does it take to call the customer back and find out if the call was dropped? Now you have to deal with bad PR. And I'm not doing this because I'm ticked. I'm just saying it was something you could have done better, and <em>should </em>be doing better. Making us call back into the queue wastes our time, makes us angry and short-tempered, and makes for a really tough experience for your customer support folks. I know that the phone support folks I talk to <em>really </em>don't like to deal with irate customers. Being proactive about things as simple as this can save you time and money, since it will reduce the stress for both the customer and your phone support personnel. Isn't that worth it? <br> <p>At this point, I'm fed up with dropped calls. I swap out one of the DVD drives with a 300GB Maxtor drive I've got sitting around, and boot off of the Windows CD. Works like a charm. Setup installs Windows onto the machine and it's screaming. Only problem: I have a couple of devices that Windows can't identify: <br> <ul> <li>Ethernet Controller <br> <li>PCI Controller <br> <li>RAID Controller <br> <li>Unknown Device</li></ul> <p>So I log onto the HP Support site and explain my problem in a chat site. The tech support person there is very helpful and provides me the links to the drivers I need. Apparently,these devices are integrated into the motherboard, and upgrading the motherboard drivers will automatically install the appropriate drivers. So I download the drivers on my laptop, burn a CD, upload them onto the PC, and then install them. <br> <p>No dice. <br> <p>The devices are still not identified. I've figured out that the unknown device must be my sound card, since there's no sound on the machine, and there's no audio device in the Sounds control panel. <br> <p>I contact HP support again. I explain the problem to them, letting them know that the motherboard device drivers didn't work. Suddenly I'm getting a different story. Now, according to HP, you can't install XP Pro on a Pavillion m7480n. Or, rather, you're not allowed to. What you're getting, when you buy this machine, is an OEM version of Windows XP Media Center Edition--it's been modified for their machine. They won't support any other version of Windows on it, and that's why you can't find the CDs for it or the individual device drivers. They want to ship me a new set of recovery CDs. I reiterate that re-running the Recovery CDs will not solve my original problem. Windows Update will continue to make the system freeze. They say, repeatedly, that they are certain that it will fix the problem. I am laughing inside, and bashing my head against the desk. <br> <p>How thick-headed can you be? I have described the problem to you repeatedly. I have told you the steps that were taken. Reinstalling from the recovery CDs does not resolve the issue. The system is now quite stable under XP Pro, except for the simple fact that I can't hit the Internet. And now you want me to return the machine to its previous unstable state? How can you ask me to do that? <br> <p>So, here I am. I have a $300 copy of Windows XP Pro, and I can't connect to the Internet. Other than that, the machine is screaming. I can't get Windows to identify the Ethernet controller or the RAID controller, so this big-ass hard disk in the machine is just useless. And I've got no sound. <br> <p>So what have I learned? <br> <ol> <li>Never buy a prebuilt machine again. I'll build my own in the future. <br> <li>Never buy from CompUSA again. <br> <li>Never, ever trust your computer to the technical support staff at a computer store. They will make potentially catastrophic decisions about your computer without consulting you, and once those decisions are made, you have little or no recourse. I lost the notes for a novel I've been working on for twenty years. Thank God for backups. But I did lose the last six months of revisions. (Shame on me.) <br> <li>Don't use a cell-phone to conduct technical support calls. Use a land-line, or use a chat service. Chat services are better, because some of them will send you the transcript via email. <br> <li>Don't expect technical support staff to call you back, even though they take your number. That's largely just lip-service to give you a warm and fuzzy feeling.</li></ol> <p>And what am I doing now? <br> <p>The only thing I can reasonably do. I'm actually going to try reinstalling, one more time, from those damned recovery CDs. Eventually, something has to give. Even if it means forking over more money for an extended warranty and shipping the whole kit and kaboodle back to HP for service. <br> <p>It just amazes me that I have to go through all this crap in the first place. It's been a serious comedy of errors. <p><strong>Addendum: </strong>Reapplying the OS from the recovery CDs worked. I'm writing this blog update from the freshly repaired machine. But I made it a point to <em>not </em>apply any of the HP updates, since I suspect that one of their automatic updates is what hosed the machine. I'll only install critical updates from Microsoft in the future. So far, it's running nice and fast. <p>But I'm still waiting nervously for something to go wrong. Call me a skeptic.</p>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-1792188015659913939.post-82045517437817029802006-11-11T16:41:00.000-05:002006-11-11T16:53:05.661-05:00Getting a Grip on Structured Exception Handling: Part 2<DIV>In <A title="Getting a Grip on Structured Exception Handling: Part 1" href="http://mikehofer.spaces.live.com/blog/cns!BBE848B7C6BE097D!131.entry">Part 1 of this series</A>, we took a look at how errors are handled in Visual Basic using <STRONG>On Error</STRONG>. We saw that it has some architectural problems, it doesn't promote developer discipline, it tends to result in code that's difficult to read and performs less than optimally, and that despite all of this <EM>it works</EM>.</DIV><br /><DIV>I can't stress enough that if your code base is working, you should leave it alone. Learning a new technology will likely get you all fired up to use it. Don't whet your appetite on a functioning and stable code base. If you're that excited, create a test project and learn it there. In the name of all that's holy, don't break perfectly good systems just to test your knowledge.</DIV><br /><DIV>In Part 2 of this series, we'll learn about structured exception handling (SEH): what it is, how it works, and why it's good for you. There are compelling reasons that object-oriented languages use it instead of passing around error numbers and returning error codes from functions. </DIV><br /><H3>What is Structured Exception Handling?</H3><br /><DIV>Structured exception handling is, in essence, a fancy term for "error handling." An exception is an error. Specifically, it's any condition that prevents your code from doing its work. For example, you might attempt to open a file and discover that it doesn't exist, or that you don't permissions to do so. These conditions would result in exceptions, since they would prevent your application from working.</DIV><br /><DIV>Some conditions, however, are recoverable. In an attempt to delete a file, you may discover that the file doesn't exist. Here, the file's absence is fine; the user was going to delete it anyway, so you can ignore the exception, and move along. </DIV><br /><DIV>So far, everything sounds familiar, and it should. It's what you've been doing all along. Only the names have been changed to protect the innocent.</DIV><br /><DIV>However, structured exception handling differs from using <STRONG>On Error</STRONG> in that it's object-oriented. It uses objects to encapsulate error information. It establishes a clean, predictable behavior for your application when an exception occurs, and allows you to nest error handlers within the same method (something that was rather difficult to do with <STRONG>On Error</STRONG>). It also provides a mechanism for guaranteeing that certain code executes before your method loses control when an exception occurs, allowing you to clean up your resources, resulting in fewer orphaned database connections, file streams, and system resources.</DIV><br /><DIV>Exceptions aren't raised, they are <EM>thrown, </EM>and exception handlers <EM>catch </EM>them. Hence, your exception handler <EM>tries </EM>to do something, and if your code <EM>throws </EM>an exception at you, you catch the exception and attempt to handle it. That's the big scary picture of SEH.</DIV><br /><DIV>The keywords used in SEH are <STRONG>Try</STRONG>, <STRONG>Catch</STRONG>, <STRONG>Finally</STRONG>, <STRONG>End Try</STRONG>, and <STRONG>Throw</STRONG>. In a nutshell, you wrap your code in a <STRONG>Try...End</STRONG> <STRONG>Try</STRONG> block. You catch any exceptions in one or more intervening <STRONG>Catch</STRONG> blocks, and you place your cleanup code in a <STRONG>Finally</STRONG> block. For example:</DIV><br /><DIV><PRE style="BACKGROUND-COLOR: white; WORD-WRAP: break-word"><DIV><SPAN style="COLOR: blue">Protected</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">Overridable</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">Function</SPAN><SPAN style="COLOR: black"> ReadFile(</SPAN><SPAN style="COLOR: blue">ByVal</SPAN><SPAN style="COLOR: black"> sourceFile </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">String</SPAN><SPAN style="COLOR: black">) </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">String</SPAN><SPAN style="COLOR: black"><br /><br /> </SPAN><SPAN style="COLOR: blue">Dim</SPAN><SPAN style="COLOR: black"> reader </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> StreamReader<br /> </SPAN><SPAN style="COLOR: blue">Dim</SPAN><SPAN style="COLOR: black"> buffer </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">String</SPAN><SPAN style="COLOR: black"><br /><br /> </SPAN><SPAN style="COLOR: blue">Try</SPAN><SPAN style="COLOR: black"><br /> reader </SPAN><SPAN style="COLOR: black">=</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">New</SPAN><SPAN style="COLOR: black"> StreamReader(sourceFile)<br /> buffer </SPAN><SPAN style="COLOR: black">=</SPAN><SPAN style="COLOR: black"> reader.ReadToEnd()<br /> </SPAN><SPAN style="COLOR: blue">Catch</SPAN><SPAN style="COLOR: black"> ex </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> FileNotFoundException<br /> Debug.WriteLine(</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">File not found:</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: black">&</SPAN><SPAN style="COLOR: black"> sourceFile)<br /> </SPAN><SPAN style="COLOR: blue">Throw</SPAN><SPAN style="COLOR: black"><br /> </SPAN><SPAN style="COLOR: blue">Catch</SPAN><SPAN style="COLOR: black"> ex </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> PermissionDeniedException<br /> Debug.WriteLine(</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">Permission denied:</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: black">&</SPAN><SPAN style="COLOR: black"> sourceFile)<br /> </SPAN><SPAN style="COLOR: blue">Throw</SPAN><SPAN style="COLOR: black"><br /> </SPAN><SPAN style="COLOR: blue">Catch</SPAN><SPAN style="COLOR: black"> ex </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> Exception<br /> Debug.WriteLine(ex.ToString())<br /> </SPAN><SPAN style="COLOR: blue">Throw</SPAN><SPAN style="COLOR: black"><br /> </SPAN><SPAN style="COLOR: blue">Finally</SPAN><SPAN style="COLOR: black"><br /> Disposer.DisposeOf(reader)<br /> </SPAN><SPAN style="COLOR: blue">End</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">Try</SPAN><SPAN style="COLOR: black"><br /><br /> </SPAN><SPAN style="COLOR: blue">Return</SPAN><SPAN style="COLOR: black"> buffer<br /><br /></SPAN><SPAN style="COLOR: blue">End Function</SPAN><SPAN style="COLOR: black"><br /></SPAN></DIV></PRE></DIV><br /><DIV></DIV><br /><DIV>When an exception occurs within the <STRONG>Try</STRONG> block, the run time scans the list of <STRONG>Catch</STRONG> blocks from top to bottom, searching for a <STRONG>Catch</STRONG> block that accepts an exception that most closely matches the type of the currently thrown exception. If it finds one, the runtime transfers control of the application to that block. If one can't be found, control is passed to the <STRONG>Finally</STRONG> block, if one exists. Your <STRONG>Finally</STRONG> block gets the opportunity to clean up resources prior to the routine losing control.</DIV><br /><DIV>If the runtime found a <STRONG>Catch</STRONG> block that handled the exception, the <STRONG>Catch</STRONG> block can do any number of things with it. It may quietly consume it, and then continue processing. Or it, may rethrow the exception. In that event, control of the application is passed to the <STRONG>Finally</STRONG> block, as if no handling <STRONG>Catch</STRONG> block were found. The <STRONG>Catch</STRONG> block may throw a <EM>new </EM>exception. In that event, the <STRONG>Finally</STRONG> block is called, and then control is passed back up the call stack until a suitable exception handler is found.</DIV><br /><DIV>In the event that no suitable exception handlers are found, a message is either displayed or recorded in the Windows event log (depending on the nature of the application), and the application terminates.</DIV><br /><DIV></DIV><br /><DIV><STRONG>Try Blocks</STRONG></DIV><br /><DIV>Place any code that <EM>could </EM>throw an exception inside the <STRONG>Try</STRONG> block (that's the portion between the <STRONG>Try</STRONG> keyword and the first <STRONG>Catch</STRONG> statement). The .NET Framework documents the exceptions that it will throw, so you should have a very good idea of what exceptions you should be ready to catch, and what statements need to be placed inside the <STRONG>Try</STRONG> block.</DIV><br /><DIV><STRONG>Catch Blocks</STRONG></DIV><br /><DIV>Exceptions are handled in the <STRONG>Catch</STRONG> block. The catch block always takes exactly one parameter, and it must be an exception. You may define multiple <STRONG>Catch</STRONG> blocks; when doing so, always put the more specific exception types at the top, and the least specific type (<STRONG>System.Exception</STRONG>) at the bottom. (The reason for doing this may not be readily apparent: if you put <STRONG>System.Exception</STRONG> any where else, anything below it will be ignored, because <EM>all </EM>exceptions are derived from <STRONG>System.Exception</STRONG>. The runtime evaluates exception types from top to bottom; once it finds a match, it will stop looking any further. So remember to put the generic exception at the bottom, as a catch-all. Better yet, if you don't know what to do with it, omit it altogether, and let the caller handle it.)</DIV><br /><DIV>In general, you do not want to place any statements inside the <STRONG>Catch</STRONG> block if those statements can throw exceptions themselves. If they can, wrap those statements in exception handlers and handle them accordingly.</DIV><br /><DIV>You are not required to provide <EM>any </EM><STRONG>Catch</STRONG> blocks at all. In that event, you must provide a <STRONG>Finally</STRONG> block. This situation is desirable when you want the code in the <STRONG>Finally</STRONG> block to execute even if an exception occurs, but you don't want to handle any of the exceptions that you'll encounter in your method.</DIV><br /><DIV><STRONG>Finally Blocks</STRONG></DIV><br /><DIV>The last block you may include in a <STRONG>Try...End Try</STRONG> block is the <STRONG>Finally</STRONG> block. You may only include one <STRONG>Finally</STRONG> block. Its contents are executed <EM>after </EM>your <STRONG>Catch</STRONG> block code has executed, and <EM>before </EM>control is transferred out of the exception handler. It's your last chance to do anything before your method loses control.</DIV><br /><DIV>Typically, the code in the <STRONG>Finally</STRONG> block rolls back transactions, closes open files, and cleans up disposable resources. As with a <STRONG>Catch</STRONG> block, it shouldn't invoke methods that can throw exceptions unless it wraps those statements in exception handlers and handles the exceptions.</DIV><br /><DIV><STRONG>Throw</STRONG></DIV><br /><DIV>To throw an exception, you use the <STRONG>Throw</STRONG> keyword, like this:</DIV><br /><br /><DIV><PRE style="BACKGROUND-COLOR: white"><DIV><SPAN style="COLOR: blue">Throw</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">New</SPAN><SPAN style="COLOR: black"> ArgumentNullException(</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">value</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">)</SPAN></DIV></PRE></DIV><br /><DIV></DIV><br /><DIV><STRONG>Throw</STRONG> always takes an exception object as its parameter. That's it. As we'll see in a later article in this series, you can create your own custom exceptions, and you can attach additional properties to them (because they're objects) to convey as much information as you need in order to describe the problem.</DIV><br /><H3>Structured Exception Handling Example</H3><br /><DIV>In the code sample below, we are doing meaningful work and committing a transaction in the <STRONG>Try</STRONG> block, rolling back a transaction in the <STRONG>Catch</STRONG> block, and disposing of resources in the <STRONG>Finally</STRONG> block. </DIV><br /><DIV><PRE style="BACKGROUND-COLOR: white"><DIV><SPAN style="COLOR: blue">Private</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">Sub</SPAN><SPAN style="COLOR: black"> InsertEmployee( _<br /> </SPAN><SPAN style="COLOR: blue">ByVal</SPAN><SPAN style="COLOR: black"> name </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">String</SPAN><SPAN style="COLOR: black">, _<br /> </SPAN><SPAN style="COLOR: blue">ByVal</SPAN><SPAN style="COLOR: black"> employeeID </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">String</SPAN><SPAN style="COLOR: black">, _<br /> </SPAN><SPAN style="COLOR: blue">ByVal</SPAN><SPAN style="COLOR: black"> connection </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> SqlConnection)<br /><br /> </SPAN><SPAN style="COLOR: blue">If</SPAN><SPAN style="COLOR: black"> name </SPAN><SPAN style="COLOR: blue">Is</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">Nothing</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">Then</SPAN><SPAN style="COLOR: black"><br /> </SPAN><SPAN style="COLOR: blue">Throw</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">New</SPAN><SPAN style="COLOR: black"> ArgumentNullException(</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">name</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">)<br /> </SPAN><SPAN style="COLOR: blue">ElseIf</SPAN><SPAN style="COLOR: black"> employeeID </SPAN><SPAN style="COLOR: blue">Is</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">Nothing</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">Then</SPAN><SPAN style="COLOR: black"><br /> </SPAN><SPAN style="COLOR: blue">Throw</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">New</SPAN><SPAN style="COLOR: black"> ArgumentNullException(</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">employeeID</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">)<br /> </SPAN><SPAN style="COLOR: blue">ElseIf</SPAN><SPAN style="COLOR: black"> name </SPAN><SPAN style="COLOR: black">=</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">String</SPAN><SPAN style="COLOR: black">.Empty </SPAN><SPAN style="COLOR: blue">Then</SPAN><SPAN style="COLOR: black"><br /> </SPAN><SPAN style="COLOR: blue">Throw</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">New</SPAN><SPAN style="COLOR: black"> ArgumentException(</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">name cannot be empty</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">)<br /> </SPAN><SPAN style="COLOR: blue">ElseIf</SPAN><SPAN style="COLOR: black"> employeeID </SPAN><SPAN style="COLOR: black">=</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">String</SPAN><SPAN style="COLOR: black">.Empty </SPAN><SPAN style="COLOR: blue">Then</SPAN><SPAN style="COLOR: black"><br /> </SPAN><SPAN style="COLOR: blue">Throw</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">New</SPAN><SPAN style="COLOR: black"> ArgumentException(</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">employeeID cannot be empty</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">)<br /> </SPAN><SPAN style="COLOR: blue">ElseIf</SPAN><SPAN style="COLOR: black"> connection </SPAN><SPAN style="COLOR: blue">Is</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">Nothing</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">Then</SPAN><SPAN style="COLOR: black"><br /> </SPAN><SPAN style="COLOR: blue">Throw</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">New</SPAN><SPAN style="COLOR: black"> ArgumentNullException(</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">connection</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">)<br /> </SPAN><SPAN style="COLOR: blue">ElseIf</SPAN><SPAN style="COLOR: black"> (connection.State </SPAN><SPAN style="COLOR: blue">And</SPAN><SPAN style="COLOR: black"> ConnectionState.Open) </SPAN><SPAN style="COLOR: black"><></SPAN><SPAN style="COLOR: black"> ConnectionState.Open </SPAN><SPAN style="COLOR: blue">Then</SPAN><SPAN style="COLOR: black"><br /> </SPAN><SPAN style="COLOR: blue">Throw</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">New</SPAN><SPAN style="COLOR: black"> ArgumentException(</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">Connection is closed.)</SPAN><SPAN style="COLOR: black"><br /></SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">End</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">If</SPAN><SPAN style="COLOR: black"><br /><br /> </SPAN><SPAN style="COLOR: blue">Const</SPAN><SPAN style="COLOR: black"> SqlTemplate </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">String</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: black">=</SPAN><SPAN style="COLOR: black"> _<br /> </SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black">INSERT INTO Employee (Name, EmployeeID) VALUES ('{0}', '{1}')</SPAN><SPAN style="COLOR: black">"</SPAN><SPAN style="COLOR: black"><br /><br /> </SPAN><SPAN style="COLOR: blue">Dim</SPAN><SPAN style="COLOR: black"> sql </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">String</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: black">=</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">String</SPAN><SPAN style="COLOR: black">.Format(SqlTemplate, name, employeeID)<br /> </SPAN><SPAN style="COLOR: blue">Dim</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">command</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> SqlCommand<br /> </SPAN><SPAN style="COLOR: blue">Dim</SPAN><SPAN style="COLOR: black"> transaction </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> SqlTransaction<br /><br /> </SPAN><SPAN style="COLOR: blue">Try</SPAN><SPAN style="COLOR: black"><br /> transaction </SPAN><SPAN style="COLOR: black">=</SPAN><SPAN style="COLOR: black"> connection.BeginTransaction()<br /> </SPAN><SPAN style="COLOR: blue">command</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: black">=</SPAN><SPAN style="COLOR: black"> connection.CreateCommand(sql)<br /> </SPAN><SPAN style="COLOR: blue">command</SPAN><SPAN style="COLOR: black">.CommandType </SPAN><SPAN style="COLOR: black">=</SPAN><SPAN style="COLOR: black"> CommandType.Text<br /> </SPAN><SPAN style="COLOR: blue">command</SPAN><SPAN style="COLOR: black">.Transaction </SPAN><SPAN style="COLOR: black">=</SPAN><SPAN style="COLOR: black"> transaction<br /> </SPAN><SPAN style="COLOR: blue">command</SPAN><SPAN style="COLOR: black">.ExecuteNonQuery()<br /> transaction.Commit()<br /> </SPAN><SPAN style="COLOR: blue">Catch</SPAN><SPAN style="COLOR: black"> ex </SPAN><SPAN style="COLOR: blue">As</SPAN><SPAN style="COLOR: black"> SqlException()<br /> transaction.RollBack()<br /> </SPAN><SPAN style="COLOR: blue">Throw</SPAN><SPAN style="COLOR: black"><br /> </SPAN><SPAN style="COLOR: blue">Finally</SPAN><SPAN style="COLOR: black"><br /> </SPAN><SPAN style="COLOR: blue">command</SPAN><SPAN style="COLOR: black">.Dispose()<br /> transaction.Dispose()<br /> </SPAN><SPAN style="COLOR: blue">End</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">Try</SPAN><SPAN style="COLOR: black"><br /><br /></SPAN><SPAN style="COLOR: blue">End Sub</SPAN></DIV></PRE></DIV><br /><H3>Moving On</H3><br /><DIV>In this article, we've seen the basics of how to handle thrown exceptions, and how to throw them ourselves. Moving forward, we'll cover nesting exception handlers, creating our own exceptions, and we'll dive into when it's appropriate to throw them, and when it's not.</DIV>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1792188015659913939.post-81624627472554702902006-11-11T16:13:00.000-05:002006-11-11T16:41:46.642-05:00Getting a Grip on Structured Exception Handling: Part 1<DIV>Many VB 6 developers who have made the switch to .NET continue to use VB 6's <strong>On Error</strong> error handling model. Some continue to do so because structured exception handling using <strong>Try...Catch</strong> and <strong>Throw</strong> represents a fairly daunting learning curve. Others do so because the <strong>On Error</strong> model is still supported for backwards compatibility and the old adage still applies: "If it ain't broke, don't fix it."</DIV><br /><DIV>But there are very good and compelling reasons to learn structured exception handling. Modern programming languages have been using it for years, and Visual Basic has only recently caught on. It's a <em>good thing, </em>believe me. In this post, and the one that will follow, I'm going to try to explain why you want to embrace it, and how it can improve your code. Hopefully, I'll do so in a way that's clear, concise and doesn't confuse you.</DIV><br /><h3>If It Ain't Broke, Don't Fix It. No, Really</h3>First and foremost, you <em>shouldn't </em>do a massive overhaul of your existing code just to replace the error handlers. And if you think someone's not considering that, think again. If <em>you</em> are, knock it off.<br /><DIV>Refactoring your entire code base just to replace the exception handlers isn't a good idea. It's tantamount to tearing apart your car just to replace all the screws. If your software is working with the existing error handling engine, leave it alone, especially if you're not familiar with structured exception handling. You do <em>not </em>want to break perfectly good code because you wanted to implement a language feature that you don't fully understand. Get a grip on structured exception handling first, then use it in your next project.</DIV><br /><DIV>As an aside, structured exception handling and <strong>On Error </strong>can co-exist in the same project. They are not mutually exclusive. Having said that, I don't normally recommend it. Mixing exception handling models tends to create code that confuses maintenance programmers; it creates a mental context switch when they're reviewing your code. Some routines use one model, some use another. This slows down maintenance of the system, and that's rarely a good thing. As you can imagine, I will <em>not </em>be providing advice on how to mix these models in a project.</DIV><br /><h3>A Review of On Error</h3>Visual Basic's <strong>On Error</strong> error handling mechanism, which is still supported in .NET for backwards-compatibility, works in a relatively straightforward manner:<br /><pre style="color:white;"><div><span style="color:blue;">Public</span> <span style="color:blue;">Sub</span><span style="color:#000000;"> Main()<br /><br /> <span style="color:blue;">On</span> <span style="color:blue;">Error</span> <span style="color:blue;">GoTo</span> ErrorHandler<br /><br /> <span style="color:blue;">Dim</span> x <span style="color:blue;">As</span> <span style="color:blue;">Integer</span><br /><br /> x = <span style="color:#red;">5</span> <span style="color:lightgray;">/</span> <span style="color:red;">0</span> <span style="color:green;">' Force a divide by zero</span><br /><br />HelloWorld:<br /> <span style="color:blue;">MsgBox</span> <span style="color:red;">"Hello, World!"</span><br /> <span style="color:blue;">Exit Sub</span><br /><br />ErrorHandler:<br /> <span style="color:blue;">If </span>Err.Number</span><span style="color:lightgray"> = </span><span style="color:red;">11</span><span style="color:blue;"> Then</span><br /> <span style="color:green;">' Yep, we did this on purpose. Ignore it and continue.</span><br /> <span style="color:blue;">Resume</span> HelloWorld<br /> <span style="color:blue;">Else</span><br /> <span style="color:blue;">MsgBox Err</span>.Description<br /> <span style="color:blue;">End If</span><br /><br /><span style="color:blue;">End Sub</span></div></pre><br /><DIV>In some cases, you didn't really want to jump to an error handler. Instead, you wanted to ignore the error, because you could safely ignore it. In those cases, you used <strong>On Error Resume Next</strong>, and handled the error on the line immediately below the offending line.</DIV><br /><DIV>Visual Basic provides the <strong>Err</strong> object, which exposes the <strong>Erl</strong>, <strong>Number</strong>, <strong>Source</strong>, and <strong>Description</strong> properties. These properties are intended to provide enough information for you to find out what happened, where it happened and what component was responsible for it.</DIV><br /><DIV>While the <strong>On Error</strong> model was straightforward, it was looked upon with fairly universal disdain for one major reason: it involved the use of the dreaded <strong>GoTo</strong> keyword. While I won't dredge up a pointless debate about its merits, I will point out that it had one particular failing: it tended to create very complex procedures when a function needed to handle many different kinds of errors. Simply put, there was a lot of jumping around.</DIV><br /><DIV>In addition, determining the nature of the error could sometimes be a risky business. In an ideal scenario, you could trust the error number. According to the documentation, the first 1,050 error numbers were reserved for use by Microsoft. An application or library vendor was expected to start their unique error numbers with 1051 and supply a descriptive error message along with the number. This rule wasn't always respected, however.</DIV><br /><DIV>Vendors were also expected to provide the source of the error. But this meant that you had to query at least two properties in order to determine the true nature of an error, because two different vendors might expose the same error number. But it gets worse. Some vendors chose to use the same number for multiple errors, and distinguish between specific errors in the message. Consequently, in order to be <em>truly </em>certain, you had to check all three properties.</DIV><br /><pre color="white"><div><span style="color:blue;">If</span><span style="color:#000000;"> Err.Number </span><span style="color:#000000;">=</span><span style="color:#000000;"> x </span><span style="color:blue;">And</span><span style="color:#000000;"> Err.Source </span><span style="color:#000000;">=</span><span style="color:#000000;"> y </span><span style="color:blue;">And</span><span style="color:#000000;"> Err.Description </span><span style="color:#000000;">=</span><span style="color:#000000;"> z </span><span style="color:blue;">Then</span><span style="color:#000000;"><br /> </span><span style="color:green;">'</span><span style="color:green;"> Handle the error</span><span style="color:green;"><br /></span><span style="color:blue;">End</span><span style="color:#000000;"> </span><span style="color:blue;">If</span><span style="color:#000000;"><br /></span></div></pre><br /><DIV> </DIV><br /><DIV>For many developers, this seemed like overkill, so they chose to parse the contents of the description, since the likelihood of two vendors providing <em>exactly </em>the same message was pretty remote. This trade-off is never a good idea, however. It compromises certainty, and it brings inefficient string operations to the table. I've worked with an immeasurable amount of VB and ASP code in which this decision was made. While the code worked, it was hard to read and therefore difficult to maintain; and the lack of information about the source made it difficult to determine <em>who </em>was raising the error that you were trying to handle.</DIV><br /><DIV>Someone once said that the difference between a professional developer and a hack was the way in which they dealt with errors: A professional developer takes them <em>very </em>seriously, and a hack plays foot-loose and fancy-free with them. I'm not sure who said that, but it struck a serious chord with me, and it changed the way I write software. When it comes to handling errors in your code, you want to be <em>absolutely certain </em>that you're handling the right error, at the right time, in the right way, and if you don't know what to do with an error, you leave it alone so that the caller can handle it.</DIV><br /><DIV>Because of the way that the <strong>On Error</strong> architecture works, it is sometimes difficult to do this. You have to create lots of "jump-points" so that your error handlers return control to the correct location. If you're querying the <strong>Err</strong> object like most developers, you're doing a <em>lot </em>of string parsing, which can be quite tedious and creates code that is hard-to-read and performs poorly. After a while, you may start letting things go, and eventually only pay attention to the big, obvious errors. You may even get to the point where you dread diving into the error handling code because it's harder to read than the application logic.</DIV><br /><DIV>Think you've got it bad? What about the next poor developer who's got to maintain that code after you've moved on to bigger and better things?</DIV><br /><h3>Moving On</h3><br /><DIV>So here's what we've got:</DIV><br /><ul><br /><li><strong>On Error</strong> is a simple, straightforward error handling system <em>that works </em>(usually).<br /><li>There's no reason to modify existing code bases that work without an utterly compelling reason to do so.<br /><li><strong>On Error</strong> does pose maintenance issues, in that it is difficult to read, and tends to discourage developer discipline.<br /><li>Error number collisions are quite frequent, meaning that developers have to rely on the <strong>Err.Source</strong> and <strong>Err.Description</strong> properties to determine the nature of the error, negatively impacting application performance and maintainability.<br /><li><strong>On Error</strong> is based on a trust system: it trusts the vendor or developer to correctly populate the <strong>Err</strong> object's properties, which often doesn't happen as it should.</li></ul><br /><DIV>In the next article in this series, we'll look at structured exception handling (SEH) as the alternative to <strong>On Error</strong>. We'll look at how it addresses these issues, and provides a superior means of dealing with errors in your applications.</DIV>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1792188015659913939.post-73093309749107273252006-11-11T16:09:00.000-05:002006-11-11T16:12:14.964-05:00Linux: The Silver Bullet of Operating Systems<div>I've been reading a lot of reviews of IE 7 lately, and many of them allow users to post responses. There seems to be a fairly common theme among those who don't like it: with zealous fervor, they tell you that you can solve all your problems by simply switching to Linux.</div><div> </div><div>My god, people, wake up.</div><div> </div><div>Are you honestly suggesting that the average home user switch to Linux or any Unix-based operating system (excluding OS X) simply because Internet Explorer doesn't meet your exacting standards? Get real.</div><div> </div><div>First off, the average home user isn't going to have the technical skills to be able to use any of the xNix operating systems (again, excluding OS X). These operating systems tend to be complex, having been built up over 40 years and counting. Their vast feature set is daunting, even to advanced IT professionals. You cannot expect the average home user to turn in an operating system like Windows, which shields them from the complexities inherent in an operating system and expect them to switch gears without introducing a whole new set of problems. The frustration from learning a new operating system alone would likely drive them bonkers.</div><div> </div><div>Second, you have to take application selection into consideration. Are your favorite applications even <em>available </em>in that operating system? Can you get Microsoft Office for it? PageMaker? Adobe Acrobat? Decent video or music editing software? And of the software that is available for it, can the average home user go to Best Buy, Circuit City, or CompUSA and purchase it in a nice shrink-wrapped box (complete with documentation, mind you), or does he or she have to go to some obscure website that he or she may or may not trust and download it (possibly with a complimentary virus)? And if they download it, will it work and can they get product support for it?</div><div> </div><div>Third, how about device support? Can I plug <em>all </em>of my peripherals in it and have them just work? Or will I have to go on a mad search for device drivers again, like I had to back in the days before Windows XP? Sure, if I'm an IT professional, that's a snap. But if I'm the Average Home User (TM), that's more than I'm usually able to deal with. It's probably going to tick me off royally, and I'm going to wonder why in the heck I bothered with this operating system in the first place. I'll be wondering, "Where's my plug-n-play support?"</div><div> </div><div>Fourth, how good is the font support? Does it support true WYSIWYG? Or does the type look completely different between the screen and the printer? The same could be asked of the colors. That's going to be really important for the average home user who's working with videos and photos, printing calendars, Christmas cards, and invitations, you know.</div><div> </div><div>Fifth, if I need to do my office work on it, will I be able to do so? If so, how difficult will it be to do so? Will I have to be a rocket scientist? And if it breaks, who do I call? I mean, if I was able to call my brother or my kid before, because he was a Windows guru at his company, will he still be able to do that? I switch operating systems, and now no one knows anything about it. I'm now an island in the middle of a Windows ocean. I've effectively isolated myself from everyone. How long do I have to wait before I can get meaningful support in an <em>emergency?</em></div><br /><div>Now. Ask yourself a really serious question and think hard before you spit out a really ludicrous answer. Are you really going to tell someone to risk losing <em>all that support </em>just because the browser isn't that cool, or because there are a few security loopholes? </div><br /><div>Sure, we live in a Microsoft world. Are all their products perfect? Heck no. I work with Microsoft products every day, and I can testify to the fact that they aren't. I also own a Mac computer at home, and I happen to be a big fan of OS X. I've also had the occasion to use Linux. Each of them has their pros and cons. But I'm here to tell you that there is no silver bullet. Statements like those being made in these IE 7 reviews are asinine, short-sighted, and utterly ridiculous. Linux is not the solution to everyone's problems because it isn't the ideal operating system for the average home user. </div><br /><div>Average home users need an operating system that's easy to use, has a large selection of over-the-counter applications, is secure but not intrusive, supports <em>all </em>of their peripherals, and has great product support. In my opinion, the one that comes closest to that is Mac OS X. However, it's pricey nature makes it cost-prohibitive for the average home user; and that's one of the reasons we live in a Microsoft-dominated world.</div><br /><div>Sure, Linux is cheap. But the over-the-counter application support just isn't there, and the GUIs, in my opinion, still look like their in their infancy. And they're still targeted at tech-heads. When all the Linux variants start targeting the <em>average </em>home user, and the over-the-counter application support gets there, and they get <em>really good </em>device driver support, things might change. But there's no way on earth I'd ever recommend it to my family or any of my non-technical friends. They'd go out of their minds trying to figure it out.</div><br /><div>So, in the end, I'll continue to read these kinds of posts from Linux advocates and shake my head. In my personal opinion, statements like these demonstrate a complete lack of comprehension when it comes to the average home user's computing needs.</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1792188015659913939.post-53239643480575043382006-11-11T16:07:00.000-05:002006-11-11T16:09:11.289-05:00Raising Base Class Events From a Derived Class in .NETI always forget how to do simple things. This blog is likely to contain lots of little snippets like this. Lots of them will be stuff that big brained techheads find fairly obvious. Well, that's just tuff. You sit in your big comfy chairs and scoff. I'm not here to feed your egos. I'm here to post stuff about things that I find hard to locate, stuff that made me smack my forehad when I found it because it was so obvious. The frustration and time wasted searching for it makes it worth posting here.<br /><br />So, with that out of the way, here we go. And I'm not even going to add any fluff to it. Just a simple code sample.<br /><br /><span style="font-size:85%;"><span style="font-family:Courier New;"><b><span style="color:blue;">Public</span></b><span style="color:blue;"> <b>Class</b></span><span style="color:#000000;"> BaseClass<br /></span></span></span><span style="font-size:85%;"><span style="font-family:Courier New;"><span style="color:blue;"><span style="font-size:+0;"><br /> </span><b>Public Event</b></span><span style="color:#000000;"> MyEvent(</span><b><span style="color:blue;">ByVal</span></b><span style="color:#000000;"> sender </span><b><span style="color:blue;">As</span></b><span style="color:#000000;"> </span><b><span style="color:blue;">Object</span></b><span style="color:#000000;">, </span><b><span style="color:blue;">ByVal</span></b><span style="color:#000000;"> e </span><b><span style="color:blue;">As</span></b><span style="color:#000000;"> EventArgs)<br /></span></span></span><span style="font-size:85%;"><span style="font-family:Courier New;"><span style="color:blue;"><span style="font-size:+0;"><br /> </span><b><u>Protected</u></b> <b>Sub</b></span><span style="color:#000000;"> OnMyEvent()<br /></span></span></span><span style="font-size:85%;"><span style="font-family:Courier New;"><span style="font-size:+0;"><span style="color:#000000;"> </span></span><b><span style="color:blue;">RaiseEvent</span></b><span style="color:#000000;"> MyEvent(Me, EventArgs.Empty)<br /></span></span></span><span style="color:blue;"><span style="font-size:85%;"><span style="font-family:Courier New;"><span style="font-size:+0;"> </span><b>End Sub<br /><br /></b></span></span></span><b><span style="color:blue;"><span style="font-size:85%;"><span style="font-family:Courier New;">End Class<br /><br /></span></span></span></b><span style="font-size:85%;"><span style="font-family:Courier New;"><b><span style="color:blue;">Public Class</span></b><span style="color:#000000;"> DerivedClass<br /></span></span></span><span style="font-size:85%;"><span style="font-family:Courier New;"><span style="color:blue;"><span style="font-size:+0;"> </span><b>Inherits</b></span><span style="color:#000000;"><b> </b>BaseClass<br /><br /></span></span></span><span style="font-size:85%;"><span style="font-family:Courier New;"><span style="font-size:+0;"><span style="color:#000000;"> </span></span><b><span style="color:blue;">Public Sub</span></b><span style="color:#000000;"> MyTask()<br /></span></span></span><span style="font-size:85%;"><span style="font-family:Courier New;"><span style="font-size:+0;"><span style="color:#000000;"> </span></span><b><span style="color:blue;">MyBase</span></b><span style="color:#000000;">.OnMyEvent() </span><i><span style="color:green;">' Raises the event<br /></span></i></span></span><b><span style="color:blue;"><span style="font-size:85%;"><span style="font-family:Courier New;"><span style="font-size:+0;"> </span>End Sub<br /><br /></span></span></span></b><b><span style="color:blue;"><span style="font-size:85%;"><span style="font-family:Courier New;">End Class</span></span></span></b>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1792188015659913939.post-82020116741729785762006-11-11T16:05:00.000-05:002006-11-11T16:07:35.798-05:00The Evil Wiles of the Chain Letter<p>My mother recently got a chain letter in her email, and was telling me how she hates it when people send them to her. Certain friends and family members are notorious for forwarding them us, and it really peeves us off, because they're a complete waste of our time. We simply delete them these days. Unfortunately, in many cases, you have to open the email before you can determine whether or not its a chain letter. And by the time you've opened it, you feel utterly compelled to finish reading it. It's like a hypnotic effect. </p><p>Once you've opened a chain letter, it's contents are like road kill: you <em>have </em>to look. You can't help it. You don't want to look, but you have to. Your brain screams, "It's not right! Turn the other way! Close your eyes!" But your eyes stare fixedly at it while your mouth drops open in horror and bile rises at the back of your throat. When you've finished reading it, you're left with that feeling that you just got ripped off, like you paid to see <em>Saving Private Ryan </em>and instead got something on the order of <em>Killer Beach Bimbos from Outer Space</em>.</p><p>Chain letters have even moved away from the email format and evolved into MySpace comments. (I haven't checked out sites like TagWorld or any of MySpace's competitors, so I can't really comment on that.) Fortunately, MySpace comments are easily ignored. You can just customize the page to not display them.</p><p>Now, chain letters pose a number of problems, as I see it:</p><ol><li><p><strong>They waste the recipient's time.</strong> No one that I know likes to waste time reading them. I'd guess that those who <em>do</em> are also those who like to forward them, thereby propagating them and contributing to the misery of those who don't want to get them in the first place. </p></li><li><p><strong>They clog up mailboxes.</strong> Most users have a finite amount of space in their mailboxes. When your mail box runs out of space on the mail server, the mail server refuses to accept mail for you until you start clearing out your maibox. If your mailbox is full of spam, that tends to tick you off. No one wants to waste a limited amount of space on email that serves no purpose and that they'll just delete anyway. This is especially problematic if the chain letter includes images, audio, or video attachments.</p></li><li><p><strong>They tend to contain deeply nested attachments.</strong> Because they are forwarded so many times, and due to the way that many email programs forward emails, the message is forwarded as an attachment: its text is not included in the body. This forces the user to open the attachment in order to read the message, which is often a risky proposition these days, since many casual users either don't use virus software or don't keep it up to date. However, <em>every</em> time the message is forwarded, the message is sent as an attachment, with a new message wrapper. So the new recipients have one more layer to click through in order to get to the actual message. By the time it's been sent ten or twenty times, which happens quite frequently, the real message is so deeply embedded in nested attachments that it's a chore to reach it. This is a highly annoying aspect of these messages, and it wastes lots of time.</p><p>The fact that the messages are included as message headers also bloats the size of the email itself. Instead of simply including the text of the body, and using a single mail header, the email is sent as a file attachment, with the complete email header plus the complete email header for the new email. Now if I send it again, I get to create an email that contains a new email header, plus a new file attachment that contains the email header and text for the email I received plus the original attachment. The file's just gotten much larger. When my recipient forwards the file, it will get larger again. This causes the chain letter's overall size when sent across the Internet to grow every time it is forwarded.</p></li><li><p><strong>They are propagated by exploiting human weakness.</strong> Chain letters aren't sent out by a mindless computer sitting in an air conditioned network operations center. Nor are they sent out by some hacker sitting in a dark room in his mom's basement somewhere. They are sent out by people who get them from their friends, and think they're cute, or touching, or emotionally stirring, and forward them on to their friends. They forward them on to the people that they think are most interested in them. And so, unlike spam, which dies when it hits a user's machine and (hopefully) goes to his or her recycle bin or junk email folder, the user keeps the chain letter alive by sharing it with his or her friends and coworkers. Further, the user sends it to multiple individuals, and those individuals do the same. So the chain letter is spread like a biological virus.</p></li><li><p><strong>They are nothing more than personal, selective spam.</strong> Don't believe me? <a href="http://dictionary.reference.com/browse/spam" rel="nofollow">Dictionary.com</a> defines spam as "unsolicited e-mail, often of a commercial nature, sent indiscriminately to multiple mailing lists, individuals, or newsgroups; junk e-mail." Now here, by "indiscriminately" we mean that the sender of a chain letter thinks that everyone wants to get the same chain letter that he or she did. "If I like it, certainly all of my friends and coworkers want to get it." So what does that mean, exactly?</p><p>It means that the sender is distributing spam, knowingly and willingly, albeit naïvely. People have been sent to jail and fined for that because it's illegal in certain states. Why is it illegal? Because it annoys users, and wastes bandwidth and the recipients' resources. The biggest reason, however, is that <em>it annoys users. </em><a href="http://www.clickz.com/showPage.html?page=1566161" rel="nofollow">This article </a>shows that the vast majority of people want spam banned <em>simply because it annoys them</em>. </p><p>By definition, spam is not required to be commercial in nature. That means that all those chain letters you get that promise you happiness, the good will of angels, good luck, blessings, prosperity, the love of your life within three days, your wish coming true, the prevention of bad luck, and on and on, are all spam. Most of us just haven't viewed chain letters in that light before.</p><p></p></li></ol><p>And those are just the issues I can think of right off the top of my head.</p><p>While I'm at it, it isn't just novice users that get sucked into these emails. With address spoofing you can receive an email that looks perfectly legitimate, and you can start clicking on those attachments because it seems perfectly reasonable to do so. I've done it. My mom has done it. My coworkers have done it. You've typically already opened four or five layers before you realize what you've done and think to yourself, "What am I doing? This is obviously junk!"</p><p>So please, <em>please, think </em>before you send that next chain letter out. Those promises they make at the bottom of them are untrue. The threats they make are untrue. You control your own destiny, and no email is going to dictate it for you. Do you think that the destiny of the people that two whom you forward the email is going to be changed because <em>you sent it to them?</em> </p><p>You aren't helping anyone by forwarding these things. Point of fact, you're likely annoying more people than you're helping.</p><p>Do everyone a favor. When you receive a chain letter, delete it immediately. Then contact the person who sent it to you and politely ask them not to send them to you anymore. </p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1792188015659913939.post-76094068340078944442006-11-11T16:02:00.000-05:002006-11-11T16:03:35.790-05:00Creating an IsDate() Function in JavaScriptOne of those things that always seems to be missing from the Date object in Javascript is the ability to determine if a string contains a valid date expression. I find that sorely lacking myself. Coupled with the fact that I needed it badly for a major project I'm working on, this became a really pressing concern of mine. Hence, a new blog entry.<br /><br />A quick search on <a href="http://www.google.com/search?hl=en&q=javascript+isdate">Google</a> turned up a few different ideas. But my favorite one was this, which I found on <a href="http://www.thescripts.com/">http://www.thescripts.com/</a>:<br /><br /><blockquote dir="ltr"><br /><span style="font-family:Courier New, Courier, Monospace;">There are a lot of good answers on this thread but personally I think<br />javascript itself is a better date validator.<br /><br /><span style="color:#0000ff;">function </span>isDate(sDate) {<br /> <span style="color:#0000ff;">var </span>scratch = <span style="color:#0000ff;">new </span>Date(sDate);<br /> <span style="color:#0000ff;">if </span>(scratch.toString() == <span style="color:#ff0000;">"NaN"</span> scratch.toString() == <span style="color:#ff0000;">"Invalid Date"</span>) {<br /> <span style="color:#0000ff;">alert</span>(<span style="color:#ff0000;">"Not a Date"</span>);<br /> <span style="color:#0000ff;">return </span><span style="color:#0000ff;">false</span>;<br /> } <span style="color:#0000ff;">else </span>{<br /> <span style="color:#0000ff;">return true</span>;</span><br /><br /><span style="font-family:Courier New, Courier, Monospace;"> }<br />}<br /><br />Javascript's date parser is actually very robust and powerful and able<br />to hande 1/20/2004 as well as January 20 2004. IE returns NaN (not a<br />number) when it can't figure out a date, Mozilla (gekko engine) returns<br />Invalid Date. </span><br /></blockquote>I like this one because it's simple, short, and to-the-point. It's unfortunate that the poster was anonymous, because I'd like to give him/her credit for it. At the least, I can provide a link back to the thread. <a href="http://www.thescripts.com/forum/thread145795.html">It's response number ten in the thread, from user pcx99..</a>Unknownnoreply@blogger.com10tag:blogger.com,1999:blog-1792188015659913939.post-26293327499857844392006-11-11T15:34:00.001-05:002006-11-11T16:00:38.250-05:00The Power of Unit Testing, and Microsoft's Big Snafu<P>Automated unit testing has got to be one of the most powerful ways to produce rock-solid code. That's not to say that code-reviews and full testing by a dedicated quality assurance team don't have value, but automated unit testing has the virtue of being fast, easy-to-execute, <EM>preemptive, </EM>and (assuming the tests were properly written) extremely accurate.</P><br /><P>The idea is simple. Before you write any code, you write a test that breaks. Then, you massage the code until the test passes. This makes you focus on the problem domain, and keeps you from getting side-tracked by fancy fluffy stuff that you might add and never actually use. </P><br /><P>Now here's where the power comes from: the automated tests are run <EM>every time the software is built. </EM>These tests don't tell you if the software is syntactically unsound (that's the compiler's job); instead, they tell you whether or not the software is <EM>doing what it's supposed to do</EM>, and it's telling you this <EM>before the testers get their hands on it. </EM>The fact that it happens every time the software is built should give you a tremendous feeling of confidence: you'll know immediately if anyone introduced any code changes that broke your business logic, so you can be proactive about correcting them, instead of waiting for the testers to find it and rub your nose in it.</P><br /><P>However, manually creating your own testing framework, and religiously executing the unit tests themselves with every build can be daunting. That's why projects like <A href="http://www.junit.org/">JUnit</A> and <A href="http://www.nunit.org/">NUnit</A> are wildly popular: they simplify the process of creating and executing automated unit tests. The idea is so deeply entrenched in our development methodology these days that it has a name: Test-Driven Development (TDD). Kent Beck wrote what is arguably the definitive introductory work on the idea, the aptly named <EM>Test-Driven Development</EM>. (You can find that book on Amazon <A href="http://www.amazon.com/exec/obidos/ASIN/0321146530/ambysoftinc/103-0300543-1115801">here</A>. But if you don't want to read the whole book—even though it's a really good idea—you can take the crash course <A href="http://www.agiledata.org/essays/tdd.html">here</A>.)</P><br /><P>So now that I've made it clear that I'm a huge proponent of test-driven development, and of automated unit testing specifically, it's time we turned our steely gaze to Microsoft.</P><br /><P>As many well know, Microsoft created its own unit testing framework. From what I've seen, it's pretty exhaustive. (Of course, like any Microsoft version of an existing "standard," their framework deviates pretty substantially from the NUnit and JUnit frameworks. So you can't just plop your existing test frameworks into Visual Studio 2005 and run them. No, you have to convert them to Microsoft's way of thinking.</P><br /><P>How very Gatesian.</P><br /><P>But that's not my main peeve. My main peeve is this: you can only access the unit testing capabilities of Visual Studio 2005 if you own Team System. Furthermore, the express editions of Visual Studio do not provide add-in support, so that tools like NUnit can't be snapped into the IDE, except as external tools.</P><br /><P>Here's what I don't understand: Microsoft is all about security these days. Part of security is writing secure, stable software. Why on earth would you deny developers access to a technology that could help them to write stable, secure software? Furthermore, if you're releasing a free version of your IDE to the masses, why would you think it's a good idea not to support add-ins, so that new developers can't take advantage of things like NUnit?</P><br /><P>And please don't tell me that you just expect everyone to upgrade to Team System. If you want them to do that, price your products reasonably. The average CIO will just about have a heart attack when he sees the estimate to upgrade from 2003 to Team Suite. I wouldn't be surprised if most companies can't afford it. </P><br /><P>We can sit here and talk about how everything comes down to long-term cost versus short-term costs until we're blue in the face. Hey, I've read Steve McConnell, I know the facts. But the plain truth is, when you have someone sitting in the finance department who doesn't really get it, and who doesn't care to get it, because he's got a "bigger picture" than you do, your upgrade costs are going to get nixed in a heartbeat. You'll get the Professional Edition, or something close to it, because it's at least reasonably priced and does just about everything you need. I'm willing to bet that more companies are run that way than aren't, and a lot of poor developers are working for them. </P><br /><P>So, as I see it, what Microsoft has done is shoot itself in the foot over unit testing. They're not fostering the growth of a corps of developers who write good, stable, well-tested code. They've placed the cost of unit testing using Microsoft's preferred framework so far out of reach that most companies will never embrace it. Those that do embrace automated unit testing will look for open-source third party products like NUnit. They'll find ways to make it work. If Microsoft was trying to squeeze that competition out, they certainly went about it the wrong way.</P><br /><P>I would certainly like to sit down and talk to the brainiac that decided that unit testing wasn't going to be made available except for the elite few who could afford Team Suite. I'd also like to find the guy who decided that Add-In support was a no-go for the Express Editions, and confiscate his Geek Mafia membership card.</P><br /><P>These two decisions together have pretty much guaranteed that most new users will find it too hard to learn how to unit test from the beginning, because Microsoft went out of its way to make it difficult. </P><br /><P>When Microsoft had the opportunity to put the tools into the hands of new developers (many of whom couldn't afford pricey development tools) to make them better, more effective programmers, they opted instead to take those tools away, to keep them for the rich, and take away their ability to use 3rd party components in the IDE as well.</P><br /><P>"Do it my way, at my price, or not at all."</P><br /><P>Shame on you, Microsoft. Shame on you.</P>Unknownnoreply@blogger.com0