CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Kyle Baley - The Coding Hillbilly

"We are stuck with technology when what we really want is just stuff that works" -- Douglas Adams

December 2007 - Posts

  • Database refactoring, or "How to remove a schema"

    Welcome back, revolutionaries! We've had a small reprieve from the fight to let our early anarchy settle in and---....y'know what. The revolutionary jargon is getting too hard to write and the refactorings more involved so I'll just throw out a "The revolution will be blogged!" and go back to bein' a hillbilly, iffen it's all the same to you.

    Today I'm throwing myself at the data access layer and the database. The data access layer is an easy target just because it exists and we've long since switched to NHibernate for our data access in other applications. So the refactoring there will entail ripping it out mostly. Along with most of the stored procedures which haven't inspired me with a lot of confidence ever since I found three of them that refer to tables that don't exist.

    One of the pain points I've encountered in dealing with this particular database is the different schemas it uses. For those of you not familiar with the concept (as I wasn't until about a week ago), most databases will use the default schema, which is usually dbo, probably for database owner but don't quote a humble hillbilly. In this case, almost all the tables were created under a different schema. For example, the users table belongs to a Admin schema and the FieldList table belongs to a Dictionary schema.

    To create the table under a different schema:

    CREATE SCHEMA [Admin]
    CREATE TABLE [Admin].[tbUsers]
    (
    [UserID] [int] NOT NULL IDENTITY(1, 1),
    [NetworkUserName] [varchar] (255) NULL,
    [RoleID] [int] NOT NULL,
    [PreferredPlaidColour] [varchar] (50) NULL
    )

    After this, you'll need to qualify the table in any queries you make to it:

    SELECT * FROM [Admin].[tbUsers]

    I'm certain there are advantages to using schemas. Security comes to mind as I'm betting you can apply security rules to a schema to grant/limit access to it. And they probably appeal to DBAs that like to keep things pseudo-organized.

    For our case, it was overkill and a surprisingly potent distraction when we had to query the database. This was the only application that used them and it wasn't exactly a high-profile, high-security type application. It's used by one app, ours, and you either have access to the app or you don't. And they weren't even used consistently. There were two tables using the default dbo schema, at least one of which shouldn't have been.

    So after talking with some developers who worked on the previous version and confirming that they weren't being used in a meaningful way, we set about removing them. Which is much easier than one might think:

    ALTER SCHEMA dbo
    TRANSFER Admin.tbUsers

    I do have to send a shout-out to DataDude, which you may know by it's official name (and I'm going by memory so it may be off a bit): Microsoft Visual Studio Team

    Did you know?

    You can't compare tables in DataDude if one or both tables in the comparison has no primary key

    Partner Coding System for Professional Licensed Database Practioners and Ornothologists, the Spongebob Squarepants Edition.

    This nifty little VS add-in/utility/whole entire version of the product has been a tremendous help during this refactoring. It has a Schema Compare feature that I used to generate the initial scripts to create the database and a Data Compare feature I used to generate test data for our QA environment.

    But a nice feature I wasn't expecting: after I changed our CREATE TABLE scripts to use the new default schema, Visual Studio immediately popped up a pile of clickable warnings indicating stored procedures, foreign key scripts, etc, that were no longer valid because the name of the table had changed. Not quite as clean as a ReSharper Rename, mind you, but better than hunting through scripts manually and inevitably missing some.

    It was also DataDude that discovered the three stored procedures referencing non-existent tables.

    The net result: a database more in tune with its sister databases and one that inspires far less cursing from developers who are prone to typing in SQL statements without the schema.

    Kyle the Databound

  • MbUnit fixtures, or "How to make your tests pass through attribute attrition"

    using MbUnit.Framework;
    using Rhino.Mocks;
    namespace Suvius.Applications.LoveDoctor.Tests
    {
        public class CritterFixationFixture
        {
            private ISpeciesSelectorPresenterView view;
            private MockRepository mocks;
            private SpeciesSelectorPresenter presenter;
    
            [ SetUp ]
            public void SetUp( )
            {
                mocks = new MockRepository( );
                view = mocks.DynamicMock<ISpeciesSelectorPresenterView>( );
                presenter = new SpeciesSelectorPresenter( view );
            }
    
            [ Test ]
            public void Should_initialize_view_with_list_of_projects( )
            {
                using ( mocks.Record( ) )
                {
                    view.LoadCritters( );
                }
                using ( mocks.Playback( ) )
                {
                    presenter.InitializeView( );
                }
            }
        }
    
        public class SpeciesSelectorPresenter
        {
            private readonly ISpeciesSelectorPresenterView _view;
    
            public SpeciesSelectorPresenter( ISpeciesSelectorPresenterView view )
            {
                _view = view;
            }
    
            public void InitializeView()
            {
            }
        }
    
        public interface ISpeciesSelectorPresenterView
        {
            void LoadCritters( );
        }
    }

    How's THAT for throwing you into the fray? I can see all of you now going, "whoa, there, Coding Hillbilly! I ain't e'en got me a cuppa joe yet, buddy." Sorry, folks, that's how I roll. I think slow and act fast. You gotta keep up.

    The test above sets up an expectation that LoadCritters will be called on the View when we called Presenter.InitializeView. In actual fact, this isn't being done. So here's the poser: why does this test pass?

    All right, I'll tell ya because I like your look. And I don't want to risk any of you actually cutting and pasting that into an IDE and coming up with some cut 'n paste error I made. Plus I gave you a hint in the post title.

    The test isn't actually passing. When it hits view.LoadCritters, it fails silently and kicks out of the test completely. The reason bein' SetUp isn't being called. The reason for *that* bein' I forgot the [ TestFixture ] attribute on the class itself.

    Now as to the question on why the test doesn't fail with a null reference exception: That's an easy answer. I have no idea. If you add the [ TestFixture ] attribute and comment out the code that creates the view, it does fail with the expected exception.

    So I'll leave that to my loyal and, I pray, generous readers.

    Kyle the Deferred

    Posted Dec 20 2007, 04:36 PM by Kyle Baley with 4 comment(s)
    Filed under: ,
  • Removing #regions, or "How to keep your code expanded"

    In a couple of recent posts, I've all but declared war on #regions. I'm not going to regurgitate my reasons but for those of you who go into "you won't like me when I'm angry" mode when they see a 800-line class condensed down to five, I submit to you not one, but TWO methods for getting rid of them.

    Method 1: The VBA way

    In this method, I used my newfound knowledge of using regular expressions in Visual Studio's Find and Replace and recorded a macro. Did some clean-up of the code and voila:

        Sub ClearRegions()
            DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
            DTE.Find.FindWhat = "^:b*\#region.*\n"
            DTE.Find.ReplaceWith = ""
            DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
            DTE.Find.MatchCase = False
            DTE.Find.MatchWholeWord = False
            DTE.Find.MatchInHiddenText = True
            DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr
            DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
            DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
            DTE.Find.Execute()
    
            DTE.Find.FindWhat = "^:b*\#endregion.*\n"
            DTE.Find.ReplaceWith = ""
            DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
            DTE.Find.MatchCase = False
            DTE.Find.MatchWholeWord = False
            DTE.Find.MatchInHiddenText = True
            DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr
            DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
            DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
            DTE.Find.Execute()
        End Sub
    

    To bind a key combination to a macro

    • Click Tools | Options
    • Select Keyboard from the Environment tree node
    • In the search box, type ClearRegions to find the macro
    • Select it from the list
    • Click in the "Press shortcut keys" textbox
    • You can probably take it from here

    So you throw that into a Macro Module, bind it to Ctrl+Shift+3, for example, and henceforth, you can use that shortcut to be free of #regions forever.

    If I wanted to be *really* anal about it, I'd extract the duplicate code into a separate method. That way, I could re-use it for a macro to, say, remove triple-slash comments (regular expression: "^:b*///.*\n" ).

    Method 2: The ReSharper Way

    This method comes courtesy of Wade Grandoni, a non-blogger-who-should from whom I plan to steal blog post ideas until he takes the hint. When we're done here, removing regions becomes part of the ReSharper reformatting process. I.E. Ctrl+Alt+F (or Ctrl+Alt+Shift+F) and your regions are gone. If it isn't obvious yet, this method requires ReSharper

    To achieve this little bit of magic:

    • Go to ReSharper | Options
    • Under Language Options, expand C#, then Formatting Style
    • Select Type Members Layout
    • Uncheck "Use Default Patterns" and DO NOT BE ALARMED AT ALL THE TEXT THAT APPEARS

    What you see at this point is, I think, some default patterns that get applied to your classes when you reformat. And the default is to ignore regions for the most part. To change this find default pattern about halfway down. It's the one that looks like this:

        <!--Default pattern-->
        <Pattern>

    and change it to this:

        <!--Default pattern-->
        <Pattern RemoveAllRegions="true">

    Henceforth, whenever you reformat your code, regions will be removed. N.B.: This works only if you have selected the option to "Reorder type members" in the Reformat dialog:

    image

    If you select this option (as well as any others) the first time, it will be remembered every time you Ctrl+Alt+F or Ctrl+Alt+Shift+F from then on.

    If you're a little confused as to what the rest of that XML is doing in the default patterns, you could delete the entire thing and replace it with the following:

    <?xml version="1.0" encoding="utf-8" ?>
    <Patterns xmlns="urn:shemas-jetbrains-com:member-reordering-patterns">
      <Pattern RemoveAllRegions="true">
         <Entry />
      </Pattern>
    </Patterns>

    This is the bare minimum you need to remove regions from your code when you reformat. What the rest of that XML does requires more reading than I'm willing to put in at 6:30 in the morning.

    This method has the advantage of less friction while you're coding in that you don't need to perform any extra steps to remove regions (assuming you reformat before you check-in, which, of course, you do). On the other hand, Method 1 can be adapted to remove other patterns (like triple-slash comments) and, of course, can be used for the ReSharper-less in the crowd. And neither method will allow you to keep any regions that are, in fact, useful.

    Kyle the Regional

  • ClassTester, or "How to exercise your assembly"

    Fear not, brethren and...uhhh....sistren. The resistance is still alive. But the revolutionary jargon will get in the way here so I'm reverting to a more traditional means of expression.

    Among the things we've implemented as part of our project clean-up is the aptly-named ClassTester. It's a neat little tool that takes some of the pain out of routine testing of constructors and properties by, for example, setting a property then checking to make sure you get the same value back from the getter. Here's some sample syntax for testing the properties of an object:

        ClassTester tester = new ClassTester(new myObject());
    tester.TestProperties();

    Now, hillbillies are not what you might call amenable to writing boilerplate code for every single class in their domain. Leads to some problems you can only imagine in your nightmares. So in an attempt to avoid duplication, I decided instead to walk through all the classes in the assembly:

           [Test]
    1       public void Test_general_constructors_and_properties()
    2       {
    3           Assembly assembly = Assembly.GetAssembly(typeof (Still));
    4           Type[] types = assembly.GetTypes();
    5           foreach (Type type in types)
    6           {
    7               if ( type.IsClass 
    8                    && !type.IsAbstract 
    9                    && type.Namespace == "Suvius.Applications.HoochDeriver.Domain" ))
    10              {
    11                  ClassTester.TestConstructors(type, false);
    12                  object item = assembly.CreateInstance(type.FullName);
    13                  ClassTester tester = new ClassTester(item);
    14
    15                  tester.TestProperties();
    16              }
    17          }
    18      }

    Some points of interest:

    • I could use any domain object in line 3. I'm interested only in getting the assembly that houses all the domain objects
    • Line 8: The ClassTester doesn't much like abstract classes
    • Line 9: This line may seem redundant but it is necessary because of the way I run my builds (which, credit where it's due, I pilfered from JP Boodhoo). Namely, in the build file, we compile all .cs files into a single assembly and run the tests against it. Which means line 3, will retrieve the assembly containing (almost) every class in the application. Compare this with compiling in the IDE where all domain classes are in one assembly by themselves. So running this test from the IDE, where it will compile the assemblies separately, we don't need the check for the namespace. Running it in the build file however, we do.

    There are in fact two separate tests here, one for constructors and one for properties. If you want to argue that these should be separate tests, I'll agree. If you want to argue against that, I'll agree too. That's how much I care about it.

    Anyway, this code is nice in theory. At present, the tester handles only very routine cases. If there are any validation rules, you'll get validation errors. For example, if you have a Percent property where you throw an exception if the value is not 'twixt 0 and 100, the ClassTester will throw this exception most of the time because it generates a random number to set the properties and doesn't know about your little rules. The ClassTester does allow you to ignore individual properties though, which is useful when testing classes individually.

    The second problem with looping through every class is that not every class can be tested, even with no validation rules. In our case, we had some classes that were inheriting from CollectionBase (did I mention it was ported over from .NET 1.1?) and the ClassTester failed on all of these because of the Capacity property (which I didn't know existed).

    The end result is that we had to include a list of classes to ignore from the domain and make sure they were tested separately through more conventional methods. Whether or not this is worth it depends on how many classes you need to ignore. In our case, it was only the collection classes which have since been removed anyway because of the generic collections in .NET 2.0.

    And to prove it was all worthwhile, the ClassTester found and located no less than three bugs in our getters and setters, each one a variation on this theme:

        public decimal AmountOfHopsToAdd
        {
            get
            {
                if (DandelionBase != null)
                {
                    // Return some complicated formulaic value based on DandelionBase
                }
                else return 0.0M;
            }
            set { _amountOfHopsToAdd = value; }
        }

    In this specific case, the setter is all but useless since _amountOfHopsToAdd is never actually used anywhere. The other examples gave similar fodder for pondering and our code is shinier and happier for it.

    Kyle the Testable

  • Test SetUp Methods, or "How to make yourself public"

    Here's a little sidebar that I bet you didn't know. Actually, that was probably more accurate at my old site where my readership measured on a more, shall we say, intimate, scale.

    Anyway, I recently converted a project from NUnit to MbUnit just because I felt like it. (Oh, I'm kidding. I have reasons. Boring but valid ones. So don't sic the alt.net police on me.) In the past, that has involved adding the assembly reference for MbUnit, then performing a global search and replace of the appropriate using statement. Perhaps cleaning up the different Assert syntax but in general, it's a pretty painless process.

    This time, I got null reference errors in all tests in several class and to make a long story short (don't get used to it, I don't do it often), I traced it to the SetUp method:

        [ SetUp ]
        protected void SetUp( )
        {
            // Do stuff
        }

    Notice that it is protected, which apparently makes it all but invisible to MbUnit. I switched it to public and my world is fully tested again.

    Initially, I thought it might be worthy of a bug report. But here is kind of what I typed out:

    Hey dudes! You got yerself a fine 'n dandy little product here. But I got meself some tests here someone was nice enough to write out for me in NUnit and when I converted them to MbUnit, they done gone failed on me 'cause the SetUp method is protected even though... the tests themselves are...ummm... y'know...public...

    Here's where I started to feel a little silly and considered that maybe the MbUnit folks knew what they're doing when they wrote the product (although I wouldn't mind it handling nullable types a little differently).

    So I guess I don't really make this long story all that shorter. The Hillbilly is nothing if not contradictory.

    Kyle the Taciturn

  • Refactoring by rote, or "How to have a ReSharper Orgy!"

    Greetings brothers and sisters and solidarity to you. The revolution is off to a rousing start. We have integrated continuously and Comrade Marc Davis has won a key early victory by excising the putrid web service that so fiendishly guarded our data access layer.

    As for myself, I have just returned from a bloody battle with the enemy and have come away battered but triumphant. It was a great blow to the despots of uncohesiveness!

    Our wily foes tried to gain the upper hand early by striking when our defenses were low. After opening a seemingly benign file, we were awash, AWASH I tell you!, in a veritable SEA OF ORANGE running along the right side.

    I stared dumbfounded at the awesomeness of the task before me. "How can this be?" asks I, "this is no mere autogenerated class? This class was typed by hand. Clearly by the devil himself!" No matter, with a cry of "ONWARD!!! TO BE RELEASED FROM UNDER THE OPPRESSIVE THUMBS OF THE HIGHLY-COUPLED SWINE-PIGS!!" I leap headlong into the fray.

    My battle begins and I become the Comment Warrior. It is a bold move because of the sheer numbers. <summary> tags that hold no relevance, <param> tags for non-existent parameters. No comment survives. Void methods have been tagged with <returns>Nothing</returns>, a move which stuns me at first but I quickly recover. After several skirmishes, I no longer notice the empty lines of History: in the <remarks> tags as I go about decapitating the methods in the name of maintainability.

    Soon, I see an opening! Find & Replace can take a regular expression! After a brief two-day refresher of the syntax, the first phase is mine in one fell swoop of replacing the following expression with an empty string:

    ^:b*///.*\n

    With that, the opposing army is almost 1000 less strong. But the enemy is quick and responds with waves upon waves upon waves of:

        catch
        {
            throw;
        }

    along with their slightly bigger pawns*:

        catch ( Exception ex )
        {
            throw ex;
        }

    These blocks are thrown at me from *every single method*. Some even have finally calls that set object references to null. Clearly, this army's overlords were trained in the style of Crouching Tiger/Visual Basic from the late 20th century.

    Luckily, they are easily picked up with the Blade of Alt-Enter from my ReSharper arsenal. But after untold hours, my fingers become weary. And my Mace of Shift-Ctrl-Alt-F fails me when it is unable to reformat the 6000+ soldiers remaining in this class. But I risk a glance to the right and allow myself a brief bout of euphoria as the orange gives way to the dull gray of victory.

    By this time, the battle is all but won. I drive a final blow by deleting all regions (regular expressions: ^:b*\#region.*\n and ^:b*\#endregion.*\n) and with a flourish of Ctrl+Alt+O, all references are properly qualified for the final victory!

    Resistance is NOT futile!

    Kyle the Understated

    * Comrades Dave Woods and James Kovacs both have posts on why this variation is particularly heinous

  • Guerrilla refactoring, or "How to bring down a totalitarian regime"

    Have started a new project at the current contract and I'm muy excited about it. It's a "legacy" .NET 1.1 app that was upgraded to 2.0 by running the wizard, then left alone. It has a web service created by an architect who appears to have worked with Jeremy Miller before. It returns datasets. Lots of them. There's a DataDude project created in some CTP version that can't be upgraded. There is no CI process.

    I discovered most of this without cracking open the code. I have since done so and analogously speaking, it is clearly the result of the type of relationship we hillbillies are *very* familiar with.

    I'm pretty pumped to dive into it, actually. The CI process is mostly done. The tests are running regularly (2.5% code coverage, woohoo!). Now it's time to start the grand-scale refactoring.

    First, however, we need to go to the customer and tell him, "The architecture of this app is not unlike something akin to roadkill. It compiles only during the waxing phase of the moon and the fact that it runs at all has restored my faith in God. I know you've been using it relatively successfully for several months but we'll need several tens of thousands of dollars from you to refactor large portions of it. When we're done, there will be no discernible difference to the application from your perspective but we in the IT department will sure feel better about ourselves."

    Now I've never been able to say "discernible" out loud convincingly and I'm sure as heck not going to imply that I believe in a higher power so obviously, this isn't going to work for me. Which leads me to a tactic I've become quite fond of: Guerrilla Refactoring

    Guerrilla refactoring requires some unconventional tactics. Like in guerrilla warfare, I'll need to ambush the code often. Come at it from an angle it's not expecting. Do quick, focused skirmishes and get out again before anyone notices (except the code which will be grateful to be liberated from its bourgeois oppressors).

    Subterfuge and guile will be my allies. "How long will it take to implement the 'sync' function? Four hours, minimum. Change the layout of the form? A good day of coding. You need the text changed on this label? I'd say....three full days. Not including writing tests to expose the bug."

    I will bear the brunt of their skeptical, leering eyes for the sake of the common good. No longer will we toil under the totalitarian regime of logic-laden views and hand-rolled data access code. Their armies will *tremble* in fear in the face of my legion of Freedom Fighter Presenters! And the fascists will COWER as I slash at them with my Sword of NHibernate!! JOIN ME, brothers (and to a lesser extent, sisters), as we stamp out the Dictatorship of Duplication and drive a *hammer* through the very heart of hardcodedness!!1!!1!!!!

    VIVA LA REVOLUCION!!!!

    Kyle the Pacifist

  • The CodeBetter Hillbilly, or "How to build a better banjo"

    The Coding Hillbilly has been called up to the Show! Seems the members of CodeBetter are serious about the "alternative" part of ALT.NET and thus, I have myself a new home. And hoo-ee, wouldja lookit the size o' this place! A little less rustic than I'm used to but I believe me 'n my banjo, Flo'll do just fine, thanky-kindly.

    If I may break character for a moment, I am officially Kyle Baley, a software developer going by the name Coding Hillbilly for reasons I swear are less interesting than you probably imagine (hint: I'm not really a hillbilly). I am some ten years into my career after abandoning a very brief initial stint as an actuary because frankly, those guys are just way too geeky. Although I'm interested in, and have dabbled with many things of late, my current love affairs in the industry include TDD and DDD and the surrounding tools and techniques. But I've been known to enjoy some cheap one-night-stands with the likes of Livelink or RPG on occasion. Born and raised in western Canada, I am currently living in the Bahamas where I suspect the next CodeBetter annual meeting will be held.

    Speaking of my new roomies, many thanks to them for inviting me to infiltrate. I will do my best to do them proud but with my assumed nom de guerre, I make no guarantees. Those of you getting your first hit of the Hillbilly can find my blogging life to date over at http://kyle.baley.org. I'll be seeding this with a couple of posts from the tail end of my run there for the sake of continuity. Potential stalkers can also get more personal insight at http://kyle.baley.org/personal and http://www.baley.org which will remain active, albeit sparsely.

    So again, thanks to the 'Better guys for their leap of faith. Time to go back to posting on squirrel huntin' and family trees/graphs.

    Kyle the Transported

More Posts