www.codinghillbilly.com   kyle.baley.org  Subscribe / Contact
 
 
 
 
LATEST POSTS
Tuesday, May 02, 2006

download the source code (292Kb)

In Part 1, we established how our search was going to perform and described a sample domain object with some properties we will use in our search criteria. Here, we'll talk about the SQL that we'll be using to generate our search results.

To that end, there is code attached with the first draft of our search engine. It is functional in that it will allow you to specify some search criteria and display the results. It is pretty in that it is not. I'm the Salieri of UI; I can recognize when it's done right but am average (at best) at building them. For this example, I chose not to try.

I haven't addressed any of the client-side improvements mentioned in part one. But the topic of this article will be at the other end, in the database. The search results will be expanded upon in a future article. For now, we're interesting only in getting results.

Final cop-out: I don't usually use SqlDataSource in my web apps. The exception is for samples and demos which, frankly, is the only reason I can think of to use them. Having said that, they rock for samples and demos.


Until relatively recently, I was under the impression that search queries had to be done using hard-coded SQL. There are two main reasons for this.

First is the multi-select listboxes and checkbox lists. With these, a user can select one or more items on which to filter. For example, let's say the user selects four different project types. That means, of course, he or she wants all Moos that have any of those project types attached.

In SQL, this translates to:

      SELECT      *
      FROM        Moo
      WHERE ProjectType IN ( 1, 2, 3, 4 )

It's the IN criteria that presents a problem. We can't pass in a comma-delimited list to replace ( 1, 2, 3, 4 ). SQL Server simply doesn't allow this. It won't throw an error if you do this but trust me on this, it just doesn't work.

The second issue has to do with the part of the search screen where you specify how to search for the name. I.E. The radio button list where you specify "Starts with", "Ends with", etc.

In this case, there's no getting around the fact that we're going to need some conditional logic. And in SQL, that means either an IF statement or a CASE statement.

But each of these would need to occur in the WHERE clause. For example:

          SELECT    *
          FROM      Moo
          WHERE

                   IF @searchMethod = 'startswith'
                       Moo.Name LIKE @name + '%'
                   ELSE IF @searchMethod = 'endswith'
                       Moo.Name LIKE '%' + @name
                   ELSE IF @searchMethod = 'isexactly'
                       Moo.Name = @name
                   ELSE IF @searchMethod = 'notspecified'
                       Moo.Name LIKE '%'

And so on and so forth. But this SQL is, of course, just plain wrong. I don't mean wrong in the philosophical sense. I mean in the "it won't compile" sense. The same is true for putting a CASE statement in the WHERE clause in a similar fashion.

Alternatively, you could use the IF statement as follows:

          IF @searchMethod = 'startswith'
                SELECT    *
                FROM      Moo
                WHERE     Moo.Name LIKE @name + '%'
          ELSE IF @searchMethod = 'endswith'
                SELECT    *
                FROM      Moo
                WHERE     Moo.Name LIKE '%' + @name

And so on and so forth. Granted this will work in this case but that's because of the lack of imagination I put into the example. There are other cases where a conditional in the WHERE clause is unavoidable. Besides, the actual SQL being executed in the various cases is a lot more complicated than in this example and having that copied and pasted throughout the stored procedure is nigh unmaintainable.

My solution to this problem was initially to build me a SQL statement in .NET code and execute it. This is usually a pretty reasonable approach. Easy to implement and easy to follow, even if the SQL that you generate isn't. A purist will tell you that it should be in a stored procedure because of the performance and the security concerns and the fact that the DBA just read in a book somewhere that hard-coded SQL was bad.

But in many situations, the performance hit won't be noticeable and depending on your requirements, the security aspect may be moot. The application that spawned this series of articles is an application for my brothers' and dad's company to search for land surveying jobs they've done in the past. Total number of users: six. Security concerns: none. They've been running the current version of the app on hard-coded SQL for over five years now and the only complaints I hear are on lack of features. But what can I say? It's an ASP app and my psychiatrist has forbidden me from looking at it ever again.

But I'm older and wiser now and always on the lookout for ways of making my code not look like it was done by someone just out of high school. So I needed a way of addressing these two issues.

The problem with the delimiited list, it turns out, is pretty easy to solve. I'll describe the thought process I'd like to think I would have gone through had I solved it myself rather than just searching Google Groups.

Like most things in SQL, there are many ways of approaching a problem. Do you use INNER JOIN or a filter in a WHERE clause? Temporary table or table variable? Return a rowset of one column and row or a scalar?

When we examine the problem of filtering on a delimited list of values, we should consider that maybe there's another way of filtering rather than using IN or even WHERE clauses. Namely, using INNER JOINS. What if we had a table of ClientIDs that contained only the ones we wanted to filter on? Then we could do an INNER JOIN on that table, automatically filtering out the ones we don't want.

We know we can declare table variables so our problem reduces to: How can we convert a delimited list of values into a table? Again, judicious use of Google keywords leads us to a SQL function that does just that.

In the database, you'll notice a function called UTILfn_split, which I found here. The function is easy enough to understand from a black box perspective: you pass in a delimited list and a delimiter and you get back a table.

In three places, you'll see code that looks like the following:

      -- Get a list of ClientIDs
      IF @clientIDList IS NOT NULL AND @clientIDList <> ''
      BEGIN
            INSERT INTO @clientIDTable
            SELECT            [Value]
            FROM        dbo.UTILfn_split( @clientIDList, ',' )
      END
      ELSE
      BEGIN
            INSERT INTO @clientIDTable
            SELECT            ClientID
            FROM        Clients
      END

In words: If a list of client IDs was passed in, populate my client ID table with the values in the list. Otherwise, populate it with every client ID in the system.

The reason for the IF statement, of course, is that an @clientIDList indicates that we don't actually want to filter on clientID. But we still need to populate the table with something because otherwise, we'd have some conditional logic to determine if we want to do an INNER JOIN, which as far as I know, isn't possible.

Now that we have a table variable with all the client IDs from our filter, the rest of the SQL is easy:

      SELECT      *
      FROM        MOO m
      INNER JOIN  @clientIDTable ct
      ON          m.ClientID = ct.ClientID

That is: Get me all the values of Moo that have a matching Client ID in our @clientIDTable variable.

The second issue isn't quite as clean but still lends itself to stored procedures. Earlier, I mentioned that we would need some sort of conditional construct in the WHERE clause to determine how to filter on the Moo Name. While this is true, I kind of lied when I said we were limited to IF and CASE.

This is probably easiest to discuss after looking at the WHERE clause:

      WHERE
       (
                 ( @nameFilter = 'startswith' AND m.Name LIKE @name + '%' )
       OR        ( @nameFilter = 'endswith' AND m.Name LIKE '%' + @name )
       OR        ( @nameFilter = 'exactly' AND m.Name = @name )
       OR        ( @nameFilter = 'contains' AND m.Name LIKE '%' + @name + '%' )
       OR        ( @nameFilter NOT IN ( 'startswith', 'endswith', 'exactly', 'contains' ) )
       )

So what kind of magic is this? This is easier to understand if you look at the individual pieces of the clause (i.e. the ones separated by OR). The first segment says:

      ( @nameFilter = 'startswith' AND m.Name LIKE @name + '%' )

This is a little easier to follow. Return all results where Moo.Name starts with the specified value but ONLY if @nameFilter = 'startswith'. If @nameFilter doesn't equal 'startswith' this, of course, will fail and we move on to the next OR to see if that succeeds:

      ( @nameFilter = 'endswith' AND m.Name LIKE '%' + @name )

This is a very similar condition. Return all results where Moo.Name ends with the specified value but ONLY if @nameFilter = 'endswith'.

Looking at the two conditions together, if @nameFilter = 'startswith', the first condition will be met and we'll filter on @name + '%'. If @nameFilter = 'endswith', the first condition will not be met but the second one will and we'll filter on '%' + @name. If neither condition is met, we move on to the rest of the statement.

The trick here is to ensure that all possible cases are covered when you compare against @nameFilter. That's why the last condition is:

      ( @nameFilter NOT IN ( 'startswith', 'endswith', 'exactly', 'contains' ) )

This is a catchall. By including this, it means that if @nameFilter doesn't equal one of the previously handled values, don't filter on Moo.Name at all. Which is why there isn't a filter condition after it as in the other ones.

Using these two techniques, along with the usual SQL suspects, we are able to get a pretty robust search mechanism in our stored procedure. If we wanted to, we could also combine this procedure with Microsoft Index Services. This would allow us to not only search metadata about a group of documents, but also search the contents of the documents themselves. A cost-effective way of doing document searches for cheap-ass family members who don't want to buy Windows 2003 for Sharepoint Services.

I should mention that there is now another way of addressing this problem, thanks to SQL Server 2005. That is, you can use SQL Server's CLR integration to run managed code directly from the database.

I'm making it sound like I know more about this than I do but I haven't actually done this in any way, shape, or form. From what I've heard, though, it is probably a very viable alternative, provided you are targeting SQL Server 2005 in your application. At the very least, you could probably replace the UTILfn_split function with managed code as it probably runs much cleaner and faster in the CLR. And I'm guessing you could make use of an enum for the @nameFilter parameter which would be a little less unstable. I'm sure I'll blather on this when I start experimenting, though.

Finally, credit to http://jargon.watson-net.com/ for the web service I used to generate the data. At present, the database has only fifty entries in the Moo table but if you navigate to SearchPontification/GenerateData.aspx, you can generate more. Note that since this is a web service, it will take a bit to generate each word so be prepared to wait if you put in more than, say, 100.

Tuesday, May 02, 2006

So on the recommendation of alert (and as far as I can tell, only) reader Brian Sherwin, I checked out qliner's HotKeys application, an open source Windows key mapping utility. At first glance, it appeared to do all I wanted and more.

Here's what I liked:

  • Very clean interface. The online keyboard gives a great overview of which keys are mapped to which functions. The developer clearly put a lot of effort into little details like making icons animate to the bottom right corner of the key when they are moved.
  • The volume control is great. It was this feature that *almost* made me dump WinKey.
  • Support for different keyboards (which, I admit, I would never use).
  • Open source and written in .NET. And the developer appears really responsive to feedback and eager to address issues.

But there are two things that keep it from replacing WinKey:

  • The keyboard stays onscreen only as long as you hold the Windows key down. Let go of the Windows key and the keyboard goes away. Not only is this awkward, it's a little confusing if you let go of the key while a dialogue or context menu is open.
  • No support for Shift+WinKey, Ctrl+WinKey, or Alt+WinKey. In WinKey, I have Win+G mapped to Google and Shift+Win+G mapped to Google Groups. I'm just getting into Windows key mapping and will probably use other similar combinations.

Both issues have been brought up in the forum and I have no doubt they will be addressed shortly, at which time, I'll promptly switch from the all-but-defunct WinKey. There is also talk of being able to override the default Windows key combinations which will be sweet. And if the developer is smart, he'll open it up for others to add their own controls similar to the volume control. Like Media Player controls (i.e. Play, Pause, Next, etc.).

Friday, April 28, 2006

There are two phrases that I simply don't allow people to say to me. They are: “quick question” and “simply”. Depending on its usage, “just” is just as questionable. (Yes, I know I've used two of the forbidden phrases in the last three sentences. The Coding Hillbilly is nothing if not a grammatical and hypocritical enigma.)

Nothing good ever follows these words so I have some canned responses when I hear them:

  • You ask the question and I'll tell you if it's quick
  • And why don't you “simply“ fix that ticking sound in your car engine?
  • I'll get right on that. I “just“ have to rebuild this server first.
  • The question may be quick. The answer won't. (Memory's fuzzy but I believe credit goes to Rajeev for that one.)

Any other verboten words and phrases I should add? I'm always up for a little personal censorship.

Monday, April 24, 2006

I read an interesting discussion somewhere a couple of weeks ago on comments. Pretty much everyone in the discussion was unanimous in their stance: if you write your code well enough, comments are a waste of time and may even be dangerous.

I shall rebut from afar because I don't remember where I read this and even if I did, I don't feel strongly enough about the topic to start a flame war. Because I just know I'm going to end up calling these guys idio--oh bother...

Some interesting points were brought up, two of which I remember. Firstly that if code is written well, it should be clear what it is doing and comments are moot. Secondly, since developers are generally a lazy species of human, we don't generally update them as often as we should and over time, they can end up being flat out wrong.

Seems to me that these are contradictory. I.E. If a developer is too lazy to update comments, do we trust him or her to write code that is easy enough to read without accompanying documentation? But I'll forego my analysis into the mind of a programmer and argue why I write comments religiously.

For the most part, I'm a .NET developer and mostly in C#. Visual Studio has colour coding for a reason. Comments are in green, keywords blue, code black (barring customisation). Which makes it much easier to tell where one function ends and another begins, provided you preface every method with a comment. And even if you're too lazy to type “this method does blah”, how hard is it to type /// at the beginning of each method to at least put in the

stub? Even when you look at code outside Visual Studio, it's easier to pick out the methods when there are three lines of /// before each one.

My second point addresses the misguided and delusional idea that code can always be clear. It can't. Plain and simple. I write business apps in which the problems are pretty easy to decipher and there are still algorithms that can get pretty complicated. In many cases, there are half a dozen ways of doing something. Maybe you've tried two or three and run into problems. Throw some comments in to the effect of “I tried this with a serializer and it threw this error.“ That way, when the serialization expert sweeps through, he'll have the benefit of your hard work before he runs into the same error during a refactor.

But let's assume you are able to create this universally comprehensible code that describes exactly what your function is doing. What about a piece of complicated business logic that spans several functions, and possibly several classes? Sure you can glean what you're doing from the individual function names and their internals but what would you rather read: well-written code in several places or even rudimentary comments in one place?

I'll grant that there is a danger that comments can become outdated over time. That's to be expected (but not necessarily accepted, which it generally is). I would counter that the same argument can be made about well-written code. On the first pass, you create magically-named functions and variables which have no double meanings (which means they clearly don't contain any English words). Then during your QA, a tester says: “Wait a minute. You're supposed to discount the interest here, not compound it.”

So you fix the code, then update all your function names that say “compoundMyValue” so that they now say, “discountMyValue”, right? Of course you do. No matter how close you are to deadline, you always have time to rename the functions in your entire project so that they are abundantly clear for the support person that's coming in tomorrow. The unit tests will catch any you've forgotten because, naturally, you've been keeping all those up to date, too.

Ignore the naysayers and for Jayzus' sake, comment your code. How hard is it really? It can be fun. It's one of the few places we can be creative. You don't need to go overboard and comment every line and every variable. But putting in something that says “Create another hashtable to cache our assemblies as we load them“ can be pretty useful for the next person that has to read your tripe.

And if you really want to stand out, make it sound like the Swedish Chef wrote the code.

Monday, April 24, 2006

I think I'm finally getting used to user instances in SQL Server Express to the point that I now know that they're called “user instances”. The annoyance I mentioned in a previous post is explained here. Specifically:

The other main issue with user instances occurs because SQL Server opens database files with exclusive access. This is necessary because SQL Server manages the locking of the database data in its memory. Thus, if more than one SQL Server instance has the same file open, there is the potential for data corruption. If two different user instances use the same database file, one instance must close the file before the other instance can open it.

Who knew reading documentation could be so helpful? We've come a long way in such a short time.

The net effect of this is that it'll be a while before I change my habits enough to use SQL Server Express effectively. And frankly, there are only two cases I can think of for using it at all:

  • Windows Forms applications that use a local database (i.e. the database is for that instance of the application and nothing else)
  • Web applications built specifically for people to download as test code

The second one is where I first encountered SQL Server Express which is why I had all my problems. I saw this database in some sample code and said, “Cool, I can work with the database directly in Visual Studio” not knowing the repercussions of my funky way of debugging. In the meantime, I'll stick with SQL Server Developer Edition and export to an Express database should the need arise. (*UPDATE*: Easier said than done)

Also, it looks like there are a bunch of SQL Server downloads this week, including SP1 for all versions of SQL Server (NOTE: SP1 for Express is a separate download than other versions). Includes a full-fledged, non-CTP version of Management Studio for SQL Server Express so if you're using that version, have at it. Some other related downloads with Advanced Somethings and Toolkit Thingies but none of them looked interesting enough just yet.

PART 2
Was going to post this separately but I don't want to look like I'm padding. Plus it's related to Part 1

I've discovered another reason I don't like SQL Server Express. In another post, I mentioned that I love me some SQL Profiler. This is even more important in the Visual Studio 2005 world because of how easy it is not to write code. I've been playing with binding GridViews to ObjectDataSources and it's pretty cool when everything works. Last night, I went through this long exercise of building up the database for the second part of my search article and building a quick search interface that uses a GridView bound to an ObjectDataSource.

So I configured my table adapter, configured my GridView, configured my data source, and clicked GO. Net effect: a blank page. No errors, no warnings, just a blank page.

So what went wrong? I can't tell because: a) there's no code to step through, and b) even if there was, it's not going to tell me exactly what SQL was executed. SQL Profiler, on the other hand, can. I started up a profiling session, refreshed the page, and there it was: the exact stored procedure call complete with parameter values.

By the way, I've found that you can get more use out of Profiler if you set up your own template with a filter of: Application Name LIKE .Net SqlClient Data Provider. This ensures only activity performed by your application is recorded in the trace. I imagine you can probably tweak the included TSQL template to do the same thing but I'm not much of a DBA. Once I get something working in that regard, I just want to get back to my code.

Last little whing against Express, I promise. I use a subst'd drive for my projects, in this case P:. All versions of SQL Server work less than well when you try to connect to them from a subst'd drive. So when I throw a database into my app that is resting in P:\RumRunner, I get errors from the git go. I essentially have to toss out the solution and reconnect to the app from the absolute path.

This problem might go away because I've since learned about junction over at www.sysinternals.com. I'm giving some consideration to this approach rather than subst'd drives after listening to a podcast on the tool. (OK, it was Hanselman's podcast again but I'm not linking to him because he's gotten enough press from me. Oh, all right.)

But now that I think about it, I like having a separate drive for my projects. More than I like having to navigate through the tree view in Management Studio to get to database files the long way. And the obsessive compulsive in me is not going to take lightly to a bunch of junction'd drives cluttering my C: drive. Then again, the ADD in me might win out long enough to try it...

Saturday, April 22, 2006

Phew! I really need to be more careful when I promise to pontificate on a topic. Either that or lower my standards when writing them. As it is, I could spend at least another few days making the sample code a little more presentable.

Part 2 of the search article is up. I've converted it (and part 1) to an article to make it somewhat more permanent and so it doesn't get lost among the trappings of my other meanderings.

A couple of extraneous tidbits I kept out of the article:

  • I haven't spent much time on the UI because I wanted to focus on the SQL this time around. Besides, it takes me a while to come up with a decent UI that I'm remotely happy with and I have other projects I should be working on at the moment.
  • Something new with BoundColumns: If you want the DataFormatString to work, you need to specify HtmlEncode="false". At least for date columns. Don't know why and don't care much.
  • There's no validation anywhere. In the page where you generate data, it's expecting a number and will throw an error if you don't enter one. Same with the textboxes on the search page. I didn't put any thought into what would happen when you start throwing in funky characters.
  • If you don't select anything in the listboxes or the Locations checkbox list, the search will not filter on that column. I.E. It will assume you've selected all values. This allows you to open the search page, click GO, and have all search results returned. This may seem reasonable but in a future article (probably the next one), I'll explain why this isn't how I would normally approach this.
  • One thing that kind of annoyed me about ListBoxes and CheckBoxLists in .NET 1.0 and 1.1 is that you always had to iterate through the Items collection to get all the selected items (if they were multi-select listboxes). I'm still annoyed in .NET 2.0.

So without further adieu, check out the article.

Saturday, April 22, 2006

My history with multi-part topics is spotty but I think I may stick with this one for a while. I'm going to talk about implementing searches in applications and I have no delusions that it will all fit in one post.

The searches to which I'm referring aren't Internet searches or desktop searches. In many ways, they're much simpler. They're searches many of you have probably implemented in your own coding hillbilly lifetime. They're application specific searches. As in: You have built a web site for a company that lets users add, edit, delete, list, define, and otherwise mangle widgets of some sort. And one of the features you want to implement is a search. I.E. The user selects some search criteria, usually based on your widgets' metadata, and you display the results.

Sounds simple enough but it isn't really. There are some pretty nifty little problems that many users probably don't even recognize (which, frankly, are the best problems to have but we'll address them anyway).

I'm going to focus specifically on web sites because they present some interesting challenges over and above Windows applications. I'm going to start off small and define a basic search and some properties you may not have considered.

And to do this, I'll work with an example. In my sample application, we're going to deal with an object called Moo. The metadata for a Moo includes:

  • ID
  • Name
  • Description
  • Order date
  • Client
  • Project Type
  • Office Location
  • Address

We have already built the application to add, edit, and delete Moos from our database. Now we want to build a search application that will display a list of Moos based on selected criteria. And our search page will allow the user to filter on any combination of metadata except for the ID. That is, the user could say: Show me all Moos with a name that starts with “Vending” for our Headingly office that was ordered in 2005. Not so simple anymore.

Also, here are a list of options I think all good search engines should have and that we will try to include in our sample:

  • Ability to page
  • Ability to sort by an arbitrary field
  • Ability to select the number of results per page
  • Ability to expand results to show more information
  • Ability to bookmark any page at any time

That last two are going to be the wrinkles. ASP.NET can handle the first two almost entirely with built-in functionality. But they do it with postbacks. We can also add a control to the pager to let users change the number of results per page but again, that will require a postback. Same with expanding results.

But what good is a search engine if it's a one time thing? Especially in a business application where users are more likely to perform the same searches over and over again as the data changes. Besides which, there's nothing stopping someone from bookmarking a page anyway whether it works or not and they'll be mighty ticked when they go back to it and find out it's useless.

So in the coming posts, we'll see if we can build a search engine that espouses these features but doesn't lead us down the path of hard-coded SQL, querystring management, and complicated javascript. A say “too far” because I must confess, I haven't built such a search engine myself in .NET just yet. I'm working on one now so you, my fellow programming hicks, can learn right along with me. I'll post sample code as it becomes available and reserve the right to revise the requirements when I run up against a problem that is too hard.

Saturday, April 22, 2006

(NOTE: This post has been converted into an article.)

My history with multi-part topics is spotty but I think I may stick with this one for a while. I'm going to talk about implementing searches in applications and I have no delusions that it will all fit in one post.

The searches to which I'm referring aren't Internet searches or desktop searches. In many ways, they're much simpler. They're searches many of you have probably implemented in your own coding hillbilly lifetime. They're application specific searches. As in: You have built a web site for a company that lets users add, edit, delete, list, define, and otherwise mangle widgets of some sort. And one of the features you want to implement is a search. I.E. The user selects some search criteria, usually based on your widgets' metadata, and you display the results.

Sounds simple enough but it isn't really. There are some pretty nifty little problems that many users probably don't even recognize (which, frankly, are the best problems to have but we'll address them anyway).

I'm going to focus specifically on web sites because they present some interesting challenges over and above Windows applications. I'm going to start off small and define a basic search and some properties you may not have considered.

And to do this, I'll work with an example. In my sample application, we're going to deal with an object called Moo. The metadata for a Moo includes:

  • ID
  • Name
  • Description
  • Order date
  • Client
  • Project Type
  • Office Location
  • Address

We have already built the application to add, edit, and delete Moos from our database. Now we want to build a search application that will display a list of Moos based on selected criteria. And our search page will allow the user to filter on any combination of metadata except for the ID. That is, the user could say: Show me all Moos with a name that starts with “Vending” for our Headingly office that was ordered in 2005. Not so simple anymore.

Also, here are a list of options I think all good search engines should have and that we will try to include in our sample:

  • Ability to page
  • Ability to sort by an arbitrary field
  • Ability to select the number of results per page
  • Ability to expand results to show more information
  • Ability to bookmark any page at any time

That last two are going to be the wrinkles. ASP.NET can handle the first two almost entirely with built-in functionality. But they do it with postbacks. We can also add a control to the pager to let users change the number of results per page but again, that will require a postback. Same with expanding results.

But what good is a search engine if it's a one time thing? Especially in a business application where users are more likely to perform the same searches over and over again as the data changes. Besides which, there's nothing stopping someone from bookmarking a page anyway whether it works or not and they'll be mighty ticked when they go back to it and find out it's useless.

So in the coming posts, we'll see if we can build a search engine that espouses these features but doesn't lead us down the path of hard-coded SQL, querystring management, and complicated javascript. A say “too far” because I must confess, I haven't built such a search engine myself in .NET just yet. I'm working on one now so you, my fellow programming hicks, can learn right along with me. I'll post sample code as it becomes available and reserve the right to revise the requirements when I run up against a problem that is too hard.

Wednesday, April 19, 2006

If I can break character for a moment:

Oh my lord sweet Jayzus, Joseph, and Mary, the stars of Phantom of the Paradise are coming to Winnipeg!!! Swan! Winslow! Beef! THE JUICY FRUITS!!! Non-Manitobans probably don't understand the reason for my excitement (or my disappointment that I can't go) but then, the movie probably didn't play for over a year in your city. For whatever reason, it was a phenomenon in Winnipeg. I missed the theatrical release but have seen the movie more times than I can count (and am severely ticked at the lack of a decent DVD version).

LIFE AT LAST!

Wednesday, April 19, 2006

You'd never know from the way I bitch on this thing that I'm generally a very optimistic person. Oh well, may as well run with the theme and post a couple of minor annoyances.

The first one is best displayed with a picture of my Windows Explorer. Notice the folder list on the left.

This has been a problem I think since I installed SP2 but I can't be sure. The closest I came to finding out what's going on is this KB article which says it could occur if you place a folder called Desktop on your desktop. I don't have this so that's not the problem. What I believe *is* causing this is that I have a Desktop toolbar on my taskbar. And I love having my Desktop toolbar there more than I hate having multiple Desktops appearing in Explorer.

The second annoyance is unconfirmed. It appears there is no way to remap your Fn key nor is there a way to override the default Windows key combinations. I adore the Windows key and have been using it for many years. Even Dell's absolutely ridiculous positioning of it in the top right corner of my laptop keyboard hasn't slowed me down. I'd love to talk to the genius who decided the heavily used Windows key deserves to be banished to the right field of my keyboard while the Fn key plays shortstop in the bottom left 'twixt the Ctrl and Alt keys.

So after being inspired by Scott Hanselman's nigh infinite list of tools, I tried out SlickRun enough that I'll keep it on my machine. But he has this seemingly innocuous throwaway little tidbit in its description: bind it to Window-R. Three hours later and I've just realized that the beginning of that sentence was “read the instructions and edit the slickrun.ini file”. Which of course I didn't do because I've spent the last...shall we say, “little while” trying to find a way to remap the built-in Windows key combinations to different commands. I've tried various keyboard mappers, WinKey, and some funky Microsoft Keyboard Layout thingy. And I had just come to the conclusion that it wasn't possible until I read the SlickRun instructions and find out that they've done it. How? HOW?!?

But during my search, it occurred to me I could remap the Windows and Fn keys to make them switch functions, thereby bringing my keyboard a little closer to being useful for people who don't have freakishly long fingers. But it also appears that the Fn key, at least for Dell, is hard-wired to some BIOS thing and blah, blah, blah. The short answer, which took me forever to discover because no one wants to come out and say it, is that it can't be done. And the title of this post is designed specifically to show up on search engines so that others' hopes can be dashed as mine were. What can I say? All this altruism in the software world has to be counter-balanced with a dose of spite every once in a while.

*Edit* Given the number of hits this post is getting, I've renamed this post to something more appropriate so as to pre-empt any hate mail I get from people who would rather take the time to send me an e-mail than to click the back button.

Friday, April 14, 2006

In my last post, I made a reference to my taskbar so I thought I'd write a bit about it. Here it is (click for a larger image):

There are a couple of things I like about this setup that others may not know. Firstly are the Administrative Tools, Control Panel, and Desktop menus in the bottom right. If you right-click the taskbar and select Toolbars, you'll see a list of toolbars you can add. If the one you want isn't there (e.g. Administrative Tools), click New Toolbar... and browse to it. I put a lot of stuff on my Desktop so I love having it available without having to minimize everything.

This is also handy at some client sites if you are constantly using Remote Desktop to get to various servers in different environments. In this case, I'll create a folder (called, say, Remote Connections), then place an .rdp file in it for each server. Add a toolbar for that folder in the taskbar and voila! Remote desktop access to any server in two clicks. I also had a toolbar for my Links folder at one point, too, but I didn't go to it often enough to justify the real estate. But if you do, that's another good candidate toolbar.

The other nice thing is the address bar embedded in my taskbar. This lets me type in my URL without having to open my browser first. It will open your default browser, too, so it works for Firefox. I also use the TweakUI power toy which allows me to create keywords for sites I search regularly like Google, MSDN, and IMDb. So I can click in the address bar, type: “imdb glengarry glen ross” (without quotes) and have the results show up in my browser without having to open it explicitly. NOTE: You'll probably want to uncheck the “Reuse windows for launching shortcuts” option in the Advanced properties of Internet options if you decide to do this yourself.

Of course, this is kind of moot if you already use WinKeys or SlickRun to their full potential but it has the advantage of having your URL history available at the click of a button.

Thursday, April 13, 2006

I believe I have some goodness happening finally but first, I want to clarify something from my last point. And zeroth, I want to plug www.calgarycodecamp.com, mostly because I'm presenting at it.

My second complaint was a little vague and upon reflection, not a fair annoyance. What I can do in ASP.NET 1.1 is launch my web app in IE while the project is open in Visual Studio 2003. Which, of course, you can do in version 2005. But when I press F5 in Visual Studio 2003, it will launch whatever the start page is for the app but the debugging process also attaches to my existing instance of IE. This means that I can refresh my existing IE page and if there is a breakpoint on it in VS, it will be hit. I like this because 80% - 90% of the time I don't need breakpoints so I work normally in IE. But when I DO need them, I don't need to change my start page. I can just set a breakpoint, press F5, then refresh my existing IE instance.

In Visual Studio 2005, web projects, by default, launch in their own little web server that I think gets created the first time you launch. The web server uses some random (and I assume open) port on your machine and I remember reading somewhere that you could specify the port used for the web server but I haven't got around to looking into that. So my whiny little complaint was that I can't have the app open in IE on localhost and have the debugger attach to it because Visual Studio will launch the app on a different port on its own web server.

If you haven't figured out why this is a stupid, petty thing to bitch out, here's why: If I have the app open in IE, then obviously I've created a virtual directory for it in IIS. So why not just have Visual Studio open the URL in the default web server instead of its custom server? It's moments like these that make me wish my pappy didn't marry Aunt Bobbi-Sue.

As for my SQL problems, they've gone away, too. How? By scrapping SQL Server Express and installing the Developer Edition of SQL Server. I spent last night having one more kick at the Express cat and I just couldn't connect to the database in more than one app at a time. When I loaded it in IE, I had to disconnect in Visual Studio. When I loaded it in Visual Studio, I had to disconnect from it in Management Studio.

So as I regale you with this story, my machine is currently and concurrently connected to my database in three different ways. Not only that, my Profiler is back! Maybe later I'll tell you how much fun it was to debug a grid that returned absolutely nothing without using SQL Profiler.

So hopefully, I'll finally be able to get more technical in this virtual hootenany in the coming days. 'Cause I gots lots to say about this Atlas thingamabob.

Monday, April 10, 2006

So I've been developing in ASP.NET 2.0 and SQL Server Express and I'm happy with them in general. Some of the changes are a little jarring, though. To the point where they're starting to hinder my progress enough to waste time complaining about them:

Firstly, is the idea of web sites being a different type of project. I.E. There's no .csproj file. I read about why they did this and promptly forgot because frankly, I don't care. What I do care about is the fact that when I download code samples, there isn't anything I can double-click to open it in VS 2005. Yes, I can open VS 2005 and select Open Web Site... You can also click File | Close to close most of your apps but when was the last time you did that? What if you were forced to?

*UPDATE* Thanks to www.hanselminutes.com for pointing me to this little number to fix this annoyance. Some great-looking additions, too. Like the ability to create a virtual directory from within VS2005 and publishing to an FTP server.

Secondly is the method of debugging. I like that you don't have to create a website in IIS for every web project you're working on. But here's my problem with that: I don't generally run web apps through VS 2005. My method of debugging is old school. I open the project in Visual Studio and I open the site in IE. I make changes in Visual Studio, recompile, refresh the page in IE, and repeat. Takes too long to start the formal debugging process in VS just because I have a typo in a property somewhere.

If I do need to debug with breakpoints, I usually have a benign starting page that loads quickly. I hit F5 until that page loads and I now have the benefit of having the debugger attached to my IE processes. I can now go back to my real IE instance and refresh the page to hit the breakpoints.

This isn't possible without doing things the .NET 1.1 way and creating an IIS site for my apps in progress. I believe, but have not verified, that I could get around this by starting the app from VS 2005 once which will create the web server port I need for that session. I also believe this port will go away when I close Visual Studio. Again, a workaround that slows me down.

Related to this is my final beef. I'm constantly getting “cannot open user default database” errors. Not when I start the project from VS 2005 but almost always when I load the site up directly in IE. The error goes away if I disconnect the database in VS 2005 but that is NOT a workaround I'm willing to put up with. As far as I can tell, the same thing happens whether you work with the database in VS or in SQL Management Studio. For now, I'm able to things going by using a SQL login in my connection string. Again, not the ideal situation but at least I can go back to work.

If anyone has details on any of these issues, don't be shy.

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Copyright © 2010 Kyle Baley. All rights reserved.
 
CATEGORIES
.NET General (18) alt.net (4) altnetconf (9) ASP.NET AJAX (40) ASP.NET MVC (29) Bahamas (1) Bahanet (9) BDD (1) Brownfield (18) Career (9) Castle (1) Code coverage (1) Coding Style (6) Communication (1) Community (18) Conscientious Coding (34) Continuous Integration (11) dasBlog (12) Development (16) DevTeach (4) Domain (2) Environment (4) Estimating (1) Featured (14) Flamingo (10) Games (1) Google App Engine (2) GWT (5) Hardware (6) Java (1) Javascript (7) Linq (2) Livelink (6) Lucene.NET (2) MbUnit (1) Metrics (1) Miscellaneous (24) Mocking (4) NAnt (4) NHibernate (12) NInject (1) Office (3) Office Development (6) Open Rasta (1) Patterns (5) Presenting (13) Professional Development (15) Refactoring (10) ReSharper (11) REST (2) S#arp Architecture (5) Security (3) Software (11) Sundry (18) TDD (19) Tools (21) User Interface (5) Utilities (8) Visual Studio (8) VSTO (1) Web development (12) Windows (3) Working Remotely (16) Workplace (3) Writing (4)
 
LATEST POSTS
 
POPULAR POSTS
 
 
ARCHIVE