|
LATEST POSTS
Saturday, May 31, 2008
Couple o' quick productivity tips. The first I posted on Twitter yesterday. That is, in Visual Studio, you can map Ctrl-Alt-F4 so that it closes all open document windows. It's a noticeable time saver for me because I often get to the stage where I'm holding down Ctrl-F4 for about ten seconds waiting for it to close everything up. Instructions: - In Visual Studio, go to Tool | Options
- Under Environment select Keyboard
- In Show commands containing, enter Window.CloseAllDocuments. You should get a single entry in the listbox below it
- Put the cursor in Press shortcut keys and press Ctrl-Alt-F4.
- Click OK
You can use whatever combination you like but check to see if it's used by something else. I originally wanted Ctrl-Shift-F4 but ReSharper was using it for "Close recent tool". Haven't determined what that means yet. Next tip. When I get zip files, ninety-nine percent of the time, I do the same thing with them: Extract to <foldername>. That requires right-clicking on the file, navigating to the 7-Zip folder in the context menu, and selecting the appropriate option. I'm using 7-Zip as my archiver of choice primarily because it's free, fast, and hasn't gone the Paintshop Pro route of bloatedness like WinZip has. (NOTE: You can configure 7-Zip's context menu to get rid of things you don't use, like Zip and E-mail. You can also make the menu non-cascading. The options are a little buried in the application but you'll find them.) 7-Zip has a command-line version, as I suspect most archivers do. So after a browse through the documentation, I created a batch file as follows: @echo off
set zipPath=%~dpn1
"C:\Program Files\7-Zip\7z.exe" x "%1" -o%zipPath% -y
pause
I've completely forgotten what the value after "set zipPath=" means but it was on the interwebs here somewhere. Sorry folks, the Hillbilly can't be expected to remember everything he did last month. Like if he showered or not.
The next step here is to link this batch file to .zip files. I went the low-tech way by right-clicking a zip file, and selecting Choose Program... from the Open With context menu:
Select your newly-minted batch file and now zip files are extracted to <foldername> with a single click (or with an Enter for the keyboarders).
Notice that the zip file has a "pause" in the last line. That's precautionary and can be removed if you like. I added it so I could see if there were any errors during the process.
That is all.
Kyle the Productive
Friday, May 30, 2008
Need to be more on the ball with announcing community events that I'm hosting. The Bahamas .NET User Group had its second meeting on May 28 and it was another success. Running a user group is an odd kind of thing. There is more work than you imagined when you had the initial idea and you often get to the point where you wonder if it's worth it. Then the meeting starts and goes by in a blur and when you're driving home, you're wishing you could meet every week instead of monthly. It's a rush that is almost as good as...well, let's not get carried away. It's a good feeling is all. The group was seven strong, not including me. That's one more than last time, and that includes six newcomers. The topic was Building Websites in ASP.NET. The group was small enough, and the topic dear enough to my heart, that I could, and did, veer off in directions I didn't anticipate. At one point, I dove into the life cycle of an HTTP request, something I haven't stopped to think about in many a full moon. Still working on getting things to run smoothly. The e-mail I sent to the group reminding them of the date actually contained the wrong date. So between that and the last minute venue change of the last one, it looks like the third one will hopefully be the charm. The good news: I recorded the session. The bad news: The air conditioning didn't work and someone took mercy on me partway through by turning on the fan. A seemingly benign act of philanthropy but the fan happened to be directly over the microphone. Who knew air made that much noise when it moved? The final half hour is nigh-incomprehensible so I had to mute the entire thing and add a soundtrack instead. My apologies for that. Guess you're never to old to learn. Part 1 (47 minutes - 40MB - WMV) Part 2 (44 minutes - 37MB - WMV) Silverlight version may come later when I figure out how to encode them into files smaller than 185Mb each. NEXT MEETING IS TENTATIVELY SCHEDULED FOR Thursday, June 26 at 6:00 at IPBS House The topic is Connecting Web Applications to a Database but there is still room for discussion on that if there is enough interest in something else. And if you have any other suggestions or want to help out yourself, you know where to reach me. The most encouraging part of the last meeting: One attendee asked if we could meet more than once a month. Kyle the Adrenalized
Tuesday, May 27, 2008
Have been waiting for Derik to announce this for a while and it's finally happened. He's launched a new service called DimeCasts.NET. The site will be a repository for screencasts he's done/doing on various .NET topics. He's seeded it with a few already on unit testing and I believe there are many more on the way. There are two reasons I'm excited about this: - He's mandated a maximum length of ten minutes for each length. In fact, it's built right into the name. So there are no excuses for skipping any episodes. I've worked on projects where the build process takes longer.
- He's starting at the very beginning. Episode one shows how to set up NUnit in your project.
The second point means that I may not exactly get a lot out of the casts, at least not the early ones. But the first point means I'm at least going to check them out regularly, if only for the reinforcement. With all the talk of Entity Framework and Persistence Ignorance and specifications, it's easy to forget that a vast number of people simply haven't written a unit test and are looking for a way to get started. And frankly, I have a feeling we're probably scaring them to death most of the time. Derik has big plans for the site and I have correspondingly high hopes for it. So keep an eye on it and give him feedback. ** UPDATE ** Apparently, Whittaker is a scam artist and a fraud! Of the first four episodes posted, a full 75% OF THEM ARE OVER TEN MINUTES. The second one alone is a whopping TWO AND A HALF MINUTES over his mandate. I can skin two prairie dogs and have 'em on the grill in less time. Therefore, I must reverse my position on this. I don't care if this is, as I originally described it, porn for your career, you can't break the rules. They're there to keep you safe. So for the love of all that is hillbilly, do NOT click on this link. Kyle the Announcer
Saturday, May 24, 2008
This post falls into the "Google-able errors" category and won't be of much use to anyone who isn't get the specific error. Here's the error I was getting: Server cannot modify cookies after http headers have been sent This is in my ASP.NET MVC application and the offending code was the last line of the following method: public void SignIn( User user )
{
FormsAuthentication.SignOut( );
var issued = DateTime.Now;
var expires = issued.AddDays( 1 );
var roles = "Administrator";
var ticket = new FormsAuthenticationTicket( 1, user.UserName, issued, expires, false, roles );
var encryptedTicket = FormsAuthentication.Encrypt( ticket );
var authCookie = new HttpCookie( FormsAuthentication.FormsCookieName, encryptedTicket )
{
Expires = ticket.Expiration
};
_httpContext.Response.Cookies.Add( authCookie );
}
This is in a class called Authentication Service which is called from my LoginController. The authentication service is wired up to the LoginController using Dependency Injection via Windsor by way of MvcContrib. Consider that foreshadowing.
The key to the error is the _httpContext variable. It is set in the constructor of the AuthenticationService as follows:
public AuthenticationService( ) : this( new HttpContextWrapper2( HttpContext.Current ) )
{
}
public AuthenticationService( HttpContextBase httpContext )
{
_httpContext = httpContext;
}
Some of you will recognize this as poor man's dependency injection, used primarily because I didn't feel like wrapping the HttpContextWrapper2 class (which is itself a wrapper) in an interface. Some of you will also note that even though I'm injecting the HttpContext into this class, it is still largely untestable because of the FormsAuthentication stuff. Frankly, I'm good with that which is why I didn't bother with the interface.
During my debugging, one of the things I did was replace _httpContext with HttpContext.Current and lo! That works fine. So clearly, my poor man's dependency injection had issues. My mentor application, CodeCampServer, went the extra step and created an IHttpContextProvider interface around HttpContextWrapper2 and used Windsor to wire it in:
public AuthenticationService(IClock clock, IHttpContextProvider httpContextProvider)
{
_clock = clock;
_httpContextProvider = httpContextProvider;
}
IHttpContextProvider has a pretty simply interface and implementation:
public class HttpContextProvider : IHttpContextProvider
{
public HttpContextBase GetCurrentHttpContext()
{
return new HttpContextWrapper2(HttpContext.Current);
}
}
So while I originally poo-poohed the extra layer of abstraction, CodeCampServer had a key feature that my application did not in that it's login facility worked.
So I introduced an IHttpContextProvider interface and replaced my existing _httpContext with it and lo! I am able to log in successfully. Here is the updated AuthenticationService:
public class AuthenticationService : IAuthenticationService
{
private readonly IHttpContextProvider _httpContext;
public AuthenticationService( IHttpContextProvider httpContext )
{
_httpContext = httpContext;
}
public void SignIn( User user )
{
FormsAuthentication.SignOut( );
var issued = DateTime.Now;
var expires = issued.AddDays( 1 );
var roles = "Administrator";
var ticket = new FormsAuthenticationTicket( 1, user.UserName, issued, expires, false, roles );
var encryptedTicket = FormsAuthentication.Encrypt( ticket );
var authCookie = new HttpCookie( FormsAuthentication.FormsCookieName, encryptedTicket )
{
Expires = ticket.Expiration
};
_httpContext.GetCurrentHttpContext( ).Response.Cookies.Add( authCookie );
}
}
One of the nice things that came out of this was that, thanks to my auto-registration code, I didn't need to do anything special to wire in the new IHttpContextProvider class. Just created it and it just worked. Who knew?
As for the cause of the error, I can only speculate. My guess is that it was using the wrong HttpContext. During my debugging, the constructor for AuthenticationService would get called during the first call to the Login page. That is, when the Login page was first rendered. So the class was storing a reference to *that* page's HTTP context. When the user logged in, it would use that context and try to add the authentication cookie to it. Since that context had already been used and the headers written out, the error occurred. So it wasn't actually getting the *current* HTTP context. It was using the previous request's context.
Further proof of this theory: when I would see that error, my workaround was to go back to Visual Studio and do a full re-compile of the application. This clears out the cached version of the class, and more importantly, it's reference to the previous request's HTTP context. When I'd refresh the page, it would have to create a new instance of AuthenticationService and use the current request's HTTP context, which hasn't yet completed.
In the new version, every call to GetCurrentHttpContext will create a new HttpContextWrapper2 object based on the current request's HTTP context. Ergo, we will always have the current context.
Whatever, as long as it works.
Kyle the Theoretical
Wednesday, May 21, 2008
Here's a true story from the other night that I'm going to masquerade as a lesson in listening to your clients and understanding their perspectives. Mostly, though, I just think it'll provide you with some entertainment at my expense. In this case, the client is my eight-year-old daughter and the application is a homework assignment. The feature under consideration: If you could be any person in history, who would it be and why? (Side note: It's telling that a question from a grade three assignment is also featured prominently on the Miss America Pageant.) Client: I want to be Samantha Superior Consultant: Who? Client: Samantha, the American Girl from the 1800s SC: I think they need you to put down a *real* person. Like Santa Claus or the Tooth Fairy or Chevy Chase. Client: She's a real person! Her parents died when she was young and her grandparents raised her and she spent her summers at her granny and grampa's estate in New England and had lots of adventures! SC: Honey...she's just a doll someone made up to make money. Like Barbie except not as pneumatically proportioned. Wouldn't you rather be Janis Joplin? At this point, the stunned client suddenly ran upstairs and went to her room. I didn't think much of it so I let her work things out in her head for a little while. After twenty minutes or so, I went up and heard sobbing from the other side of the door. Cautiously entering the room, the first thing I noticed is a pad of paper on her desk. At the top of the page, she had written: "American Girls are not real." Except that she crossed out Girls and wrote Dolls in its place. She followed this up with "American Dolls are not real" which she wrote another 37 times down the page a la Jack Torrence. I know it was 37 times because she numbered each and every instance. At this point, the sobs sounded less like "My daddy won't let me do my homework the way I want" and more like "Everything I have ever known is a DIRTY, TREACHEROUS LIE!" I slumped downstairs and quietly informed Mrs. Hillbilly that she had to clean something up while I fed some more money into the Rainy Day Psychiatric Fund. There are a number of lessons to be learned here, the first of which is: never help your children with their homework. Only slightly less important is: Don't mess with your client's belief system. Granted, in my experience, only about 15% of clients will run away crying like little girls if you disagree with something they believe fundamentally to be true. But even the ones that don't will harbour some form of resentment. And any good will you've amassed won't always be bought back easily (although in my case, all that was required was $30 and a batch of fresh-baked cookies). By the way, I later learned that American Girls *are* actually based on real people. Kyle the Parental
Wednesday, May 21, 2008
I was invited to speak at the Florida Tweener weekend which is the weekend 'twixt the two TechEd weeks in Orlando. The schedule is up and I had no idea what the scale of this thing would be! Not only is there a full day .NET code camp but also over a dozen other activities. The ones I'm excited about are the Day of Agile, the Day of Silverlight, the Open Space, and the Mickey Mouse Club.NET session. The last one's not on the schedule yet but I have high hopes. For my part, I'll be presenting on Brownfield applications, mostly because I have the presentation ready (well, actually Donald Belcham has the presentation ready). Luckily, I'm the first session in the morning on Saturday which leaves the rest of the weekend to drink it all in. Oh, and don't be dissuaded by the registration process. "Click to attend" is an appropriate name given how much &*%$ clicking you have to do to sign up. Kyle the Tween
Sunday, May 18, 2008
Coming off a week at DevTeach. Others have commented on how great it is so I'll just use my own observation as a lead-in for the real meat. DevTeach gives me a renewed interest in learning. After seeing presentations on topics I've kinda sorta grokked/solubled, it's nice to gain further insight into them. So I come away with a laundry list of patterns, techniques, tools, and frameworks to look into. And one of those techniques is auto-registration of components in an IoC container. Ayende has talked about this quite a bit with Binsor and I did get to see it in action. But recently, there have been enhancements to Windsor itself to allow for this in a relatively clean(er) way. Hammett has an example here as does Ken Egozi (who took it directly from the Castle dev list). Note that the syntax is slightly different 'twixt the two (AllTypes<T>.Of vs. AllTypesOf<T>). I just got the latest from the trunk and it appears to be the former syntax at the moment. A concrete example. Here is the former code in my ASP.NET MVC application (modified, as usual, from the CodeCampServer sample app): foreach ( var type in Assembly.GetExecutingAssembly( ).GetTypes( ) )
{
if ( typeof (IController).IsAssignableFrom( type ) )
{
_container.AddComponentWithLifestyle( type.Name.ToLower( ), type, LifestyleType.Transient );
}
}
And here is the new version:
_container.Register(
AllTypes.Of( )
.FromAssembly( Assembly.GetExecutingAssembly( ) )
.Configure( c => c.LifeStyle.Transient.Named( c.Implementation.Name.ToLower( ) ) )
);
Note the call to Configure. In most cases, this won't be necessary. But with MvcContrib, it seems to retrieve controllers from the container based on the controller name in lower case. So we need to register them with that same name.
Also note that this is based on a version of Windsor from the trunk (I think). Which is why I didn't update the CodeCampServer project myself (though there are already comments in there to do exactly that).
Here's another comparison. In this case, it's a more "traditional" registration in that we loop through all the classes in an assembly and register it with the container based on its first interface. The before
var presentationTypes = Assembly.Load( "Trilogy.Gunton.Presentation.Services" )
.GetTypes( )
.Where(
t => t.IsClass == true
&& t.IsAbstract == false
&& t.GetInterfaces( ).Length > 0
);
foreach ( var type in presentationTypes )
{
_container.AddComponentLifeStyle( type.Name.ToLower( ), type.GetInterfaces( )[0], type, LifestyleType.Transient );
}
And after:
_container.Register(
AllTypes.Pick( )
.FromAssemblyNamed( "Trilogy.Gunton.Presentation.Services" )
.WithService.FirstInterface( )
);
Side note on the use of var in this code. How many of you looked at it and cared that Where( ) returns an IEnumerable of Type as opposed to an array of Type?
I do have classes in this assembly that are concrete but it appears the FirstInterface call will ignore them. Not so much when I do it more manually.
The last example is more interesting. In this case, I have a bunch of repositories, each of which derive from a RepositoryBase class, which implements IRepository. But the classes also implement their own interfaces.
For example, JobRepository would derive from RepositoryBase<Job> as well as implement IJobRepository. RepositoryBase<T> implements IRepository<T> while IJobRepository implements IRepository<Job>. A lot of the time, the specific interfaces (like IJobRepository, IOfficeRepository, etc.) will be empty but I've been burned before so I usually create them so I can add functionality later if I need it. Plus it makes things clearer when I'm passing them around.
For the purpose of container registration, this presents a slight problem because the repositories now implement two interfaces. For example, IJobRepository implements both IJobRepository and IRepository<Job>. But I want it registered against IJobRepository in the container.
Here's the previous code I had to do this, which I will admit could use some tuning:
var repositoryTypes = Assembly.Load( "Trilogy.Gunton.DataAccess" )
.GetTypes( )
.Where( t => t.IsClass && t.GetInterfaces( ).Length > 0 );
foreach ( var type in repositoryTypes )
{
var types = type.GetInterfaces( ).Where(
t => t.IsGenericType == false
&& t.Namespace.StartsWith( "Trilogy.Gunton" ) );
if ( types.Count( ) > 0 )
{
_container.AddComponentLifeStyle( type.Name.ToLower( ), types.ElementAt( 0 ), type, LifestyleType.Transient );
}
}
The corresponding code isn't quite as terse:
_container.Register(
AllTypes.Pick( )
.FromAssemblyNamed( "Trilogy.Gunton.DataAccess" )
.WithService.Select(
delegate( Type type )
{
var interfaces = type.GetInterfaces( )
.Where(
t => t.IsGenericType == false && t.Namespace.StartsWith( "Trilogy.Gunton" )
);
if ( interfaces.Count( ) > 0 )
{
return interfaces.ElementAt( 0 );
}
return null;
}
)
);
But we can extract the delegate into an extension method that we can use similar to the FirstInterface method:
public static class CastleExtensions
{
public static TypesDescriptor FirstNonGenericTrilogyInterface( this ServiceDescriptor descriptor )
{
return descriptor.Select(
delegate( Type type )
{
var interfaces = type.GetInterfaces( )
.Where(
t => t.IsGenericType == false && t.Namespace.StartsWith( "Trilogy.Gunton" )
);
if ( interfaces.Count() > 0 )
{
return interfaces.ElementAt( 0 );
}
return null;
}
);
}
}
And now our new code to register repositories is:
_container.Register(
AllTypes.Pick( )
.FromAssemblyNamed( "Trilogy.Gunton.DataAccess" )
.WithService.FirstNonGenericTrilogyInterface( )
);
The condition to check that the namespace is mine is because I have a UnitOfWork class in there that implements IDisposable as well as IUnitOfWork. So I want that one in the container only for the IUnitOfWork interface.
Putting it all together, here is the complete code to register my repositories, services, and controllers in the Windsor container:
_container.Register(
AllTypes.Pick( )
.FromAssemblyNamed( "Trilogy.Gunton.DataAccess" )
.WithService.FirstNonGenericTrilogyInterface( )
);
_container.Register(
AllTypes.Pick( )
.FromAssemblyNamed( "Trilogy.Gunton.Presentation.Services" )
.WithService.FirstInterface( )
);
_container.Register(
AllTypes.Of( )
.FromAssembly( Assembly.GetExecutingAssembly( ) )
.Configure( c => c.LifeStyle.Transient.Named( c.Implementation.Name.ToLower( ) ) )
);
Phew, so much for being billable today...
Kyle the Registered
Tuesday, May 13, 2008
I've had "searching" on my mind lately but in a very narrow focus. Specifically, I have a Brownfield application for a client I dig up every so often whenever new features need to be added to it. (Off topic: As soon as I can re-brand the sucker, it'll make a dandy little sample app for some Brownfield-related posts and webcasts. There, I said it on the Internet so the guilt will be that much greater if I don't follow through.) One of the key components to this application is a document repository that you can search both with meta-criteria as well as a full text sea rch of the documents (see screenshot). The meta-criteria part is boring. Bunch of lookup tables in a SQL Server database and I'm building up a SQL query manually in the search process. (I *did* say this was a Brownfield application.) The full text search is done through a kind of poor-man's search index. I'm using a custom catalog with Microsoft's Indexing Service, available on Windows Server 2000 and later. To do this, and I'm going to skim partially because it's not really the focus of the post but mostly because I think it's outdated, I create a catalog that indexes the document folder. Then, I make the catalog accessible to SQL Server so I can combine queries to it with queries to the metadata. Here's the SQL to do so: EXEC sp_addlinkedserver <linkedServerName>, 'Index Server', 'MSIDXS', '<name of catalog>' Now I can write queries like the following: SELECT DocumentID, Title, Date, Filename
FROM doc_list_view dl,
OpenQuery( <linkedServerName>,
'SELECT Filename FROM SCOPE( )
WHERE CONTAINS( Contents, ''<queryTerm>'' ) '
) q
WHERE Date BETWEEN 'January 1, 1850' AND 'December 31, 2008'
AND q.Filename = dl.Filename
ORDER BY Date DESC, Title
Easy as raccoon pie, yesno? The nice thing about this is that it will automatically index all Microsoft Office docs and PDFs are handled by installing Adobe Reader 8.0 (though it's a separate download for 64-bit).
Fast-forward to 2008 and the Indexing Service is no longer turned on by default on either Vista or Windows Server 2008. Posed a problem for the client who runs it locally on his laptop but it was easily remedied simply by turning the service on. For my own newly-minted Server 2008 machine, Indexing Service and the new Windows Search can't run together. And I'm loathe to enable the older technology so it's time to update if I can.
That led me to this page which confuses me to no end. Starting with the terminology: Windows Search, Windows Desktop Service, Instant Search, Windows Indexing Service, Indexed Search, MSN Desktop Search are the terms used to discuss various incarnations of the technology. And that's not including codenames or different versions of the same technology. Adding to my addlement is when Microsoft changes terminology from one page to the next. One example is this page which, in the section on the History of Windows Search, leads you to the download page for Windows Search for Windows Server 2003. That one is titled: Description of Windows Desktop Search 3.01.
So now I'm facing the unenviable, but oddly ironic, task of having to search through the myriad of search options for a solution that will run on Windows Vista, Windows Server 2008, and Windows Server 2003 (where the main site is hosted). At first glance, it seems Windows Desktop Search is the way to go for Server 2003 as it appears the new Windows Search is API-compatible with it.
In any case, this'll make one heckuva a sample for the Brownfield series on managing integration with external systems. Because as you've probably guessed by now, there are no automated (or even manual) tests for this whole process.
Kyle the Researched
Sunday, May 11, 2008
'Tis the day before I head out for DevTeach and have already completed my pre-conference ritual with my wife. It's a fun game we play a few times a year called "Why do you do this?" I was just settling in for some quality time with Bear Grylls when she starts the first round off with the standard "So you're taking a week off work?" opening. We'd just had a pretty fun day at the beach so her heart isn't really in it. So I counter with a half-hearted "Yep. Should be a fun time" to let her know I'll take it easy on her, too. She responds with a biting "Didn't you just start your contract last week?". It's a slight breach of etiquette upping the ante that early but I give her the benefit of the doubt and play along: "Yeah, I cleared it with them before I started. They were good with the time off for this as well as our trip to London next month." She gives an appreciative nod at my subtle volley but then her eyes harden for a few seconds while she plans her next move. She throws out a quick "I thought we agreed to cut back on these things" which stuns me for a moment. If I didn't know her better, I'd chalk it up to an amateurish mistake. But in fact, she's looking to buy some time. I'm not letting her off that easy: "We agreed to no such thing and you know it." Her delay tactic isn't long but it's enough: "Do you actually learn anything at these conferences or is it just an excuse to get drunk and speak whatever language it is you people speak?" It's a baiting ploy because we both know I don't imbibe but I don't rise to it and instead bring out the heavy artillery in an effort to return to normalcy: "I've done the cost/benefit analysis already and believe I will gain more than I will lose (though admittedly, the benefits are more long-term and intangible). I'll remind you of our agreement that all aspects of my career remain solely to my discretion as long as we remain on this island. We have already received direct benefit of my community involvement in the form of the upcoming book and my last contract being a direct result of my blogging." There is more admiration than resignation in her face and the battle is all but won. But she can't resist a parting shot. With a twinkle in her eye, she snuggles up to me just in time to see Bear chow down on a live frog and says: "Oh well, I guess I'll just stay home and watch Brokeback Mountain while you go off 'fishing' with your buddies." Kyle the Victor (I think)
Thursday, May 08, 2008
The hillbilly recently upgraded his trailer to Windows Server 2008 but it wasn't interesting enough to talk about, mostly because it went so seamlessly. So much so that I'm hauling out a post from my old blog rather instead of relating my experience. But I will say that so far, I'm very happy with it, thanks in part to these instructions. The reason I'm regurgitating is because I've been looking at other people's code recently, including some of my own from way back (trust me, that's an "other" person too). And as I scan through it, I was reminded of something: I'm a big fan of white space. In coding standards, there is usually much said about things like putting curly braces on a single line and wrapping single line if statements in braces to enhance readability. But for some years now, I've adopted a personal standard that goes a little further. It was after discussions with Simon Watfa way back when I was looking over some code of his and it stood out more than usual. I'll start with two examples. The first is a more "traditional" white space scheme: private void returnSomeValue(int parm1, string parm2)
{
// if, for and foreach
if(i == 1)
{
// Do something
}
for(int j = 0; j<100; j++)
{
// Loop
}
if(i == 100) return;
foreach(var item in items)
{
// Process item
}
// Function calls
string theValue = i.ToString();
theValue = String.Format("The value is {0}", i);
// Casting
TextBox textbox = (TextBox)Page.FindControl("textBox1");
string text = ((TextBox)Page.FindControl("textBox1")).Text;
// Square braces
string[] myArray = theValue.Split(',');
myArray[0] = "moo";
// Lambdas
User user = userList.Find(u=>u.Username=="moo");
}
And here's the same code with my preferences:
private void returnSomeValue( int parm1, string parm2 )
{
// if, for and foreach
if ( i == 1 )
{
// Do something
}
for ( int j = 0; j < 100; j++ )
{
// Loop
}
if ( i == 100 ) return;
foreach ( var item in items )
{
// Process item
}
// Function calls
string theValue = i.ToString( );
theValue = String.Format( "The value is {0}", i );
// Casting
TextBox textbox = (TextBox)Page.FindControl( "textBox1" );
string text = ( (TextBox)Page.FindControl( "textBox1" ) ).Text;
// Square braces
string[] myArray = theValue.Split( ',' );
myArray[0] = "moo";
// Lambdas
User user = userList.Find( u => u.Username == "moo" );
}
That is, there are spaces almost everywhere. The only places I don't use them are within array indexes and when casting.
Fairly minor differences, to be sure but they really help me follow code, especially when it gets particularly dense (in both senses of the word). Coupled with a decent IDE colour scheme (and the hillbilly still *strongly* favours dark text on light backgrounds), I can more easily pick out the meaty parts of the code while filtering out the noise. The nice thing is that this can all be enforced with ReSharper's formatting options and, I think, even with Visual Studio itself, sans add-ins.
Couple o' downsides, though. I often work on code on my laptop where I don't set the standards. This requires setting up project specific styles in ReSharper depending on how much work I'm going to be doing in it. Bonus points if the project already includes a ReSharper style. But in general, the lesson is: if you adopt a similar coding style to this, *never* contribute to an OSS project.
ReSharper sometimes actually gets in the way here, too. With these styles in place, I would type the opening parenthesis of a method call and ReSharper would helpfully add the extra space, then the closing parenthesis for me. When there is no space, you can type the closing parenthesis yourself and ReSharper is smart enough to recognize you've just typed what it's already added. That is, it won't re-add the closing parenthesis. But with the space, you end up with two closing parentheses. It's not as much an issue if you get used to using live templates and auto-complete more often but still an annoyance at first.
As I mentioned in the original post, I can make up semi-lucid reasons why others should use spaces in this way but the reality is: I just like how it looks. It says to me that the author cares about how the code is presented. Or that the author has eyesight problems.
Kyle the Aging
Friday, May 02, 2008
NOTE: This post is about a day and a half late. I could claim that I wanted the event to sink in first but the reality is I was trying to get a handle back on things after a very hectic week. Part of it was a short but very successful trip to NYC. So dense was the wife's and daughter's shopping-to-waking-hours ratio that I'll be looking for sponsors for our next trip (anyone from Visa and/or American Girl in my readership?). Closest I got to purchasing anything of value was walking by a Best Buy after closing and laughing at a fellow who tried to sell me a router for $120, which was over half off the ticket price. But that's not what I came to babble about today. Encouraged and invigorated. That's what I felt right after the first BahaNET meeting and what I still feel now two days later. Attendance, including myself and Dave Noderer, was a whopping seven people. Experience ranged from an IT Pro looking to get into programming, to a game programmer (watch for Rudolph the Red-Nosed Reindeer for the Wii this Xmas), to a young lady acting as the sole .NET programmer at a local bank. It was a very interactive meeting with everyone showing genuine interest in solving not only the problems I lamented last time but a few others. Everyone was active in the discussion, especially at the end. And I got the sense that from these seven people, we'll have a good base from which to grow. The main problem I see is simply getting people to see the value of such a group. As such, this was something we discussed for some time. We veered into some unfamiliar territory for me as we talked about some practical ideas as well as some more...ummm...let's say "long term" plans (like a robotics competition). There was much talk on how to change attitudes in the country. This, by far, is why I foresee any real change taking several years. In my last post, I talked about people who have no interest in "talking computers" after working on them all day. But as was pointed out at the meeting, that shouldn't be the focus initially. So I'll be tweaking things more toward the social aspects of the gatherings. But even with the small group, one of the major benefits of regular meetings like this came up several times. Namely, people learned things that they may not have known before. There was tremendous interest in DotNetNuke as a possible platform for creating websites (which is a common business model in the Bahamas), which some of the attendees had never heard of. This was not the only example of side conversation enlightenment by any stretch either. All in all, I believe those that attended will be back and will bring friends. And I got what I wanted out of it: a sense of how to proceed for the benefit of everyone. The final pleasant surprise came when I handed out prizes. People were offered a choice of items and not a single XBox game was claimed. Instead, people took Visual Studio licenses, a book on SQL Server programming, and a copy of Communication Server. We even discussed the next meeting: May 30 at the same location (IPBS House). Topic will be: Creating a Website in ASP.NET. See you there! Kyle the Re-animated
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
|
|
LATEST POSTS
POPULAR POSTS
|
|
ARCHIVE
| March, 2010 (1) |
| February, 2010 (8) |
| January, 2010 (2) |
| December, 2009 (3) |
| November, 2009 (2) |
| September, 2009 (5) |
| August, 2009 (4) |
| July, 2009 (2) |
| June, 2009 (5) |
| May, 2009 (6) |
| April, 2009 (5) |
| March, 2009 (6) |
| February, 2009 (2) |
| January, 2009 (6) |
| December, 2008 (5) |
| November, 2008 (2) |
| October, 2008 (5) |
| September, 2008 (9) |
| August, 2008 (5) |
| July, 2008 (7) |
| June, 2008 (6) |
| May, 2008 (11) |
| April, 2008 (13) |
| March, 2008 (13) |
| February, 2008 (12) |
| January, 2008 (19) |
| December, 2007 (16) |
| November, 2007 (8) |
| October, 2007 (23) |
| September, 2007 (15) |
| August, 2007 (8) |
| July, 2007 (6) |
| June, 2007 (11) |
| May, 2007 (19) |
| April, 2007 (14) |
| March, 2007 (3) |
| February, 2007 (4) |
| January, 2007 (7) |
| December, 2006 (5) |
| November, 2006 (9) |
| October, 2006 (11) |
| September, 2006 (14) |
| August, 2006 (11) |
| July, 2006 (15) |
| June, 2006 (8) |
| May, 2006 (10) |
| April, 2006 (12) |
| March, 2006 (3) |
| February, 2006 (7) |
|
|
|