home · blog · groups · about us · contact us
DevelopmentNow Blog
 Sunday, October 29, 2006
 
 

http://ankhsvn.tigris.org/screenshots.htmlRC4 of AnkhSVN, a Visual Studio plugin that works with Subversion, was released yesterday. From the site:

AnkhSVN is a Visual Studio .NET addin for the Subversion version control system. It allows you to perform the most common version control operations directly from inside the VS.NET IDE. Not all the functionality provided by SVN is (yet) supported, but the majority of operations that support the daily workflow are implemented.

A screenshot from the screenshots page:

Code | Tools
October 29, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 Wednesday, October 25, 2006
 
 

Ok so this is one of my favorite tips in .NET. Of course, there are a lot of things I like about the framework, but anyhow...

A lot of times you'll want to create an array of items, but you don't know how big the array is going to be. You're hoping you can declare an array & just dynamically add items to it like you can with an ArrayList. The first impulse might be to do something like

string[] foo = new string[] { };
foo[1] = "some text";
foo[2] = "some text";

But, that won't work -- you'll get an System.IndexOutOfBoundsException.

However, in .NET 1.1, your friend the ArrayList comes to the rescue with this fun stuff:

ArrayList fooArrayList = new ArrayList();
fooArrayList.Add("some text");
fooArrayList.Add("some more text");

string[] foo = (string[])fooArrayList.ToArray(typeof(string));

Yay! Now you can dynamically add stuff to an array. But wait, there's more -- with .NET 2.0, the Array class somes with a new Resize() generic method, so you can also do this:

string[] foo3 = new string[]{};

// resize array & add an item
Array.Resize<string>(ref foo3, foo3.Length + 1);
foo3[foo3.Length - 1] = "some text";

// resize array & add an item
Array.Resize<string>(ref foo3, foo3.Length + 1);
foo3[foo3.Length - 1] = "some text";

So that's cool. But which is faster, you ask? Let's see, here's the code we used to add 10,000 items to an array.

const int MAX_ITEMS = 100000;
DateTime start;
TimeSpan timeTaken;

// time ArrayList
start = DateTime.Now;
ArrayList fooArrayList = new ArrayList();
for (int i = 0; i < MAX_ITEMS; i++)
{
fooArrayList.Add("some text");
}
string[] foo2 = (string[])fooArrayList.ToArray(typeof(string));
timeTaken = DateTime.Now.Subtract(start);
Console.WriteLine("ArrayList\t" + timeTaken.Milliseconds + " msec\n\n");

// time Array.Resize
start = DateTime.Now;
string[] foo3 = new string[] { };
for (int i = 0; i < MAX_ITEMS; i++)
{
Array.Resize<string>(ref foo3, foo3.Length + 1);
foo3[foo3.Length - 1] = "some text";
}
timeTaken = DateTime.Now.Subtract(start);
Console.WriteLine("Resize\t" + timeTaken.Milliseconds + " msec\n\n");

// prompt for ENTER
Console.WriteLine("Press ENTER.");
Console.ReadLine();

Final result: ArrayList 0 msec, Array.Resize 250 msec. If we go for 100,000 items, it ends up being ArrayList 10 msec, Array.Resize 45000 msec. ;(

So, that's not surprising. The ArrayList probably only resizes things when it needs to and keeps a buffer. So what if we change Array.Resize to resize the Array in chunks of, say, 10,000? And let's go for 1,000,000 items in the array.

const int MAX_ITEMS = 1000000;
DateTime start;
TimeSpan timeTaken;

// time ArrayList
start = DateTime.Now;
ArrayList fooArrayList = new ArrayList();
for (int i = 0; i < MAX_ITEMS; i++)
{
fooArrayList.Add("some text");
}
string[] foo2 = (string[])fooArrayList.ToArray(typeof(string));
timeTaken = DateTime.Now.Subtract(start);
Console.WriteLine("ArrayList\t" + timeTaken.Milliseconds + " msec\n\n");

// time Array.Resize
start = DateTime.Now;
string[] foo3 = new string[] { };
int itemCount = 0;
int arrayChunkSize = 10000;
for (int i = 0; i < MAX_ITEMS; i++)
{
// only resize the array if we need to, and do it in chunks
if (foo3.Length <= itemCount)
Array.Resize<string>(ref foo3, itemCount + arrayChunkSize);
foo3[itemCount] = "some text";
itemCount++;
}
// trim the final size of the array
Array.Resize<string>(ref foo3, itemCount);
timeTaken = DateTime.Now.Subtract(start);
Console.WriteLine("Resize\t" + timeTaken.Milliseconds + " msec\n\n");

// prompt for ENTER
Console.WriteLine("Press ENTER.");
Console.ReadLine();

Much better. ArrayList 150 msec, Array.Resize (chunked) 700 msec. But, ArrayList is still faster. So I guess ArrayList is pretty efficient about expanding its buffer when you add items to it, which is a good thing, since I like ArrayLists.

So...there's your tip, plus we looked at a new Array generic method, and did a bit of performance analysis. If you need a dynamically-sized array, just use an ArrayList and cast it to an array using the .ToArray function. Remember, you can do this for any array, even an array of custom objects.

October 25, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [2]



 Tuesday, October 24, 2006
 
 

Data Dictionary Creator was recently released by Jon Galloway. It's a UI allowing you to document your SQL Server databases by storing the documentation in extended properties, and then exporting it as Excel, XML, etc. The nice thing is it stores the documentation in the same place that SQL Server normally stores its descriptions.

So instead of using the SQL Server tools to update your descriptions like this (ugh):

sqlserverfielddescription.gif

You can now use a cool lil' UI to do it, like this!

Screenshot

October 24, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 
 

Google just announced Google Custom Search Engine, which allows you to create your own custom search engine and stick it on your site with a few lines of code. I was able to add one to http://www.developmentnow.com is about 5 minutes (look at the bottom of the blog page, or in the groups section e.g. at the bottom of this thread about web services).

One downside is that it relies on Google's index, so if your site isn't well indexed by Google (which mine isn't) your results are limited. But, it would be a nice thing for budding webmasters who don't want to build or maintain their own search engine, especially if they made sure to populate Google's index with Google sitemaps. And of course they make it really easy to incorporate AdSense into the search results.

October 24, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 Sunday, October 22, 2006
 
 
I found another online accessibility checker for Section 508 etc compliance at www.aprompt.ca. It's web-based but also has a downloadable Windows version.
October 22, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 Saturday, October 21, 2006
 
 

One of the tricky things with unit testing is getting your test system back to a known state after a test run. If your code involves a lot of database changes, then you often have to go to elaborate lengths like keeping track of all objects & deleting them afterwards, or restoring a database from a recent backup. Ugh.

I just read about EntryZero's dataFresh and it looks interesting. From their site:

Entropy's dataFresh is a toolkit that assists test driven development projects in restoring their database to a known state before each test within a test fixture.  The time consuming effort of having to write tear down methods to clean up the database after running your tests are a thing of the past.

Our appoach is unlike others as we do not attempt to rip and replace the entire database.  Instead we track database modifications to the table level and only work with those tables that have been modified.

So, it might be worth checking it. Currently it only works with SQL Server 2000 & 2005.

October 21, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 Friday, October 20, 2006
 
 

I've been doing some HTML parsing & cleaning lately, which often involves a lot of regular expressions. Turns out that .* doesn't match across newlines, though, so that if you want to grab an HTML page's title value, the following regular expression:

<title>(.*?)</title>

works for this HTML

<title>this is my page title</title>

but not for this HTML

<title>this is my
page title</title>

I usually use regexlib.com for patterns, tips, & testing, & it didn't say anything about the period . not matching against newlines. It supposedly matches any character, but apparently not CR or LF. So...after banging my head against a wall for a while I finally found a good tip at OsterMiller.org that suggested this pattern

<title>((.|[\r\n])*?)</title>

and voila, it worked! So I was able to write my GetTagValue function like so

public static string GetTagValue(string html, string tag)
{
string pattern = @"<\s*" + tag + @"[^>]*>((.|[\r\n])*?)</\s*" + tag + @"[^>]*>";
Match m = Regex.Match(html, pattern, RegexOptions.IgnoreCase);

if (m == null)
return String.Empty;

if (!m.Success)
return String.Empty;

return m.Groups[1].Value;

}

October 20, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 Wednesday, October 18, 2006
 
 

Last year I enrolled in Microsoft's Empower for ISVs program, which for $375 allows small businesses to get an MSDN Universal subscription & other benefits in order to help them build a software product. In exchange for getting MSDN so affordably, you must be a real business (sole proprietor is ok) and be committed to producing at least one Windows-compatible product during your one-year enrollment period.

Now, if you aren't able to get your product produced in time (which I wasn't due to my upheaving move to the pacific Northwest), you're allowed to renew your Empower for ISVs members for an additional year (only), whereupon you have to get your product out within 18 months of your original signup date.

Signing up the first time was easy, but renewing my membership was much harder. I didn't see anything specifically for renewals other than a link to the main enrollment form, which is only for first-time signups. After emailing the Microsoft Empower group, I received this reply:

Thank you for your email regarding your re-enrollment in the Microsoft Empower for ISV Program.

We apologize for any inconvenience this may have caused you. In order to re-enroll in the Empower for ISV Program, you will need to fax in the form to us. There is a fax number on the order form, and you will have the option of paying by credit card or check. We apologize that there is no option to pay online.

No link to said fax form, and I didn't see it anywhere. Finally after some googling I found a link to the Empower Community site and eventually found a link to the Empower for ISVs re-enrollment fax form. So there you go ... print out that form, fill it out, fax it in, and you're re-enrolled. I decided to pay by check (I don't like the idea of my credit card number lying on a fax machine somewhere), so they'll be sending me an invoice in the mail.

October 18, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 Wednesday, October 11, 2006
 
 

I was having some odd errors sending MemoryStreams as email attachments and I wasn't finding samples on the web that worked (or compiled). I was finally able to figure out that the MemoryStream needs to be open in order for the send to go through, like so:

string from = "ben@foobar.com";
string to = "you@foobar.com";
string subject = "my email";
string body = "this is an email with an attachment.";
MailMessage mail = new MailMessage(from, to, subject, body);
System.IO.MemoryStream memorystream = new System.IO.MemoryStream();
System.IO.StreamWriter streamwriter = new System.IO.StreamWriter(memorystream);
streamwriter.Write("Hello world!");
mail.Attachments.Add(new Attachment(memorystream, "test.txt", System.Net.Mime.MediaTypeNames.Text.Plain));
SmtpClient smtp = new SmtpClient();
smtp.Send(mail);
streamwriter.Close();
streamwriter.Dispose();
memorystream.Dispose();


 

If the Stream is closed you'll get a System.Net.Mail.SmtpException whose InnerException says "Cannot access a closed Stream."


October 11, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [2]



 
 

I found out today that if you have EnableViewState="False" in your master page declaration, that ViewState seemed to not be preserved for User Controls (.ascx), even if you have EnableViewState="True" in the Page and User Control's declarations.

I'm sure it's related to what Rick Strahl is talking about here but right now I'm not going to dive into it. ;)

October 11, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 
 

I'm using dasBlog because it's .NET based & I wanted to host it myself. But I've started to tire of the IE-based interface and thought it was time for a desktop tool. BlogJet is the obvious choice, but I wanted to see if there were any decent free tools available.

Tankfully, Scott Hanselman had a post about Windows Live Writer (MS' new free blog writing tool) and some other editors. So we'll see how it goes. ;)

October 11, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 
 

So I had written a big post about slow debugging with Visual Studio and how I got my time down from 30 seconds to 10 seconds. And then IE hung before I could save my post. So, that was kinda annoying, because I thought it was a good post. So maybe I'll be buying BlogJet soon. :/

Anyhow, here's the Cliff's Notes version *sigh*:

  • Run msconfig.exe and use the Startup tab to get rid of programs that you don't need running (e.g. Quicktime)
  • Configure your antivirus program to not scan your ASP.NET Temporary files directory
  • Defrag your hard drive & make sure it has at least 15% free space.
  • Clean up IE's temporary files.
  • Disable Windows services you don't need using Black Viper's Windows XP Services Guide (no longer online, but available here at the Internet Archive). Use services.msc to disable services, not msconfig.
  • If you develop on your laptop, make sure it's plugged in & running at full speed.
  • Specify a new ASP.NET temporary directory via the web.config's <compilation tempDirectory="H:\new directory">. Pick a different partition, a different drive, or use a RamDisk (free one, commercial one) as your temporary directory for even more speed.
  • Make sure you don't have duelling assembly references using this ScottGu tip. Also check your project's Properties->Reference Paths for network or internet references.
  • Look at your Output Window when building to see if you notice any long pauses before certain things. I consistently saw a 5+ second pause preceeding some log4net error messages. After fixing my log4net config and changing my code to initialize log4net on demand (i.e. if I don't log anything, I don't load log4net), my build time dropped by 5-10 seconds.
October 11, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 Tuesday, October 10, 2006
 
 

I've been playing with the free PDF library iTextSharp with ASP.NET and I wanted to create a PDF document on the fly & send it to the browser via ASP.NET. iTextSharp only comes with C# console demo apps (some of which don't work) that only write PDFs to files.

So, here's a little ASP.NET Hello World PDF sample that you can use to get started with generating free PDFs using ASP.NET:

// other using statements goes here
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.xml;
using iTextSharp.text.html;

// standard ASP.NET code goes here, then in Page_load...

protected void Page_Load(object sender, EventArgs e)
{
// create a new memory stream to hold the document
MemoryStream m = new MemoryStream();

// create a new PDF document
Document document = new Document(PageSize.A4.Rotate(), 10, 10, 10, 10);
PdfWriter.GetInstance(document, m);

// open the document
document.Open();

// add some content
document.Add(new Paragraph("Hello world"));

// close the document
document.Close();

// send the PDF to the browser
Response.Clear();
Response.Buffer = true;
Response.ContentType = "application/pdf";
Response.BinaryWrite(m.ToArray());

// release the stream
m.Dispose();

// end
Response.End();
}


 

October 10, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 Saturday, October 07, 2006
 
 

I'm doing a web site where one of the requirements is Section 508 compliance and I wanted to find an online validator similar to W3C's XHTML and markup validator.

So, I found two online accessibility validators that seem to work well:

http://www.contentquality.com/Default.asp

and

http://bobby.watchfire.com/

Also, Stephen Walther has a good article on MSDN about how to configure your ASP.NET pages and controls for accessibility (credit Brian Russell with the link).

 

October 7, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 Thursday, October 05, 2006
 
 

In the .NET 1.1 days, documentation could be generated easily and freely using nDoc. Understandably, the fact that it was free is partly why nDoc has now been discontinued.

Since nDoc doesn't support .NET 2.0, you'll need something else to make CHM and other help files for your new Visual Studio projects. That's where SandCastle, the documentation tool recently provided by Microsoft, comes into play. SandCastle is the new free .NET 2.0 documentation tool of choice (if not the only one), and there's a new Septmber 2006 CTP version out. SandCastle is heavily command-line driven, with many steps involved, which is not what I like to deal with when I need to make documentation. I'd rather automate it, which is where SandCastleBuilder (one of several tools that can automate SandCastle steps). Here's a quick walkthrough on how to use SandCastleBuilder and SandCastle to generate rich help files quickly and easily.

Download the Files

  • First look in your Add/Remove Programs and remove older versions of SandCastle and any helper UIs. You can keep HTML Help Workshop installed, though.
  • Get SandCastle: Go to SandCastle's Wiki at sandcastledocs.com. There should be a link to download the latest version of SandCastle (you can get the September 2006 CTP here).
  • In order to generate HTML help 1.x .CHM files (my recommendation), download HTML Help Workshop here
  • To generate HTML Help 2.0 .HxS files, then
    • If you have Visual Studio 2005, you should download the Visual Studio 2005 SDK here.
    • If you have Visual Studio 2003, download the Visual Studio Help Integration Kit (VSHIK) 2003 here.
  • Get a tool to automate SandCastle. I highly recommend Eric Woodruff's SandCastleBuilder -- it looks very similar to nDoc, supports a lot of features, and unlike many of the other supporting tools, it worked for me the first time around and has been updated to support the latest SandCastle CTP. It's interface is a little more imposing than some of the other tools, but you can run it with my of the default settings.

Fixing a bug with the September CTP (optional)

SandCastleBuilder can generate your help files as CHM files, HxS files, and/or as navigatable websites that look a little like MSDN's online documentation. If you only want to make a CHM or HxS file, you can skip this step. Otherwise, if you want to create a navigatable help website you'll need to fix a small SandCastle bug according to the SandCastleBuilder's documentation:

With the September 2006 CTP, when generating the help as a website, the resulting output will cause JavaScript errors when loaded in Internet Explorer. This is the result of a bug in one of the script files that tries to access a style sheet with an MS-Help format URL. Open up the Presentation\Prototypes\Scripts\StyleUtilities.js file in the SandCastle installation folder and change the getStyleDictionary() function to the following:


    function getStyleDictionary() {
        var dictionary = new Array();

        // iterate through stylesheets
        var sheets = document.styleSheets;
        for(var i=0; i<sheets.length;i++) {
            var sheet = sheets[i];

            // It can't handle ms-help URLs
            if(sheet.href.substr(0, 8) == "ms-help:")
                continue;


            // get sheet rules
            var rules = sheet.cssRules;
            if (rules == null) rules = sheet.rules;

            // iterate through rules
            for(j=0; j<rules.length; j++) {
                var rule = rules[j];

                // add rule to dictionary
                dictionary[rule.selectorText] = rule.style;

            }
        }

        return(dictionary);

    }

Configuring a SandCastleBuilder Project

  • Open up SandCastleBuilder. If you've used nDoc before you'll notice a similar interface, and you can import old nDoc projects. For this post, though, we're going to start from scratch.
  • Pick Project->New Project from Visual Studio Project
  • Navigate to and select a Visual Studio Solution file. SandCastleBuilder will ask you which configuration to use for loading assembly information. You can choose your Debug or Release configuration.
  • You'll see the assemblies listed under "Assemblies to Document."
  • Go ahead and save the Help Project for now by clicking Project->Save Project in the menu.
  • In the Build/HelpFileFormat, the default setting is HtmlHelp 1.x. You can change it to other formats if you want.
  • In the Build/Dependencies section, you'll need to specify any assemblies your project is referencing. That includes third party DLLs and the .NET DLLs. So, open the Documentation Assembly References dialog and add a folder dependency to the version of .NET you're using (e.g. C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727). Make sure your project is built, and add another folder dependency to your project's bin directory, since third party DLLs will probably be copied there.
  • Next, click the Namespaces button in the upper right. Here you can indicate which namespaces should be documented. Normally you wouldn't want documentation for any namespaces that contain web pages. You may have to doubleclick in order to uncheck a namespace. While you're in there, give your namespaces a summary description in the large textfield below the namespace list.
  • If you want, fill in the fields in the Help File section like Copyright, Header Text, HelpTitle, etc.

Generate the Help Files

  • In the Build/HelpFileFormat, the default setting is HtmlHelp 1.x. You can change it to other formats if you want, e.g. HtmlHelp1.xAndWebSite will give you a CHM file and a web site
  • The Paths/OutputPath section determines where your help files will be placed. By default it's in a subdirectory called "Help" inside your project folder. Note that when SandCastleBuilder generates help files, everything in this directory is erased, so if you already have a "Help" subdirectory with stuff in it, change Paths/OutputPath to a different (empty or nonexistent) directory name.
  • Go ahead and save your Help Project, then click Documentation->Build Project. It'll take several minutes for your help file to build, so go get a sandwich.
  • If the generation ends abruptly with an error saying "Unresolved assemby reference", add that assembly to the Build/Dependencies collection, save the Help Project, and try again.
  • You should now have help files inside the Paths/OutputPath (normally <your project directory>\Help). If you set the Build/HelpFileFormat to create a help website, the files will be inside this directory, too.

Automatically Generating Help Files in Visual Studio Whenever You Compile Your Project

If you want to generate your help files automatically whenever you compile a new release version of your project, follow these instructions (from the SandCastleBuilder help):

Right click on the project name in the Solution Explorer, select Properties, and select the Build Events sub-item. Click in the Post-build Event Command Line option to enter the command line to run. You can click the Edit Post-build button to open a dialog with a larger editor and a list of available macros.

In a solution with multiple projects that are documented by the same help file builder project, the post-build event should be defined on the last project built by Visual Studio. If the projects are documented individually, you can place a post-build event on each one.

Below is an example of a common command line script that can be used (lines wrapped for display purposes). Replace the path to the tool with the path where you installed it on your PC, and specify the correct location of the SandCastleBuilder project file (.shfb). The IF statement prevents building the help file in debug builds where it may not be needed.

IF "$(ConfigurationName)"=="Debug" Goto Exit

"C:\Program Files\EWSoftware\Sandcastle Help File Builder\
SandcastleBuilderConsole.exe" $(SolutionDir)Doc\TestProject.shfb

:Exit

Conclusion

That's pretty much it. I'm sure later versions of SandCastle will feature more, but with the right helper tools it's pretty easy to get started.

 

 

 

October 5, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]



 Wednesday, October 04, 2006
 
 
Atlas, Microsoft's AJAX framework for ASP.NET, has been renamed to ASP.NET AJAX. Not that there's a huge surprise, since Atlas was just a code name. There's no new CTP, although there's a new version (rel 9/14/06) of the Atlas Control Tool Kit with a few new controls.
October 4, 2006    Bookmark to Digg or other social bookmarking
#    Disclaimer  |  Comments [0]