April 2005 - Posts
I was thinking the other day about the changes over the years in what we call people who write computer programs. Back in the day, we called these folks computer programmers. A rather fitting title, one would suppose, for a person who programs computers. But one would suppose wrong.
Shortly after computer programmer became the "official" title, someone, somewhere, somehow decided that it wasn't enough. After all, computer programmers do more then program, they analyze a problem and then write a program. They should, therefore, be titled programmer/analysts. One would suppose that such analysis is an implicit part of the job, much like how writers need to think about something before they actually write it. But one would suppose wrong.
Unlike the computer programmer title, programmer/analyst seemed to stick around for quite a while. In fact, it was only fairly recently that we all became software developers (or, developers for short). The change this time around was all about image; you gotta admit how much sexier developer sounds over programmer. Certainly one would suppose it's pretty hard to "sex up" an industry whose Steves out number its women (see the Steve Rule). But one would suppose wrong.
Believe it or not, developer is on its way out and we're in the middle of yet another title change. If you think about it, the problem with developer is that, if any one asked what a "developer" is, you'd have to expand it to software developer. Software == Computers == Programming == Nerdy. We can't have that!
This is where the title solution developer comes in. We're the guys who you call when you have a problem. Doesn't matter what the problem is, we will develop a solution. Heck, we can even develop solutions (by programming a computer) for problems that don't exist. We're that good.
But where do we go from here? First, we need to reach the maximum level of ambiguity possible. I'm not an expert at coming up with job titles, but I suspect solution specialist is a step in the right direction. Of course, once we've gone all the way to one side, the only place we can really go is to other extreme: a way more overblown/descriptive/nerdy sounding name than needed. When solution specialist (or whatever) expires, I really hope the replacement will end with -ologist. I would really like to be an -ologist of some sort. You know you'd like it, too.
Yesterday on The Daily WTF, I mentioned something I called the Steve Rule: in a random sample of programmers, there will be more named Steve then there will be females.
What's ironic is that I didn't mean that as a joke. In my personal experience, every group of programmers with fifteen or more always had more guys with the same name than women. No, it's not always Steve, but that was the name when I first made the observation (three Steves, one woman).
I thought I'd test the "Steve Rule" once again using the Weblogs.Asp.Net bloggers. To do this, I looked at the OPML [XML], and used the title of 258 blogs. There were 139 I skipped because either I couldn't tell the gender (no offense, Suresh Behera, et al.) or the title didn't have a name (e.g. Models and Hacks).
Of the blogs, I found six female bloggers:
For the guys, I was only able to find four different Steves. But, there were seven different names (with slight variations) that outnumbered the girls
- Andrew (7)
- Chris (7)
- Dave (7)
- Jason (7)
- John (7)
- Robert (7)
- Scott (9).
So, for us Weblogs.Asp.Net bloggers, it looks like we are goverened by the "Scott Rule." How about that?
Thought I'd share my review of James Avery's latest book ...
If you spend a fair amount of time writing code in Visual Studio.NET, then Visual Studio Hacks will definitely improve your productivity in writing, debugging, and maintaining code. You'll find everything from using (and the practical uses of) built-in features (such as the Clipboard ring) to in-depth explanations of downloadable add-ins.
One thing I love about the book is how easy the hacks are to implement. For example I've always been annoyed at the output from the build results window but never took the time to even think about changing it. It seemed to be just easier to deal with it than fix it. After reading Hack #35 (Modify the Build Output and Navigate the Results), I'm confident I can take a few minutes to write a macro and customize it as needed.
Worried about difficulty in writing a macro? Hack #51 explains it step-by-step.
Although I don't consider myself to be an Über-user of VS.NET, I do use the development environment quite a bit and know my way around fairly well. That said, Visual Studio Hacks has definitely augmented my knowledge of not only what VS.NET does, but what *good* 3rd party add-ins are available. I've had too many bad experiences with *bad* add-ins killing half-a-day of work after requiring a reinstall of VS.NET. The ones listed here work well and are documented well.
By the way, I have to say that I liked Hack #7 (Make Pasting into Visual Studio Easier) the most. But then again, it's pretty hard *not* like a hack that you contributed to the book ;-).
In this Community Server tip, I'll describe how to make a quick & easy home page that displays posts from one or more forums on one page.. If you're concerned that new visitors to your community will be turned off by the default listing of forums when they go to www.YourSite.com, then this just may be for you. I think you'll find that most of the time you spend on this tip will be in the actual design of the home page. This is what I do for TheDailyWtf.com.
Step One - Layout
The standard default.aspx that comes with Community Server contains the title banner, site navigation tabs, and an area with some explanatory copy ("Community Server is a rich knowledge management and collaboration platform designed ..."). Keep as much or as little as you want; I opted to replace the main area with a two column view: a left "side-bar" and a center posts. Before you start coding, you may find it easier to put in dummy place holder post text where your normal posts would appear.
Step Two - Default.aspx Header
At the top of the top of the page, you'll want to make sure that you have the appropriate references to the Community Server components. Here is what I have at the top of my file. Some of these may or may not be already in the existing default.aspx:
<%@ Page SmartNavigation="False" Language="VB" %><%@ Register TagPrefix="CS" Namespace="CommunityServer.Controls" Assembly="CommunityServer.Controls" %><%@ Import Namespace="CommunityServer.Galleries.Components" %><%@ Import Namespace="CommunityServer.Blogs.Components" %><%@ Import Namespace="CommunityServer.Components" %><%@ Import Namespace="CommunityServer" %><%@ Import Namespace="CommunityServer.Discussions.Components" %>
<%@ Page SmartNavigation="False" Language="VB" %> <%@ Register TagPrefix="CS" Namespace="CommunityServer.Controls" Assembly="CommunityServer.Controls" %> <%@ Import Namespace="CommunityServer.Galleries.Components" %> <%@ Import Namespace="CommunityServer.Blogs.Components" %> <%@ Import Namespace="CommunityServer.Components" %> <%@ Import Namespace="CommunityServer" %> <%@ Import Namespace="CommunityServer.Discussions.Components" %>
Note that I have Language="VB" at the top. Although Community Server is entirely C#, I prefer to code in VB and will whenever I get a chance. Ahh, the power of .NET.
Step Three - Add The Code
Just place this block of code and place it under your page header Import statements:
<script runat="server">
dim sideBar as ThreadSet
dim main as ThreadSet
sub Page_Load
sideBar = Threads.GetThreads( _
18,0,5, Users.GetAnonymousUser(), _
DateTime.MinValue,SortThreadsBy.PostDate,SortOrder.Descending, _
ThreadStatus.Open, ThreadUsersFilter.All,false,false,false,false)
main = Threads.GetThreads( _
12,0,5, Users.GetAnonymousUser(), _
DateTime.MinValue,SortThreadsBy.PostDate,SortOrder.Descending, _
ThreadStatus.Open, ThreadUsersFilter.All,false,false,false,false)
DataBind
end sub </script>
The code is very basic. It declares two ThreadSets (a group of threads within a forum), fills them when the page loads, and then binds the thread sets to the repeater (which we will build in Step Four).
Since the GetThreads() arguments are a bit intimidating, I'll explain them one-by-one, in order:
- forumID - The ID of the forum to get the threads for. For my site, 12 is the ID of the main forum and 18 is of the sidebar.You will definitely need to change this to the forum you'd like to display.
- pageIndex - Because a forum can contain more than one "page" of threads, you need to specify which page to retrieve. We're getting the threads from the first page, which is 0.
- pageSize - The number of threads to display per page. Both of my thread sets show five per page.
- user - The user who is requesting the threads. If you wanted, you could use the CurrentUser. I chose to display the threads as an anonymous user would have seen. I honestly think it makes little difference.
- threadsNewerThan - I want to show 5 threads, regardless of age. This is why I use DateTime.Min -- nothing can be older than that value. If you want to show the latest 10 days of threads, you could use Now.AddDays(-10) instead.
- sortBy - This refers to that drop down box in the sort options. Note that I'm using the custom one I discussed last time.
- sortOrder - This can be Ascending (old to new) or Descending (new to old).
- threadStatus - This can be Open, Closed, Resolved. You will most likely want Open
- userFilter - This can be All, HideTopicsParticipatedIn, HideTopicsNotParticipatedIn, HideTopicsByAnonymousUsers, or HideTopicsByNonAnonymousUsers. I think they're all pretty descriptive.
- activeTopics - true or false, indicates whether to show only active topics. An Active Topic is defined as (I believe) one where there was activity within the past seven days. I chose false.
- unreadOnly - true of false, indicates whether to only show unread. I think false is the best choice here.
- unansweredOnly - true of false, indicates whether to only show unanswered. I think false is the best choice here as well.
- returnRecordCount - true or false, indicates whether to return a record count or not. I said false because I'm not using a record count on the page
That wasn't so bad, now was it?
Step Four - Add the Repeater(s)
Now that we have the threadsets coded, we need to add some repeaters to display them. These are a very basic control to use. Here is a very basic code. As you can probably tell, this will have no styling whatsoever. This is all for you to add :-).
<asp:repeater runat=server datasource=<%#main.Threads%>> <itemtemplate> <h2><%#Container.DataItem.Subject%></h2> <div> <em><%#Container.DataItem.PostDate.ToLongDateString%></em>
<br/> <div><%#Container.DataItem.Body%></div> </div> </itemtemplate> </asp:repeater>
Note the datasource. Simply change that to whichever threadset (declared in step two) you'd like to display. Just play around with the formatting, and you're good to go. If you'd like, feel free to look at the source code of my default.aspx. Mostly you'll be able to see how I have links back to the post and some other things. It's pretty basic; you'll probably be able to figure it out by poking around.
I think that there's a general consensus out there that Exceptions should be limited to exceptional circumstances. But being that "exceptional" is a rather subjective adjective, there's a bit of a gray area as what is and isn't the appropriate use of Exceptions.
Let's start with an inappropriate use that we can all agree too. And I can think of no better place to find such an example than TheDailyWTF.com. Although that particular block of code doesn't exactly deal with Throwing exceptions, it is a very bad way of handling exceptions.
To the other extreme, exceptions are very appropriate for handling environment failure. For example, if your database throws "TABLE NOT FOUND," that would be the time to catch, repackage, and throw an exception.
But it's in the middle where there's a bit of disagreement. One area in particular I'd like to address in this post is exceptions to business rules. I mentioned this as an appropriate before, but noticed there was quite a bit of disagreement with that. But the fact of the matter is, exceptions really are the best way to deal with business rule exceptions. Here's why.
Let's consider a very simple business rule: an auction bid may be placed if and only if (a) the bid amount is higher than the current bid, (b) the auction has started, and (c) the auction has not ended. Because these rules (especially b and c) are domain constraints (i.e. they restrict the range of acceptable values), they are best handled by the Preserver of Data Integrity (some call this the "database"). To accomplish this validation, we'll use a stored procedure with an interface like this: procedure Place_Bid ( @Auction_Num char(12), @Bidder_Id int, @Bid_Amt money )
Now let's consider the layers of abstraction it actually takes to go from the user clicking the "Place Bid" button to the stored procedure being called:
•PlaceBidButton_Click()
•AuctionAgent.PlaceBid()
•IAuction.PlaceBid()
•--- physical tier boundary --
•IAuction.PlaceBid()
•Auction.PlaceBid()
•AuctionDataAgent.PlaceBid()
•SqlHelper.ExecuteCommand()
•--- physical tier boundary --
•procedure Place_Bid
Without using exceptions, it gets pretty ugly passing the message "Bid cannot be placed after auction has closed" from the stored procedure all the way back to the web page. Here's two popular ways of doing this:
- Return Codes - Have every method that could potentially fail return some sort of value. True/False is the most common but rarely provides enough information about the failure. Our PlaceBid function would need four different return codes: Success, Fail-LowBid, Fail-EarlyBid, Fail-LateBid. Of course, this technique fails when your method may need to actually return something other than the return code.
- Class Level Checking - For each of classes, add property called "LastError." This will contain an Error object that contains information about the last error (if one occurred). Simply check it after each operation.
- Output Params - Add an out paramter to every method to pass back an ErrorObject. This is similar to the aforementioned technique except it is on the method-level.
In all three cases, you need to manually "bubble" up the message from method to method. As you can imagine, this adds lots and lots of needless "plumbing" code intertwined with your business logic. Since it's at the method level, all it takes is one developer to not code a method to return the right code.
The proper way of handling the Bid exception is, naturally, with Exceptions. When you raise the error in the stored procedure code, indicate that the message is a business rule exception intended to be displayed back to the end user. After that, you only need to put try/catch blocks in two places, the ExecuteCommand() method and the PlaceBidButton_Click() method.
ExecuteCommand() Psedo-code
Try
sqlCmd.ExecuteNonQuery();
Catch ex As SqlExecption
If IsUserSqlException(ex) Then Throw ConvertSqlExceptionToBusinessException(ex)
End Try
PlaceBitButton_Click() psedo-code
Try
AuctionAgent.PlaceBid()
Catch ex As BusinessException
DisplayUserMessageFromException(ex)
End Try
Less code, less mess. Nanoseconds slower? Probably. A big deal in the context of a web request going through three physical tiers? Not at all.
More Posts