Archive

Archive for the ‘Miscellaneous’ Category

[REPOST] – I am a Professional Geek

June 6, 2008 Comments off

I was just reading Payton Byrd’s recent post about the two types of programmers, which references another post about the same topic by Jeff Atwood, and I felt encouraged to respond.

Growing Up ABM

I honestly couldn’t care less about the open-source vs. Microsoft wars. My background is probably atypical, but I as a young programmer I was raised on IBM technologies. As a result, I spent many years as an ABM-er (Anyone But Microsoft). While thinly-veiled by necessity, IBM has embraced this philosophy since the OS/2 debacle. So much so, in fact, that they have donated billions of dollars to Linux, Java, Rational, and Eclipse. To this day there are a great many IBM oriented developers who will do anything to avoid Microsoft solutions, no matter how painful or inconvenient. I know because until 2003 I was one of them.

Being anti-Microsoft meant that I had the opportunity to learn a great many things that I otherwise may not have been motivated to learn: PHP, Linux, Apache, Java, OpenOffice, etc. all fall into that category. I even used a Linux Desktop for almost 2 years just to be as anti-Microsoft as I could. It was during this period that a friend of mine, a TRUE Linux geek from v1, introduced me to VB6. I watched as he created a GUI prototype in minutes that would have taken me days in Swing. When .NET came out, I started seriously paying attention, and by the time VS2003 came out I was hooked.

Technology Agnostic

Today, while I spend most of my time in .NET, it is not unusual for me to spend time in SEU coding RPG on the iSeries or VI coding PHP on a Linux Server. My projects span several different databases with DB2, MySQL, and SqlServer being the most common. I write and maintain Green Screen programs, Windows applications, and Web sites.

The point of all this is to show that I have gone from an ABM Religious Zealot to a true Technology Agnostic. I think that all these people who have “taken sides” got it all wrong. My zealotry is now limited to using the right tool for the right job. I will admit, that these days this usually means Microsoft, but at the same time it could be open source. It could be Linux, it could be IBM, it could be just about anything.
In other words, I just don’t care: I want to get the job done, effectively and with a pleasing result (even if only to me.)

So what am I? I am a Professional Geek

So what struck me about these articles and this general discussion was the question: what am I? In reading about the 20% Alpha Geeks and the 80% Vocational Programmers, I realized that I am really neither. Payton essentially draws the same conclusion, but does so from a much different personal (professional?) viewpoint than I do.

I will definitely say that I am NOT a 20% Alpha Geek. I don’t contribute to Open Source projects or spend my evenings bit twiddling. In fact, I don’t even own a computer. I have often said to people that “I am a professional Geek: I only Geek at work.” After spending most of my working time staring at 2 computer monitors, sitting on my butt in my desk chair, with my fingers glued to the keyboard, the last thing I want to with my free time is spend it on a computer.

But to lump me in with the 80%-ers (by their definition, not mine) would be unfair. I stay ahead of my peers. I read about programming and development incessantly (and occasionally I will read at home). I spend a lot of time learning new technologies and new techniques. I go to trade shows. I participate in User Groups. I write, I teach, and I give presentations. I even give software away. In other words, I am not just “vocational”: I learn, grow, and contribute to our community at large. And I am content with my position, meaning that I recognize I will never be on the level of many of the other developers out there whom I admire, but I still have something to offer so I choose to do so.

50%-ers of the world unite… or don’t…

And so I find myself in neither category, so I’ll just split the middle and say I am a “50%-er”, and I’ll bet most of you are as well. Chime in and rate yourself below – or don’t… it does not matter to me.

Categories: Miscellaneous

Weekly update

March 28, 2008 2 comments

I’m going to start off by saying that this is a totally lame post, but I resolved to post at least once a week, so here it is.

Site Change

Scott Hanselman had an interesting post this week that got me thinking about how I judge the success of this blog. Like he mentions, I check in on it several times a day. I like to see how traffic is faring, which pages and topics are getting the most interest, and as I’ve posted before I’m fascinated by the Search Terms that drive people here.

What interested me about Scott’s post is that he judges success by the number of comments and participation, not the traffic. I have to admit that I think he is right. I have always wanted to see more comments, questions, and discussion here, but even when I have asked for it there has been very little in the way of community participation. In a perfect world, this would be because I’m such a brilliant writer that people have no questions or arguments with my musings. Since we do not live in such a world, however, it is probably because my topics are too basic, mundane, obvious, or even down right boring.

But it could also be because people don’t want to register on yet another site. On other blogs I managed in the past, comment spam was a downright kill-joy, so I wanted to make sure that the comments people made were serious and real. To that end, I configured the site to require registration and log in before being allowed to post a comment, which, as of this writing, has been the only benefit of registration. I plan on changing this in the future, but for now it is a pretty weak reason to register, so I can easily believe (perhaps deluding myself in the process) that this may be one reason why comments are few and far between.

So, in Grand Announcement Fashion, I would like to say that registration is no longer required to post a comment. I have an appropriate Comment Spam plug-in enabled, so we’ll see just how bad it gets. If it becomes a problem, I can always go back to the original configuration. But for now, I’d like to ask for your help in testing the new configuration: just leave a comment, innocuous or otherwise, to this post.

ASP.NET MVC

I have spent the bulk of this week coding away at my new ASP.NET MVC web site, and I am still high as a kite over the project. I’m planning a couple of articles about some specific topics, but overall I feel as though I have finally found a way to integrate my traditional HTML control-freak issues with my C# and .Net skills. I spent a few days developing oodles of backend code, but the last couple of days have been a flurry of web page activity, and I can actually begin to see and feel the results. Next week will mostly be spent “prettyfying” the site and distributing it to our webserver.

Categories: Miscellaneous

The Birth of the Cubicle

March 21, 2008 Comments off

According to Timm over at DevTopics, this year is the 40th anniversary of the much maligned cubicle. Since that time, cubicles have been reviled, scorned, berated, ridiculed, and generally hated. According to the article though, 70% of American office workers spend their work time in a cubicle. Other than spreading gossip, surfing the Internet, and drinking coffee, ANYTHING that so many Americans do at work every day is going to be hated.

I spent the first three years of my professional life in a Cube Farm. At the height of my employment, there were almost 800 of us crammed into the 5×5 boxes (or were they 4×4?) This particular company was a little different in that the cubes were not arranged in a grid, but rather at an angle. This meant that most of the cubes had open backs where you could easily roll out into the walk way. This had the benefit of feeling more open and promoting communication. On the downside, it was more open and promoted communication, most of which was inane and unnecessary.

Blissfully, I escaped such confines in 2000, and I’m typing this from my private office with the door closed. I am also blessed that we have a receptionist who actually screens my calls. Unfortunately the walls are paper thin, and even with the door closed I can hear the conversations of the two offices on either side of me, not to mention all the office chatter and ambient noise from the kitchen and the upstairs. *sigh*

The Zone

Timm also talks about getting in “the zone”, something to which most programmers can easily relate. All these minor complaints aside, when I get into the zone none of these distractions exist. I had two experiences just this week that perfectly illustrate this. I was testing some of the new ASP.NET MVC project I’ve been feverishly working on, and after a particularly rewarding success I did a little fist pump celebration (come on, you all know you’ve done it!) Immediately following, I literally jumped in surprise when I noticed someone sitting in one of the guest chairs by my desk. Apparently, one of the partners had come in, tried to get my attention, and failing to do so had patiently sat down to wait for me. I don’t know how long she had been there, but apparently it had been a little while.

Yesterday, a co-worker came to my office door (which I mistakenly had left open) and stood there for a moment. After waiting a few seconds for me to notice him, he asked if he could bother me for a moment. I said “no, not really”. He thought I was kidding and started talking about whatever issue it was he had. I gruffly stopped him and said “No, REALLY”. He then realized I was not kidding, apologized, and moved on. He apologized again this morning, and truthfully I had to think hard to even remember the incident. When you are in the zone, anything outside is unwelcome.

So what is the lesson in all this? Don’t mess with the zone, baby.

I think non-programmers really misunderstand the zone. Quality programming time is fleeting. Some days you have it, some days you don’t. Most of my days are a mix of the two: usually long hours of relative dormancy punctuated by brief periods of intense productivity. This is hard for a lot of people to understand or appreciate, especially non-technical managers of technical people. They see us reading, chatting, emailing, spending oodles of time on blogs and forums, etc., and to them we are just slacking or surfing the Internet. What they don’t get is that we can do a lot more in 2 hours of zone than in 8 hours of drudgery. Extended periods of zone will actually wear me out physically. I’ve had a few of those the last couple of weeks, where by 2pm I am just toast, but man have I done some serious coding!

Any programmer who has done this for a living for more than a month has come to truly cherish zone time. Zone time is gold. This is why we are so intolerant of interruptions. Some of us handle them more diplomatically than others (I had one mentor who would literally throw things at you until you left his cubicle), but we all share the same goal: prevent the loss of the zone. Once the zone is lost, who knows when it will return.

Wrapping it up

So what does the Zone have to do with the lowly cubicle? Well, Timm wants us to comment on our preference for offices or cubicles. For me, working in a cubicle and getting in the zone are almost mutually exclusive, especially in a busy place with a lot of distractions. You can certainly be distracted in an office, frequently by yourself. Truth be told, this blog is a bit of a distraction for me, as is keeping up with all the others I subscribe to. But I find outside interruptions (the phone notwithstanding) to be a lot fewer, especially when I shut my door.

One last thought: the zone is really what we get paid for. Sure, what we know and can do is important, but the zone is when we actually produce. Barry Bonds can hit Home Runs, but that isn’t why he gets paid: he gets paid because when he gets in the zone he actually hits them.

So jump on over to DevTopics and post your preference. You have time, you’re obviously not in the zone right now…

Categories: Miscellaneous

Authorize.net C# Code

February 13, 2008 19 comments

UPDATE 2: The final code for this project is available for download on the Free Code page.


UPDATE: The code for this posting was updated 2-19-2008. I removed the x_password reference. It is not listed in the current documentation, and according to Authorize.Net, the request needs to include the API Login ID and the TransactionCode instead. I also changed isTestMode to IsTestMode for consistency. I also confirmed with them that I need to submit the request from an SSL page, so I am still waiting for that before I can test the code.


My company has an account with Authorize.net, and I am in the process of using it to replace our old PayPal system for online Credit Card purchases. We’ve used their Virtual Terminal system for several years, and once long ago I integrated their CC processing with a PHP site.

I guess it has to do with supplying a ubiquitous solution, but I find that their developer integration tools are somewhat lacking. They have several different methods of submitting and processing payments, and I believe we used SIM (Server Integration Method) previously. In this solution, also my first official foray into the world of ASP.NET, we will be using AIM (Advanced Integration Method) which gives us a much finer level of control over the user experience.

There is a developer’s guide and sample code, but I found the sample code to be lacking in anything terribly useful, like a “sample”. It does not really show you how to send the request or handle the response. I assumed correctly that I am not the first person who wanted a better example, so I hit the Google pavement and found this little gem from 2004 at The Jackol’s Den.

This code gave me a great start, but in looking at it I decided I wanted something a little more flexible. This sample shows all the code in a single method which I assume is supposed to be embedded in an ASP.NET page. I would like to have something more reusable, so I restructured the code a bit.

First, there were a number of items hard coded, such as the Authorize.net version and account information. I could see where you might want this information to be stored in a database or configuration file somewhere, so I put the account information a separate class.

public class AuthNetAccountInfo
{
    public string AuthNetVersion {get; set;}
    public string AuthNetLoginID { get; set; }
    // public string AuthNetPassword { get; set; }
    public string AuthNetTransKey { get; set; }
    public bool IsTestMode { get; set; }
}

Second, some of the actual charge information was hard coded while the rest were passed in to the method as parameters. One item that was hard coded was the description, meaning that this approach would only be good for a single item (either that or the description had to be overly generic). Another is the card number and expiration date! Surely anyone would recognize that this could not go to production, but making it ready for prime time would again mean tying this method to a single page. Parameters such as first name, last name, address, and amount are passed in making the signature of the method long and a little unwieldy. So I created a TransactionRequestInfo class to handle all of these details.

public class TransactionRequestInfo
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Country { get; set; }
    public string Description { get; set; }

    private decimal _amount;
    public decimal ChargeAmount
    {
        get
        {
            return _amount;
        }
        set
        {
            _amount = Decimal.Round(value, 2);
        }
    }

    private string _zip;
    public string Zip
    {
        get
        {
            return _zip;
        }
        set
        {
            int res;
            if (int.TryParse(value, out res))
            {
                if (res > 99999)
                {
                    throw new ArgumentException("Zip Code Value invalid");
                }
                else
                {
                    _zip = res.ToString().PadLeft(5, '0');
                }
            }
            else
            {
                throw new ArgumentException("Zip code must be numeric");
            }
        }

    }

    private string _securityCode;
    public string SecurityCode
    {
        get
        {
            return _securityCode;
        }
        set
        {
            int res;
            if (int.TryParse(value, out res))
            {
                if (res > 999)
                {
                    throw new ArgumentException("Security Code Value invalid");
                }
                else
                {
                    _securityCode = res.ToString().PadLeft(3, '0');
                }
            }
            else
            {
                throw new ArgumentException("Security code must be numeric");
            }
        }

    }

    private string _cardNumber;
    public string CardNumber
    {
        get
        {
            return _cardNumber;
        }
        set
        {
            long res;
            if (long.TryParse(value, out res))
            {
                _cardNumber = res.ToString();
            }
            else
            {
                throw new ArgumentException("Card Number may only contain numbers");
            }
        }
    }

    private string _expDate;
    public string ExpDate
    {
        get
        {
            return _expDate;
        }
        set
        {
            int res;
            if (int.TryParse(value, out res))
            {
                string exp = res.ToString().PadLeft(4, '0');
                int month = int.Parse(exp.Substring(0, 2));
                int yr = int.Parse(exp.Substring(2, 2));

                if (yr > DateTime.Now.Year ||
                    (yr == DateTime.Now.Year && month >= DateTime.Now.Month))
                {
                    _expDate = month.ToString().PadLeft(2, '0') +
                        "/" + yr.ToString().PadLeft(2, '0');
                }
                else
                {
                    throw new ArgumentException("Expiration Date already passed");
                }
            }
            else
            {
                throw new ArgumentException("Zip code must be numeric");
            }
        }
    }
}

Finally, the original method returned a Boolean indicating whether or not the charge was successful. It does a lot of work deciphering and interpreting the errors and building good error messages, but it writes the message to a Label control. Again, this code is intended for a single page. I felt that we needed a real response mechanism, so I created another class for that as well.

public class TransactionResponseInfo
{
    public string AuthorizationCode { get; set; }
    public string TransactionID { get; set; }
    public string ReturnCode { get; set; }
    public string Message { get; set; }
}

Finally, I made a Transaction class to hold the method and made the method static. As you’ll see, most of the original code remains intact, with the necessary changes to incorporate the new class structure discussed above.

public static class Transaction
{
    public static TransactionResponseInfo ProcessPayment(
        TransactionRequestInfo transaction, AuthNetAccountInfo account)
    {
        TransactionResponseInfo response = new TransactionResponseInfo();

        WebClient objRequest = new WebClient();
        System.Collections.Specialized.NameValueCollection objInf =
          new System.Collections.Specialized.NameValueCollection(30);
        //System.Collections.Specialized.NameValueCollection objRetInf =
        //  new System.Collections.Specialized.NameValueCollection(30);
        byte[] objRetBytes;
        string[] objRetVals;
        string strError;

        #region Set Request Values
        objInf.Add("x_version", account.AuthNetVersion);
        objInf.Add("x_delim_data", "True");
        objInf.Add("x_login", account.AuthNetLoginID);
        // objInf.Add("x_password", account.AuthNetPassword);
        objInf.Add("x_tran_key", account.AuthNetTransKey);
        objInf.Add("x_relay_response", "False");
        objInf.Add("x_delim_char", ",");
        objInf.Add("x_encap_char", "|");

        // Billing Address
        objInf.Add("x_first_name", transaction.FirstName);
        objInf.Add("x_last_name", transaction.LastName);
        objInf.Add("x_address", transaction.Address);
        objInf.Add("x_city", transaction.City);
        objInf.Add("x_state", transaction.State);
        objInf.Add("x_zip", transaction.Zip);
        objInf.Add("x_country", transaction.Country);

        // Card Details
        objInf.Add("x_card_num", transaction.CardNumber);
        objInf.Add("x_exp_date", transaction.ExpDate);

        // Authorization code of the card (CCV)
        objInf.Add("x_card_code", transaction.SecurityCode);

        objInf.Add("x_method", "CC");
        objInf.Add("x_type", "AUTH_CAPTURE");
        objInf.Add("x_amount", transaction.ChargeAmount.ToString());
        objInf.Add("x_description", transaction.Description);

        // Currency setting. Check the guide for other supported currencies
        objInf.Add("x_currency_code", "USD");

        if (account.IsTestMode)
        {
            // Pure Test Server
            objInf.Add("x_test_request", "True");
            objRequest.BaseAddress =
              "https://test.authorize.net/gateway/transact.dll";
        }
        else if (!account.IsTestMode)
        {
            // Actual Server
            objInf.Add("x_test_request", "False");
            objRequest.BaseAddress =
              "https://secure.authorize.net/gateway/transact.dll";
        }
        else
        {
            throw new Exception("Transaction Mode Invalid");
        }
        #endregion

        try
        {
            // POST request
            objRetBytes =
              objRequest.UploadValues(objRequest.BaseAddress, "POST", objInf);
            objRetVals =
              System.Text.Encoding.ASCII.GetString(objRetBytes).Split(",".ToCharArray());

            // Process Return Values
            response.ReturnCode = objRetVals[0].Trim(char.Parse("|"));

            if (objRetVals[0].Trim(char.Parse("|")) == "1")
            {
                // Returned Authorisation Code
                response.AuthorizationCode = objRetVals[4].Trim(char.Parse("|"));
                // Returned Transaction ID
                response.TransactionID = objRetVals[6].Trim(char.Parse("|"));
                strError = "Transaction completed successfully.";
            }
            else
            {
                // Error!
                strError = objRetVals[3].Trim(char.Parse("|")) + " (" +
                  objRetVals[2].Trim(char.Parse("|")) + ")";

                if (objRetVals[2].Trim(char.Parse("|")) == "44")
                {
                    // CCV transaction decline
                    strError += "Our Card Code Verification (CCV) returned " +
                      "the following error: ";

                    switch (objRetVals[38].Trim(char.Parse("|")))
                    {
                        case "N":
                            strError += "Card Code does not match.";
                            break;
                        case "P":
                            strError += "Card Code was not processed.";
                            break;
                        case "S":
                            strError += "Card Code should be on card but was not indicated.";
                            break;
                        case "U":
                            strError += "Issuer was not certified for Card Code.";
                            break;
                    }
                }

                if (objRetVals[2].Trim(char.Parse("|")) == "45")
                {
                    if (strError.Length > 1)
                        strError += "n";

                    // AVS transaction decline
                    strError += "Our Address Verification System (AVS) " +
                      "returned the following error: ";

                    switch (objRetVals[5].Trim(char.Parse("|")))
                    {
                        case "A":
                            strError += " the zip code entered does not match " +
                              "the billing address.";
                            break;
                        case "B":
                            strError += " no information was provided for the AVS check.";
                            break;
                        case "E":
                            strError += " a general error occurred in the AVS system.";
                            break;
                        case "G":
                            strError += " the credit card was issued by a non-US bank.";
                            break;
                        case "N":
                            strError += " neither the entered street address nor zip " +
                              "code matches the billing address.";
                            break;
                        case "P":
                            strError += " AVS is not applicable for this transaction.";
                            break;
                        case "R":
                            strError += " please retry the transaction; the AVS system " +
                              "was unavailable or timed out.";
                            break;
                        case "S":
                            strError += " the AVS service is not supported by your " +
                              "credit card issuer.";
                            break;
                        case "U":
                            strError += " address information is unavailable for the " +
                              "credit card.";
                            break;
                        case "W":
                            strError += " the 9 digit zip code matches, but the " +
                              "street address does not.";
                            break;
                        case "Z":
                            strError += " the zip code matches, but the address does not.";
                            break;
                    }
                }

            }
        }
        catch (Exception ex)
        {
            strError = ex.Message;
        }

        response.Message = strError;

        return response;
    }
}

One of the changes I made was the Test Mode handling. Previously, this was also hard coded, requiring the developer to uncomment certain code to make the service “live”. I felt it would be a good enhancement to make this code driven. This way, the test vs. live status can be switched on and off by the end user (or via configuration). The code uses System.Collections.Specialized.NameValueCollection objects extensively, which I wanted to upgrade to a generic Dictionary<string, string>, but the System.Net.WebClient methods required the old NameValueCollection.

I’m not ready to actually publish this code yet: I haven’t written the code to implement this and it still needs to be tested. If I understand AIM, the request will need to come from a server that has an SSL Certificate installed, which as of yet I do not have. {UPDATE: I rechecked the documentation and it says if you have SSL you “may” use AIM, so perhaps this is optional}

Once I have it all working, I will add it to the Free Code section. In the meantime, feel free to try what is here and let me know your thoughts.

First RVNUG Presentation

February 8, 2008 Comments off

Last night I had the pleasure of presenting the program at the monthly RVNUG meeting. I spoke about the new Language Features in C# 3.0 and .NET 3.5. The material came from the first three articles in the “Upgrade Your C# Skills” series. I received lots of nice comments, so many thanks to those of you who were there.

You can download the slides of the presentation (Office 2007 format), but most of the demonstration was done writing live code in VS2008. I will be giving the same presentation at the Roanoke Code Camp on March 1st, so if you can make it be sure to say hello!

Categories: Miscellaneous

Sun agrees to buy MySQL

January 19, 2008 2 comments

OK, so I know this is not a .NET specific topic, but I thought this was important enough to share.? According to SitePoint.com, one of my favorite sites and publishers, Sun is buying MySQL for $1 Billion.? MySQL has been instrumental in the growth of Open Source, the acceptance of PHP, and the explosion of Internet interests such as blogs.? Without a stable, reliable, easy to use, free database, none of these things would have been nearly as successful or prolific as they have been.? For many, this represents a sell out by one of the corner stones of the Open Source movement.

Fortunately, according to the post above, Sun has a good track record of acquiring but not destroying Open Source technologies:

But there is other evidence of Sun understanding the value of keeping software open source and free: projects like OpenOffice.org and NetBeans haveSubo en un juego de casino es la aceptaci?n tacita del envite de otro jugador incrementandolo en la cantidad indicada. healthy communities and promising road maps. In fact, if you examine Sun?s current offerings to the corporate world, the only key component missing from the portfolio (and one that competitors such as Oracle, IBM, and Microsoft already offer) is a database. MySQL, with over 10 million installations worldwide (including high-traffic sites such as Google and Facebook) is a natural fit.

I have written here before about one of my first projects, a wrapper system for accessing multiple ADO.NET data sources.? MySQL was one of the initial databases that project supported (and still does today).? We use MySQL a lot for Internet applcations, so I will be watching this development with some interest.

Categories: Miscellaneous

Saving Word 2007 Docs to PDF

January 3, 2008 Comments off

This is not really a development issue, but something I found that might interest you.

With my new computer, I recently installed Office 2007 Enterprise (courtesy of our Microsoft Partner Action Pack) on my new Vista machine. I wanted to save a document to PDF, so I searched the help and it indicated that I could.

Unfortunately, the default install did not appear to be capable of doing so, but I found this document at Microsoft, which led me to another page: Enable support for other file formats, such as PDF and XPS. Turns out there is a free plug-in you can install that will enable additional file format support.

AIl pi? alto mano vince, e tutti casino in linea mani contengono cinque carte. quick Windows Validation, download, and install later and I am now saving docs to PDF. Just thought I would share, hope it helps someone out there.

Categories: Miscellaneous

Back from Vacation

January 2, 2008 Comments off

Hey folks!? Just wanted to let you all know I’m back from vacation so I’ll be posting again soon.? I did spend the last hour or so upgrading WordPress, so if you see any irregularities be sure to let me know.

On a more traditional note, let me wish you all a Happy New Year.? I’m not a big fan of New Year’s Resolutions, but this year I have a list I’d like to share.? And so, in Calendar year 2008, I resolve to:

  • Learn Blend, XAML, and WPF
  • Finally learn ASP.NET and publish an ASP.NET driven website
  • Take a SQL Server class
  • Publish a White Paper (already underway – watch for the announcement)
  • Post here at least once a week but more often as I am able
  • Attend VSLive! somewhere other than Texas!
  • Find and become an active member of some .NET forums

Naturally, I will be sharing my experiences and efforts with you.? I welcome any and all feedback on the site and its content, so please feel free to comment here about what you would like to see.

Categories: Miscellaneous

Don't forget to close StreamWriter

December 21, 2007 4 comments

In the midst of all my testing, learning, and blogging, my employer occasionally expects me to write a little code. As a result, I’ve spent most of this week being a productive employee, so you’ll have to wait a little while longer for the LINQ articles. In the meantime, I wanted to share a bone head problem I ran into while working on this new project.

The new project is fairly straight forward: read some data from a database, analyze it, look for inconsistencies, correct the inconsistencies, update the database, log the changes in a text file. OK, there’s more going on, but this is the general gist of things.

Having completed the difficult bits, namely recognizing and correcting “user input irregularities”, I moved on to the seemingly mindless task of writing the before and after information to a log file. How many times have we all done this:

StreamWriter file = File.CreateText(logFileName);
foreach (var item in collection)
{
    file.WriteLine("Some Text");
}

Simple, right? I ran a test and opened the text file and all appeared to be well. A few tests later, while viewing the results in Excel 2007, I noticed that the last line was cut off. Surely the problem could not be with Excel, I thought, so I looked at the raw text file, and the last line was cut off so many characters in from the left.

Naturally, all sorts of questions start flying through my head. Can an open StreamWriter only handle so many lines? Or could it be a limitation on number of bytes? Is there some system or program memory setting I’m not aware of? The code was so simple that it had to be something more

But alas, a little digging quickly led me to the answer: it was in fact my code. I failed to Close the StreamWriter, and so there were bytes that even though I had issued WriteLine(), had not yet been saved (or flushed) to the Text file.

I added Close() and sure enough all my data was written to the text file.

StreamWriter file = File.CreateText(logFileName);
foreach (var item in collection)
{
    file.WriteLine("Some Text");
}
// Close the StreamWriter to flush all the data to the file!
file.Close();

It’s good once in a while to run into something like this: it keeps us humble. Like the Roman Generals of old it reminds us that “we are mortal”.

Cheers, and Merry Christmas.

Categories: .NET, Miscellaneous

Developing For .NET is a year old!

December 12, 2007 Comments off

Well, it’s official: today marks the first anniversary of the first post here at developingfor.net.? One year ago today, a friend and former co-worker Jim Burnett wrote the first post about ini File Access with VB.NET.? Jim only wrote a few other posts before moving on to other projects, but he got the ball rolling.

Over the last year, there have been 53 posts, almost 7,000 unique visitors, and over 11,000 page views.? The average view numbers steadily climb every month.? Several of the posts are even frequently submitted to Digg.? What this tells me is that some readers out there like the site and are getting something out of it.? That was always the goal: to help others.

What I would like is to hear from you, the reader.? I’d like to know what you like, what you don’t like, and what you’d like to see.? Send requests for topics, samples, or code that you would like to see here on the site.

So thanks for making 2007 a great start: register today and post a comment, help me make this site even better in 2008.

— Joel Cochran

Categories: Miscellaneous