Subscribe | Alerts via Email
View All Quotes
“It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way …”
-Charles Dickens
<July 2010>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

©2010 Cal Zant
Sign In
Total Posts: 106
This Year: 5
This Month: 1
This Week: 0
Comments: 2

One of the biggest drawbacks of using ASP.NET is the way it can really bloat a page size with ViewState.  That is because http and the web in general are stateless by nature, but when creating applications developers often need to track what changed on a form ... hence ViewState was introduced to essentially let the development code as if the web was stateful.  Although some left-wing purist might argue, my more pragmatic nature causes me to think this was a good thing overall because of the dramatic increase in productivity it offers.  However, the downside was bloated pages to the client ... but now ASP.NET 4.0 offers a better way to control that.

Pre 4.0, ASP.NET offered the EnableViewState property which you could put on a page or control.  If you set that value to false, it disabled all viewstate for that control and any child controls.  The problem was you didn't have the ability to override that behavior in any of the child controls.  So this was more of a system where it had to be enabled at a high level, but then disabled in an opt-out fashion where you didn't really need it.  The result was that people just didn't opt-out.  That isn't because they were lazy (well, not completely) ... its just that pattern doesn't encourage good practices.

ASP.NET 4.0 includes a new ViewStateMode property that is completely different than EnableViewState.  It allows you to set the overall viewstate for a site off by default, but that can be overridden at any level below that.  This creates an opt-in pattern.  When I first tried to confige this setup, I ran into a few problems because I was trying to mix EnableViewState with ViewStateMode. EnableViewState always trumps the ViewStateMode setting.  I think it is much simpler to not mix the two and just avoid the old school EnableViewState property all together, and solely rely on ViewStateMode to control your ViewState.

The ViewState Opt-In Pattern Using ViewStateMode

  1. Set the site's masterpage(s) to have a ViewStateMode setting of "Disabled".  Unfortunately you can't set that property at the page level in the web.config, but disabling at the master page level should have a similar site-wide effect.

    <%@ Master Language="C#" ViewStateMode="Disabled" ...

    You could optionally set the ViewStateMode="Disabled" on one or more of the content placeholders in your master page:

    <asp:ContentPlaceHolder ID="ContentPlaceHolder1" ViewStateMode="Disabled" ...
  2. Then, when you need viewstate on a certain page or control ... just set ViewStateMode="Enabled" at that location.  It is usually very obvious when you need this when you are developing the site from scratch, but this type of pattern can be more difficult to retro-fit into an existing app.  Essentially the things that typically need ViewState are controls that you will need to access the values of on a PostBack to the server (e.g. textboxes, drop downs, checkboxes, etc).

    One tricky thing I have ran into is that you can't set the ViewStateMode at the page level in the Page directive like this (at least when using master pages):

    <%@ Page Title="Dynamic Schedule" ViewStateMode="Enabled" ... // WON'T WORK

    Instead you should preferrably set it for the individual controls that need it that way only the ViewState data that is required would be sent to/from the client, and the page wouldn't be bloated with unnecessary ViewState data.

    <asp:CheckBoxList ID="cblProducts" ViewStateMode="Enabled" ...

    If you have many controls that you need to enable ViewState on, and you didn't want to have to set the property on all of them ... you could optionally just enable viewstate for an entire content placeholder section like this:

    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" ViewStateMode="Enabled" ...

Here are a few more helpful resources on the new ViewStateMode functionality:

http://msdn.microsoft.com/en-us/library/system.web.ui.viewstatemode.aspx
http://weblogs.asp.net/sreejukg/archive/2010/04/06/viewstatemode-in-asp-net-4-0.aspx
http://www.mostlylucid.net/archive/2009/01/28/1312.aspx

Friday, July 23, 2010 2:06:36 PM (Central Standard Time, UTC-06:00)  # 

This is a T-SQL code snippet I find myself using regularly to find all references to a particular field or table, or search for comments from a particular date.  It essentially allows you to search for a keyword in all of the definitions for stored procedures, functions (scalar or table-valued), and views.  It uses some of the built-in methods SQL Server provides for obtaining metadata, which are essentially simplified views of the data contained in system tables like sysobjects and syscolumns.  I know this T-SQL script works on SQL Server 2005 & SQL Server 2008, but don't know if the methods it is dependent on were available on previous versions.

DECLARE @Keyword varchar(64)
SET @Keyword = '%keyword%'

(
  SELECT  [ROUTINE_TYPE] AS 'Type', [SPECIFIC_NAME] AS 'Name'
  FROM  INFORMATION_SCHEMA.ROUTINES
  WHERE  [ROUTINE_DEFINITION] LIKE @Keyword
 UNION
  SELECT  'VIEW' AS 'Type', [TABLE_NAME] AS 'Name'
  FROM  INFORMATION_SCHEMA.VIEWS
  WHERE  [VIEW_DEFINITION] LIKE @Keyword
)
ORDER BY [Type], [Name]

Here is an example of the results of the script:

Search Results of All SQL Defintions for Stored Procedures, Functions, & Views
Tuesday, April 13, 2010 6:45:29 AM (Central Standard Time, UTC-06:00)  # 

During my career, I have written a few components that used colors to visually encode data.  Recently I found myself looking at a color chart and trying to choose colors that were easy to distinguish from one another ... again.  It seems like I have done that a few times during my career, so this time I wanted to put it to bed for good.  Instead of relying on my unscientific approach, I figured this had to be a problem that someone solved before ... and Excel came to mind.

When you create a chart in Excel, it automatically assigns each series a different color.  Microsoft almost certainly poured a ton of research/money into this same problem I was trying to solve:

Which set of colors are easiest to differentiate from one another?

They probably also took visual disabilities into account, like color blindness (which I have) and poor vision.  So why reinvent the wheel with this stuff?  Here is a pie chart that displays default color series Excel 2007 automatically assigned to a simple pie chart, which includes the HEX and RGB values for each series color:

Default Excel Color Series Easy Colors To Tell Apart

A few things to keep in mind

  • Here is an helpful excerpt from Information Dashboard Design (a very research driven book) on this topic: "When organizing data into distinct groups using different expressions of any preattentive attribute, you should be careful not to exceed five distinct expressions. ... When using hue, keep in mind that even though we can easily distinguish more than five hues, short-term memory can't simultaneously retain the meaning of more than about nine in total."
  • When using colors to visual encode information, think about what it would look like if printed in black and white.  Would you still be able to distinguish between the different series or groups, or would that encoding be lost without color?  Although the project you are working on may never be printed black and white, almost 10% of males have some type of color blindness ... so thinking through this scenario can help you understand their perspective.  Usually adding a label (like the numbers in the example above) or patterns (e.g. stripes) in addition to color would be enough to enable someone to make sense of the encoding, even if they aren't able to process it preattentively like the color coding.

Here is a C# code snippet that I use, which returns a list of the related colors outlined above:

/// <summary>
/// Returns a list of colors derived from the defaults Excel uses for chart series.
/// These colors should be very easy to differentiate from one another.
/// </summary>

public static List<Color> DefaultExcelColors
{
    get
    {
        return new List<Color>()
            {
                Color.FromArgb(69,114,167),
                Color.FromArgb(170,70,67),
                Color.FromArgb(137,165,78),
                Color.FromArgb(113,88,143),
                Color.FromArgb(65,152,175),
                Color.FromArgb(219,132,61),
                Color.FromArgb(147,169,207),
                Color.FromArgb(209,147,146),
                Color.FromArgb(185,205,150),
                Color.FromArgb(169,155,189)
            };
    }
}

Tuesday, April 06, 2010 6:50:28 AM (Central Standard Time, UTC-06:00)  # 

A few weeks ago, I started noticing that every time I opened a LINQ to SQL .dbml file in Visual Studio 2008 I would get an alert message that said "The connection property in the web.config file is missing or incorrect.  The connection string from the .dbml file has been used in its place."

The connection property in the web.config file is missing or incorrect

The dbml file was configured to reference a connection string in the web.config file, but after I got this alert and clicked OK, Visual Studio would check out the project and add a new Settings.settings file under the Properties folder and generate a new connection string there, and then change the dbml file to reference that instead of the web.config.

Changes that were automatically applied to the project after I clicked OK

This would probably be the behavior you would expect if the web.config was really missing the connection string, but in my case the referenced connection string was there and well-formed.  I know this, because the related code was under source control and other developers could open the same dbml file (from the shared code base) and they wouldn't get the alert and everything worked flawlessly.

To me, it seemed like the LINQ to SQL designer on my machine was simply having trouble reading from the configuration file ... so I thought it might be an issue with a particular dll or registry entry.  Another thing that convinced me of this was the fact that when you were in the dbml editor ... under the Connections property on my computer, none of the connection strings from the web.config were included in the drop down to select from.  But, when another developer would look at this on their machine (using the same exact code base and files), their Visual Studio environment would find those connection strings in the web.config and include them in the list to select from.

Connection Options on my computer containing no references to web.config connection strings

Connection Options on other computers containing references to web.config connection strings

I figured out that I could just undo the changes to add the new file, and then view the differences of the dbml file's XML and undo the reference change to make it once again reference the web.config instead of the settings file it created ... and that worked fine for a couple weeks, but eventually jumping through those extra hoops that "should just work" got pretty old.

So I first tried to repair my Visual Studio install, but that didn't work.  Next, I completely uninstalled Visual Studio and then re-installed it and the related service packs ... but that didn't work either.  I was about to just format my machine and re-install Windows ... but I thought I would give Microsoft Support a chance, hoping they could help me.  I knew this was an obscure enough problem that they probably wouldn't be much help, but I really hate paving my machine ... so it was worth a little time to give them a chance.  After explaining what was going on to a technician, we tried to use ProcMon to figure out what was going on behind the scenes.  We noticed several registry errors, and that further convinced me that the problem was corrupted  or missing registry keys.  The support technician said he would have to look into it more, but I wasn't going to count on them as my only hope for a solution ... so I kept investigating myself.  Apparently when you uninstall Visual Studio, it doesn't remove all of the registry keys.  It also doesn't overwrite them all when you run a repair.  It doesn't even remove them all when you remove all the other software that might have hooks or add-ins related to Visual Studio.  So these corrupted or missing registry keys could have persisted through the repair and re-install.

Here are some of the issues we noticed in ProcMon (it is a snapshot of what was happening when I would open the dbml file and the pop-up alert would appear, and then I would click OK).  The one highlighted is the what I was guessing was the culprit, because that missed registry key occurred right between the read of the dbml file and the opening the related project (which I am guessing was to create the new settings file).  You can ignore the "Fast IO Disallowed" stuff, because that is unrelated.

Related errors from Process Monitor while opening the LINQ to SQL dbml file

So, although the support technician had actually explicitly advised me not to do this ... I decided to uninstall Visual Studio (and any other software add-ins that might be related to it), manually delete the related registry keys contained in any folder that appeared related to Visual Studio, and then do a fresh install.  I never think hacking in the registry is a good solution to a problem, but this was my last ditch effort ... and I was resolved to do a full re-install of Windows if it didn't work.  To my surprise ... it actually worked.

I can't guarantee this will work for you, or even recommend it ... but here are the registry folder locations I manually deleted:

  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio
  • HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio
  • HKEY_CURRENT_USER\SOFTWARE\Microsoft\VSCommon
  • HKEY_CURRENT_USER\SOFTWARE\Microsoft\VSTAHost

Hopefully if you are still reading, this is the exact problem you are having and this will work for you as well.  The only thing I can say is ... it worked on my machine, and it saved me from having to completely re-install Windows to fix the problem.

Wednesday, January 20, 2010 9:22:50 AM (Central Standard Time, UTC-06:00)  # 

I was recently using SQL Server Report Builder, and needed to format a date and time in a specific way.  I went to edit the related expression, and looked for built-in functions to help me work with dates under Common Functions > Date & Time ... where I found FormatDateTime:

Using FormatDateTime in SSRS Expression

This is the example shown for how to use it:

=FormatDateTime(Fields!BirthDate.Value, DateFormat.ShortDate)

This is helpful, because the 2nd argument is obviously a DateFormat enumeration ... but what are the other members besides ShortDate?  I searched (like you might have) and couldn't come up with anything.  The only thing I can assume, is that the DateFormat enumeration used in SSRS expressions is the same as the DateFormat enumeration in Visual Basic, which is:

  • GeneralDate
  • LongDate
  • ShortDate
  • LongTime
  • ShortTime

None of these "canned" formats matched the way I wanted to format the date, so I kept looking and noticed some MSDN samples that used the Format method instead of the FormatDateTime method.  In the expression edittor, that function can be found under Common Functions > Text:

Format a date and time in SQL Report Builder Expression

The Format method is very flexible, and allows you to use any of the standard string-based format patterns you are probably already familar with.  Here are a few common examples:

=Format(Fields!myDateTime.Value, "M/d/yy") ... 6/15/09
=Format(Fields!myDateTime.Value, "M/d/yyyy h:mmtt") ... 6/15/2009 2:45PM
=Format(Fields!myDateTime.Value, "MM/dd/yy HH:mm") ... 06/15/09 14:45
=Format(Fields!myDateTime.Value, "MMM d, yyyy") ... Jun 15, 2009
=Format(Fields!myDateTime.Value, "Short Date") ... 6/15/2009
=Format(Fields!myDateTime.Value, "Long Date") ... Monday, June 15, 2009

Of course, the standard format strings like "Short Date" and "Long Date"  may vary depending on the culture set on your system.  The examples above reflect the "en-US" configuration.

Then, here is a slight variation of the 2nd sample.  It is my personal favorite and how I usually format all dates that have time values related to them, because I think the lowercase AM/PM designator makes the value easier for someone to scan, and requires less conscience effort to process.  If it is all uppercase, the AM/PM designator seems to blend in with the rest of the text and you have to pay a little closer attention to interpret the related value (even if it is just a tenth of a second).

=LCase(Format(Fields!myDateTime.Value, "M/d/yy h:mmtt")) ... 3/15/2009 2:45pm

Tuesday, December 29, 2009 3:06:08 PM (Central Standard Time, UTC-06:00)  # 

I was working on a report in Report Builder 2.0 for SQL Server 2008 Reporting Services, and added a textbox to an existing report.  I wanted to display some summary info related to the data shown in the report.  I added a textbox, then went to edit the “Expression” property of that report item, but wasn't able to select any fields because it said “Report item not linked to a dataset.”

 

Report Item not linked to a dataset

 

So I tried to find a property on the textbox related to a "Dataset" (like the "DataSetName" property on Tablix elements), but the only thing I found was a “DataElementName” property … which I tried, but didn’t work.

SSRS Report Item Textbox and Tablix Report Builder Properties

I then learned you can link to a dataset by defining it’s name directly in the expression, like this:

=Count(Fields!Address.Value,"DatasetName")

Monday, December 28, 2009 11:36:38 AM (Central Standard Time, UTC-06:00)  # 

I have some WCF services that I work in quite a bit, and ever since I enabled the project to automatically generate an XML documentation file (so I could use Sandcastle to build MSDN-style help files with it) ... I started getting really verbose warnings every time I built the project.  It would warn me about any publicly visible types or members that didn't have explicit XML comments associated with it.  Because those comments wouldn't be exposed to the WCF client that consumes the service anyway, my team doesn't add XML comments to every class and property (at least not the ones where the name makes the behavior completely obvious, so that a summary isn't needed).

Every time I did a build I would see a ton of warning that said something like "Missing XML comment for publicly visible type or member..."  So, I wanted to supress all of those warning, so I didn't have to wade through them to see other warnings that really did need to pay attention to.  In order to hide a particular warning, you have to figure out what the warning number is for the type of message you want to disable.  One easy way to quickly find that in VS 2008 is to simply right click on the warning and click on "Show Error Help":

Find Number To Disable Build Warnings

The help file that pops up, will contain a heading like shown below ... which contains the error number or warning number that you need to know to supress that type of message (highlighted):

Compiler Warning Help

After you have the number, all you have to do is set the project's build to surpress that warning number.  To do that, just right click on the project in Solution Explorer, and select "Properties."  Then, under the "Build" tab, find the section for "Errors and warning" and add the warning number to the "Supress Warning" textbox like shown below:

Configuring Build To Supress Certain Warnings

Note: You can also decrease the overall warning level for the project, which will make VS less verbose about non-critical warnings ... but I personally think it is a better idea to leave it at 4 (the default) and explicitly filter warnings that are a problem.

Thursday, October 08, 2009 10:30:35 AM (Central Standard Time, UTC-06:00)  # 

I got this error message when trying to open a few open source or sample solutions in Visual Studio 2008.

error x.csproj cannot be opened The project type is not supported by this installation

Of course I had the generic C# projects installed, so I googled around and after sifting through a few solutions that didn't work ... I realized what the issue was.

The real issue is that Visual Studio 2008 didn't recognize some of the "ProjectTypeGuids" defined in the .csproj file, because one or more those GUIDs referenced a type of project that wasn't installed.  However, instead of figuring out which project templates you are missing, and finding out where to download those from and get them installed ... there is a shortcut you can take if you just want to open the project.  You can simply replace that tag in the .csproj file with standard GUIDs (installed by default in any instance of VS), then just reload the project and it should open as expected.

Here is an example of the before and after for one sample app I had problems opening.  The app is called Nerd Dinner, which is an open source application that Scott Hanselman created (http://nerddinner.codeplex.com/).  It turns out it has a dependency on MVC, and I didn't have the project templates related to MVC installed on my machine (nor did I want to install them).  The templates for MVC aren't bundled with VS 2008, but instead are installed through a separate "extension" download (which is the case for a lot of project templates).  All I wanted to do is look at some of the code, so by removing the bad GUID references, I could then open the project and poke around.

Before:

<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    ...
    <ProjectTypeGuids>{603c0e0b-db56-11dc-be95-000d561079b0};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    ...

After:

<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    ...
    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    ...

That's it ... just replace the entire <ProjectTypeGuids> tag with the values shown above, and reload the project.  Note: You might need to open the csproj file in notepad to be able to edit the underlying XML this way.

Tuesday, October 06, 2009 7:40:08 AM (Central Standard Time, UTC-06:00)  # 

I was trying to add a Database Trace Listener to my Enterprise Library Logging Application Block, and had a hard time figuring out what the signature of the two stored procedures it references were supposed to look like.  Here are the procedures that I am referring to, AddCategoryStoredProcedure and WriteLogStoredProcedureName:

I finally just opened up the source code for the Enterprise Library itself to look what it was going to try to pass those procedures, and then created procedures that had the parameters it expected.  To save you the same trouble ... here is what those procedures are supposed to look like:

CREATE PROC [dbo].[WriteLog]
@EventID int,
@Priority int,
@Severity nvarchar(32),
@Title nvarchar(256),
@Timestamp datetime,
@MachineName nvarchar(32),
@AppDomainName nvarchar(512),
@ProcessID nvarchar(256),
@ProcessName nvarchar(512),
@ThreadName nvarchar(512),
@Win32ThreadId nvarchar(128),
@Message nvarchar(1500),
@FormattedMessage ntext,
@LogId int OUTPUT
AS

-- Add logic here to insert the values into the appropriate table

-- Set the ID the DB automatically assigned to the log entry so it
--
can be returned as an output parameter
SET @LogID = SCOPE_IDENTITY()

Then here is the one that tags the log with one or more categories:

CREATE PROC [dbo].[AddCategory]
@CategoryName varchar(MAX),
@LogID int
AS

-- Add logic here to insert the values into the appropriate table

Friday, September 18, 2009 12:13:54 PM (Central Standard Time, UTC-06:00)  # 

I was recently trying to store my Enterprise Library configuration in a common location, and read some people talking about SqlConfigurationSource that comes as a sample with the Enterprise Library.  It took me forever to get my hands on the actual .dll and T-SQL code needed to make use of this ... so I thought I would try to save someone else the pain of searching for that stuff, installing, and compiling the samples.  Here are links to the compiled dll, along with the T-SQL code needed to setup the database to be able to store the config settings.

Here is an example of the type of code you should be able to use to dynamically log the config settings from a SQL database:

var source = new SqlConfigurationSource(
   "ConnectionString",
   "EntLib_GetStoredProcName",

   "Entlib_SetStoredProcName",
   "Entlib_RefreshStoredProcName",
   "Entlib_RemoveStorecProcName");

var factory = new LogWriterFactory(source);
var writer = factory.Create();
writer.Write(logEntry);

Microsoft.Practices.EnterpriseLibrary.SqlConfigurationSource.dll (26.5 KB)

SqlConfiguration.zip (.99 KB)

To read more about how this works, check out this: http://geekswithblogs.net/akraus1/articles/62869.aspx

Also, a tool named "SQL Config Convertor" really helped me out.  It is an older open source project that helps import the configuration settings that are currently stored in a .config file into the new database structure in the proper format.  It was a little bit painful to get working, but I did finally get it up and running and it still saved me a lot of time.

http://code.msdn.microsoft.com/SqlConfigConverter

Here is an example of the data that the configuration table would store:

section_name section_type section_value lastmoddate
loggingConfiguration Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral <?xml version="1.0" encoding="utf-16"?><SerializableConfigurationSection name="Logging Application Block" tracingEnabled="true" defaultCategory="General" logWarningsWhenNoCategoriesMatch="true" revertImpersonation="true">      <listeners>          <clear />          <add source="Enterprise Library Logging" formatter="Text Formatter" log="Application" machineName="" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" traceOutputOptions="None" filter="All" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="Formatted EventLog TraceListener" />      </listeners>      <formatters>          <clear />          <add template="Timestamp: {timestamp}&#xD;&#xA;Message: {message}&#xD;&#xA;Category: {category}&#xD;&#xA;Priority: {priority}&#xD;&#xA;EventId: {eventid}&#xD;&#xA;Severity: {severity}&#xD;&#xA;Title:{title}&#xD;&#xA;Machine: {machine}&#xD;&#xA;Application Domain: {appDomain}&#xD;&#xA;Process Id: {processId}&#xD;&#xA;Process Name: {processName}&#xD;&#xA;Win32 Thread Id: {win32ThreadId}&#xD;&#xA;Thread Name: {threadName}&#xD;&#xA;Extended Properties: {dictionary({key} - {value}&#xD;&#xA;)}" type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="Text Formatter" />      </formatters>      <logFilters>          <clear />      </logFilters>      <categorySources>          <clear />          <add switchValue="All" autoFlush="true" name="General">              <listeners>                  <clear />                  <add name="Formatted EventLog TraceListener" />              </listeners>          </add>      </categorySources>      <specialSources>          <allEvents switchValue="All" autoFlush="true" name="All Events">              <listeners>                  <clear />                  <add name="Formatted EventLog TraceListener" />              </listeners>          </allEvents>          <notProcessed switchValue="All" autoFlush="true" name="Unprocessed Category">              <listeners>                  <clear />              </listeners>          </notProcessed>          <errors switchValue="All" autoFlush="true" name="Logging Errors &amp; Warnings">              <listeners>                  <clear />                  <add name="Formatted EventLog TraceListener" />              </listeners>          </errors>      </specialSources>  </SerializableConfigurationSection> 9/17/2009 2:44:42 PM

Thursday, September 17, 2009 10:37:37 AM (Central Standard Time, UTC-06:00)  # 

I was recently attempting to build documentation for a few projects using Sandcastle, a free engine provided by Microsoft that compiles documentation with a MSDN look and feel from managed class libraries and projects.  Sandcastle doesn't have a GUI or command-line interface built-in, so I used the Sandcastle Help File Builder, which is an open source project.  If I used a dll as the documentation source, it was able to build the documentation ... but you are also supposed to be able to reference a project (e.g. a file with a .csproj extension), but when I tried to do that instead of the dll I got this error:

SHFB: Error BE0065: BUILD FAILED: The imported project "C:\Program Files\MSBuild\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.  C:\...\XXX.csproj

I googled this error (like you probably did), and found this post about multi-targetting that seemed unrelated at first ... but turned out to kind of be the answer.  It suggested replacing the typical import statement in the .csproj file with one that included conditions, which allows the project to automatically determine which Microsoft.WebApplication.targets file to reference based on the environment.

To fix it, I just opened the .csproj file with Notepad, and removed the existing import statement related to Microsoft.WebApplication.targets (towards the bottom of the file), and replaced it with two import statements that included the conditions.

Replace This Line:

<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" />

With These Lines:

<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v8.0\WebApplications\Microsoft.WebApplication.targets" Condition="'$(Solutions.VSVersion)' == '8.0'" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" Condition="'$(Solutions.VSVersion)' == '9.0'" />

After that it built fine with just the reference to the project file instead of the actual dlls.  This is a good thing, and here is a tip from the Sandcastle Help File Builder documentation that explains why this is better than referencing the dlls directly:

Given that solutions and projects are supported as documentation sources, you may find it easier to add them as documentation sources instead of the assemblies, comments, and references that they contain. When a solution or project is used, these items are imported from them automatically at build time.

Monday, September 14, 2009 7:36:25 AM (Central Standard Time, UTC-06:00)  # 

A recent project forced me to try to use the COM-based Office Interop stuff ... painful.  I found some example code and tried to get it to run, but kept getting a System.UnauthorizedAccessException "Retrieving the COM class factory for component with CLSID ..." exception on this line of code:

ApplicationClass excelApplication = new ApplicationClass();

Retrieving the COM class factory for component with CLSID exception

Although the exception above didn't give me much direction, I was able to uncover more details when I went to view the same error in the computer's event log:

"The machine-default permission settings do not grant Local Activation permission for the COM Server application with CLSID {00024500-0000-0000-C000-000000000046} to the user DOMAIN\USER SID (S-1-5-21-789336058-854245398-1417001333-2107) from address LocalHost (Using LRPC). This security permission can be modified using the Component Services administrative tool."

The machine-default permission settings do not grant Local Activation permission for the COM Server application with CLSID

Turns out you have to configure some COM security stuff on the computer before you can actually make a call like this.  Here is what you have to do to adjust the settings.

  1. Figure out what application the CLSID GUID represents.  To do this you have to look in the registry.  So run "regedit" and go to Computer\HKEY_CLASSES_ROOT\CLSID and find the related CLSID folder.  In my case, the error was related to "Microsoft Office Excel Application."
    Figure out what application the CLSID GUID represents
  2. Open the "Compenent Services" configuration window by running "dcomcnfg".

  3. Find the related app like shown below, and then right-click on it and go to "Properties"
    Find the related application in component services
  4. In the "Properties" window go to the "Security" tab.  Then under "Launch and Activation Permissions" click the "Customize" radio button, then click edit.
    customize the launch and activation permissions
  5. Add the account the code is executing as and give them the appropriate permissions:
    add account code is executing as
  6. Then go back to the "Component Services" configuration window and right-click on "My Computer" and go to "Properties":
    Go to computer properties in Component Services configuration window
  7. Under the "COM Security" tab find the area for "Launch and Activation Permissions" and click the "Edit Default" button.
    Edit default for Launch and Activation Permissions
  8. Add the account and set the permissions like we did in step 5. 

That should be it.  Try the code again and it should run (or at least get you past this particular exception).  So far, my experience with COM is that it is painful and should be avoided at almost any cost.  In my scenario though, it seems to be the only viable option out there, because no 3rd party component has the particular feature I needed ... so sometimes this is unavoidable, and I hope these instructions make it a little less painful for you.

Monday, July 20, 2009 9:16:23 AM (Central Standard Time, UTC-06:00)  # 

This is really simple, but I just keep forgetting how to do it and it isn’t real easy to find on Google … so here it is:

subst g: "C:\Program Files (x86)"

To delete a drive mapped this way, do this:

subst g: /d

Friday, July 17, 2009 3:10:56 PM (Central Standard Time, UTC-06:00)  # 

In my recent post on How To: Find Missing Indexes in SQL Server, I showed how to find the information related to indexes that SQL Server thinks would be helpful (based on query stats it has been keeping since it was last rebooted).  The actual "CREATE INDEX" script you have to write based on that info can be a little tedious to get write, so I created a user-defined function that you could simply pass some of the info you get from the query in that post and it will return the necessary T-SQL you would need to run in order to create the index for the given info.

ALTER FUNCTION [dbo].[bcz_fn_GetCreateIndexScript](@TableName varchar(100), @EqualityColumns varchar(500), @InequalityColumns varchar(500), @IncludedColumns varchar(500))
RETURNS varchar(MAX)
BEGIN

 DECLARE @CreateScript varchar(MAX), @Name varchar(128), @EqualityAndInequalityColumnList varchar(MAX), @ColumnListForName varchar(MAX), @MaxColumnListLength int, @IndexPrefix varchar(63)

 -- Construct what the prefix of the index will be (this will be followed by the list of related columns)
 SET @IndexPrefix = 'IX_' + @TableName + '_'

 -- Construct a list of all columns that will be involved with the index (equality, inequality, and included)
 SET @EqualityAndInequalityColumnList = ''
 IF @EqualityColumns IS NOT NULL
  SET @EqualityAndInequalityColumnList = @EqualityColumns
 
 IF @InequalityColumns IS NOT NULL
 BEGIN
  IF (LEN(@EqualityAndInequalityColumnList) > 0) SET @EqualityAndInequalityColumnList = @EqualityAndInequalityColumnList + ', ' + @InequalityColumns
  ELSE SET @EqualityAndInequalityColumnList = @InequalityColumns
 END
 
 SET @ColumnListForName = @EqualityAndInequalityColumnList
 
 IF @IncludedColumns IS NOT NULL
 BEGIN
  IF (LEN(@ColumnListForName) > 0) SET @ColumnListForName = @ColumnListForName + ', ' + @IncludedColumns
  ELSE SET @ColumnListForName = @IncludedColumns
 END

 -- Remove the brackets around the column names, and make it "_" delimited instead of ", " delimited
 SET @ColumnListForName = REPLACE(REPLACE(REPLACE(@ColumnListForName, '[', ''), ']', ''), ', ', '_')
 
 -- The max length of the index name must be <= 128 ... so calculate how big the column list can be with
 -- respect to that and the given naming convention we will use.
 SET @MaxColumnListLength = 128 - LEN(@IndexPrefix)

 -- Create the name with the list of columns appended to the end of it
 SET @Name = @IndexPrefix + SUBSTRING(@ColumnListForName, 1, @MaxColumnListLength)

 SET @CreateScript =
  'CREATE NONCLUSTERED INDEX ' + @Name + ' ON ' + @TableName + ' (' + @EqualityAndInequalityColumnList + ')'
  
 IF @IncludedColumns IS NOT NULL
  SET @CreateScript = @CreateScript + ' INCLUDE (' + @IncludedColumns + ')'

 RETURN @CreateScript
END

If you would like to dig deeper into this, here is a good resource: http://msdn.microsoft.com/en-us/library/ms345405.aspx.

Wednesday, July 08, 2009 1:29:35 PM (Central Standard Time, UTC-06:00)  # 

I listened to a .NET Rocks podcast recently on database design, and although I found most of the content near worthless … they did mention in passing some dynamic management views that are built into SQL Server that I was unaware of.  They turned out to be immensely helpful, so I thought I would share them.

Some of the dynamic management views are designed to provide usage info about the indexes you currently have in place in a particular database.  Often times we create indexes that seem like they would be helpful for some functionality, but if the query that is actually ran against the database varies slightly from what we had in mind … SQL might not even be able to use the index.  There are also times were the data a particular index is built on is updated much more frequently than it is used, and is therefore causing a huge amount of maintenance overhead for an index that is rarely used … and is therefore robbing performance.

Here is a simple T-SQL script I created that helps me find these types of indexes that should be candidates to be removed.  It finds all of the “non-system” indexes that are just simple indexes (not primary keys or unique constraints), which have to be updated at least twice as often they are used.

SELECT OBJECT_NAME(S.object_id, S.database_id) AS 'Table',
  I.Name AS 'IndexName',
  S.user_seeks,
  S.user_scans,
  S.user_lookups,
  S.user_updates,
  S.last_user_seek,
  S.last_user_scan,
  S.last_user_lookup,
  S.last_user_update
FROM sys.dm_db_index_usage_stats S
  INNER JOIN sys.indexes I ON I.object_id = S.object_id AND I.index_id = S.index_id
WHERE S.database_id = DB_ID()
  AND NOT OBJECT_NAME(S.object_id, S.database_id) LIKE 'sys%'
  AND NOT OBJECT_NAME(S.object_id, S.database_id) LIKE 'fulltext%'
  AND NOT I.Name IS NULL
  AND NOT I.is_primary_key = 1
  AND NOT I.is_unique_constraint = 1
  AND S.user_updates > ((S.user_seeks + S.user_scans) * .5)
ORDER BY S.user_updates DESC

You can see in the WHERE clause the “* .5” text.  You can change that to be lower to be more stringent on how bad an index has to be before you consider removing it.

For more info on index usage stats, see these MSDN articles:

Monday, June 01, 2009 3:03:27 PM (Central Standard Time, UTC-06:00)  # 

I listened to a .NET Rocks podcast recently on database design, and although I found most of the content near worthless … they did mention in passing some dynamic management views that are built into SQL Server that I was unaware of.  They turned out to be immensely helpful, so I thought I would share them.

Some of the dynamic management views are designed to help you find “missing indexes.” While SQL Server is processing queries, it keeps track of some usage stats and this view allows you to see a list of indexes that SQL Server thinks would help performance if they were added.  (I think the stats are cumulative since the last time the server was rebooted.)

Here is little T-SQL script I created that finds the top 10 missing indexes with the highest anticipated improvement for user queries.  It is based on an example from SQL Books Online, which is where the scoring algorithm comes from.  The score is simply meant to provide a relative comparison of the “anticipated cumulative improvement” implementing a particular index would have on query performance.

SELECT TOP 10
  GS.avg_total_user_cost * GS.avg_user_impact * (GS.user_seeks + GS.user_scans) AS 'Score',
  OBJECT_NAME(I.object_id) AS 'Table_Name',
  I.equality_columns,
  I.inequality_columns,
  I.included_columns,
  GS.avg_user_impact,
  GS.avg_total_user_cost,
  GS.user_seeks,
  GS.user_scans
FROM sys.dm_db_missing_index_details I
  INNER JOIN sys.dm_db_missing_index_groups G ON G.index_handle = I.index_handle
  INNER JOIN sys.dm_db_missing_index_group_stats GS ON GS.group_handle = G.index_group_handle
WHERE I.database_id = DB_ID()
ORDER BY GS.avg_total_user_cost * GS.avg_user_impact * (GS.user_seeks + GS.user_scans) DESC

The scores are relative, and there doesn’t seem to be a hard rule for what the score should be in order to need to create the index … but I have set my personal threshold at 200.  If the “anticipated cumulative improvement” score is greater than 200, I create the related index … otherwise I don’t. 

Creating too many indexes can hurt the performance of your server just as bad as not having enough indexes.  The podcast actually mentioned a company that wrote a script that would look at this view every night, and simply create all the indexes it suggested.  That turned out to be a horrible idea.  You need to be the ultimate judge of what your “score threshold” is, and whether it would be wise to create a particular index. 

What is neat is that often times when I could look at the suggested indexes, and know exactly what functionality would be boosted by adding a particular index.  Most the time it was just something I had accidentally overlooked, and this view is designed specifically to help you find those things.  It can also help you find ways to optimize indexes for common ad-hoc queries that users might run on the server.  All-in-all this view is a great tool … when used wisely.

For more info, see these MSDN articles:

Monday, June 01, 2009 2:53:18 PM (Central Standard Time, UTC-06:00)  # 

On SQL Server 2008, when I went to create my first Maintenance Plan I got an error message that said:

'Agent XPs' component is turned off as part of the security configuration for this server.  A system administrator can enable the use of 'Agent XPs' by using sp_configure.  For more information about enabling 'Agent XPs', see "Surface Area Configuration" in SQL Server Books Online.

'Agent XPs component is turned off as part of the security configuration for this server

To turn them on, all you have to do is run this script:

USE master
GO
sp_configure 'show advanced options', 1
GO
RECONFIGURE
GO
sp_configure 'Agent XPs', 1
GO
RECONFIGURE
GO

After running the script you should see messages like the ones shown below:

T-SQL to enable Agent XPs using sp_configure

Friday, April 17, 2009 3:26:49 PM (Central Standard Time, UTC-06:00)  # 

I was trying to set up dasBlog on my development laptop, which is a 64-bit machine, and got an error message that said "Could not load file or assembly 'BasicFrame.WebControls.BasicDatePicker' or one of its dependencies. An attempt was made to load a program with an incorrect format."

Could not load file or assembly BasicDatePicker

I searched online and found one blog post that had a suggested solution to "Download the x64 edition from http://www.basicdatepicker.com/ (manual install) and copy (override) the "BasicFrame.WebControls.BasicDatePicker.dll" from that zip file to your "bin" folder in the dasBlog web."  I went to that site and was going to download it, but it turns out they don't allow you to download that dll for free anymore.  Then I remembered IIS could run in a 32-bit compatibility mode, and modified the application pool settings to reflect those shown below.  Jackpot ... everything loaded correctly after that.

Configure IIS Application Pool to run in 32-bit compatibility mode
Sunday, April 05, 2009 4:49:45 PM (Central Standard Time, UTC-06:00)  # 

I recently ran into an interesting issue where I had a .xls file linked in one of my web pages, and changed the content to a .xlsx file ... updated the link and it quit working.  Every time I clicked on it, I was sent to a 404 error ("Page Cannot Be Found").  I double checked the path in the link, that the file actually existed, that it had the correct permissions, etc. and everything seemed correct.

Turns out you have to manually add the MIME Types for the new Office 2007 file extensions (e.g. docx, xlsx) in order for IIS 6 to render them.  I checked IIS 7 and it apparently doesn't have this same problem.  Here is how you add the new types:

  1. Go into IIS Manager, right-click on the Web Sites folder, and select Properties
    IIS Manager Web Sites Properties
  2. Click on the HTTP Headers tab and then click on the MIME Types... button
    Click on HTTML Headers then MIME types
  3. Add each of the MIME Types contained in the table below
    Add each MIME Type

New Office 2007 MIME Types

Extension MIME Type
docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
dotx application/vnd.openxmlformats-officedocument.wordprocessingml.template
xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xltx application/vnd.openxmlformats-officedocument.spreadsheetml.template
pptx application/vnd.openxmlformats-officedocument.presentationml.presentation
potx application/vnd.openxmlformats-officedocument.presentationml.template
ppsx application/vnd.openxmlformats-officedocument.presentationml.slideshow
Friday, March 20, 2009 3:37:08 PM (Central Standard Time, UTC-06:00)  # 

This is based on excerpts from Wikipedia’s “Join (SQL)” article, which can be found at http://en.wikipedia.org/wiki/Join_(SQL). That article contains way more detail than most people really need to know, and also touches on about 20 different types of joins ... where in reality the majority of people just use two: inner and left outer. So I tried to filter the info down to just the basics that you need to know.


A SQL JOIN clause combines records from two tables in a database. It creates a set that can be saved as a table or used as is. A JOIN is a means for combining fields from two tables by using values common to each.

All subsequent explanations on join types in this article make use of the following two tables. The rows in these tables serve to illustrate the effect of different types of joins and join-predicates. In the following tables, Department.DepartmentID is the primary key, while Employee.DepartmentID is a foreign key.

Inner Joins
An inner join requires each record in the two joined tables to have a matching record. An inner join essentially combines the records from two tables (A and B) based on a given join-predicate. The result of the join can be defined as the outcome of first taking the Cartesian product (or cross-join) of all records in the tables (combining every record in table A with every record in table B) - then return all records which satisfy the join predicate.   This type of join occurs most commonly in applications, and represents the default join-type.

Programmers should take special care when joining tables on columns that can contain NULL values, since NULL will never match any other value (or even NULL itself), unless the join condition explicitly uses the IS NULL or IS NOT NULL predicates.

As an example, the following query takes all the records from the Employee table and finds the matching record(s) in the Department table, based on the join predicate. The join predicate compares the values in the DepartmentID column in both tables. If it finds no match (i.e., the department-id of an employee does not match the current department-id from the Department table), then the joined record remains outside the joined table, i.e., outside the (intermediate) result of the join.

SELECT *
FROM   Employee
       INNER JOIN Department ON Employee.DepartmentID = Department.DepartmentID

Notice that the employee "Jasper" and the department "Marketing" does not appear. Neither of these has any matching records in the respective other table: "Jasper" has no associated department and no employee has the department ID 35. Thus, no information on Jasper or on Marketing appears in the joined table. Depending on the desired results, this behavior may be a subtle bug. Outer joins may be used to avoid it.

Outer Joins
An outer join does not require each record in the two joined tables to have a matching record. The joined table retains each record—even if no other matching record exists. Outer joins subdivide further into left outer joins, right outer joins, and full outer joins, depending on which table(s) one retains the rows from (left, right, or both).

Left Outer Joins
The result of a left outer join (or simply left join) for tables A and B always contains all records of the "left" table (A), even if the join-condition does not find any matching record in the "right" table (B). This means that if the ON clause matches 0 (zero) records in B, the join will still return a row in the result—but with NULL in each column from B. This means that a left outer join returns all the values from the left table, plus matched values from the right table (or NULL in case of no matching join predicate).

For example, this allows us to find an employee's department, but still to show the employee even when their department does not exist (contrary to the inner-join example above, where employees in non-existent departments are excluded from the result).

Example of a left outer join, with the additional result row italicized:

SELECT *
FROM   Employee
       LEFT OUTER JOIN Department ON Employee.DepartmentID = Department.DepartmentID

Wednesday, February 18, 2009 8:35:47 AM (Central Standard Time, UTC-06:00)  # 

I have often times wanted to write something like CheckBoxList1.Items.Where(i => i.Selected).  Turns out you can't do that, because the Items property is a "specialized" collection.  But to get it to work all you have to do is cast it to a generic list of ListItems like this:

var selectedItems = CheckBoxList1.Items.Cast<ListItem>().Where(i => i.Selected)

The example above uses method syntax, but you could also use query syntac like this:

var selectedItems = from i in CheckBoxList1.Items.Cast<ListItem>()
                    where i.Selected
                    select i;

Monday, November 17, 2008 1:52:49 PM (Central Standard Time, UTC-06:00)  # 

Here is a code snippet that uses a little reflection to return a string representing all of the various fields and properties contained in the given object, and their related values.  Because it uses reflection it has a higher cost at runtime than typical strongly-typed code ... but there are still some scenarios this type of discovery is useful.

public static string GetObjectDump(Object thisObject)
{
    StringBuilder sb = new StringBuilder();
    Type thisType = thisObject.GetType();

    var fields = thisType.GetFields();
    foreach (var f in fields)
        sb.Append(String.Format("{0} = {1}", f.Name, f.GetValue(thisObject)) + Environment.NewLine);

    var properties = thisType.GetProperties();
    foreach (var p in properties)
        sb.Append(String.Format("{0} = {1}", p.Name, p.GetValue(thisObject, null)) + Environment.NewLine);

    return sb.ToString();
}
Tuesday, October 14, 2008 11:18:48 AM (Central Standard Time, UTC-06:00)  # 

I find myself doing the same series of steps in VS 2008 quite a bit, and decided to give Visual Studio's macros a shot at helping with some of those mundane, mindless tasks.  The "Record Macro" functionality (Ctrl+Shift+R) works pretty well ... except when I tried to work in a project build.  While messing with it I got a few different types of exceptions (e.g. invalid arguments, the COM object called couldn't return), but I eventually found the little documentation there is on the DTE object model, and combined some ideas from examples on blogs and whitepapers to come up with something that works.

You can leverage the DTE.Solution.SolutionBuild a couple different ways.  You can build a single project (like in the example below) or the whole solution using DTE.Solution.SolutionBuild.Build(True).  You can also do other things like clean the solution with DTE.Solution.SolutionBuild.Clean(True).  Here is a link to the full reference of the SolutionBuild members.

In the example below I explicitly set the build configuration to "Debug", but you could also leverage DTE.Solution.SolutionBuild.ActiveConfiguration.Name to build it in whatever configuration is currently selected in the IDE.

...
DTE.Solution.SolutionBuild.BuildProject("Debug", "C:\inetpub\wwwroot\ProjectFileName.csproj", True)

' Check to see if there build errors, and if there were alert the user ... otherwise continue
' NOTE: LastBuildInfo returns an integer representing the number of projects that failed during
' the last build.
If DTE.Solution.SolutionBuild.LastBuildInfo > 0 Then ' There were build errors ... alert the user by popping up the "Output" window DTE.Windows.Item(Constants.vsWindowKindOutput).Activate() Else ' Do more stuff here is needed End If ...
Tuesday, October 07, 2008 9:29:29 AM (Central Standard Time, UTC-06:00)  # 

This is probably the first of many topics I will post about jQuery.  Since I found myself writing more and more JavaScript these days (especially for DOM manipulation resulting from AJAX calls), I am going to try to start leveraging the jQuery library. 

What is jQuery?  As the creator of jQuery, John Resig, says in the book jQuery in Action "it's all about simplicity.  Why should web developers be forced to write complete, book-length pieces of code when they want to create simple pieces of interaction? ... When I first set out to create jQuery I decided that I wanted an emphasis on small, simple code that served all the practical applications that web developers deal with day to day."  As Scott Hanselman said in one of his recent podcasts about jQuery, it's syntax is "very terse."  That is a simple way of saying it is neat, compact, elegant, and devoid of needless, verbose constructs.  It is also another tool that helps abstract away the browser differences that typically leads developers to writing "if(IE)" type code.

jQuery offers a lot of benefits, and I will probably cover some more in future posts ... but one of the fundamental features is a new type of event you can attach to.  It is the $(document).ready event, and can be used in places where developers might have previously used something like window.onload.  Through painful experiences I have learned that the window.onload event doesn't always fire when you expect that it would.  The onload event doesn't fire until all binary content has been downloaded, which includes images and any other type of content.  On the other hand, the ready event provided by jQuery fires as soon as the DOM is ready to be traversed and manipulated ... which in extreme instances might be seconds before the onload event.

Here is a little blurb from the jQuery documentation about the ready event:

This is probably the most important function included in the event module, as it can greatly improve the response times of your web applications.

In a nutshell, this is a solid replacement for using window.onload, and attaching a function to that. By using this method, your bound function will be called the instant the DOM is ready to be read and manipulated, which is when what 99.99% of all JavaScript code needs to run.

... Please ensure you have no code in your <body> onload event handler, otherwise $(document).ready() may not fire. ... You can have as many $(document).ready events on your page as you like. The functions are then executed in the order they were added.

Here are a couple simple example of how you can use it:

$(document).ready(function()
{
    // Your code here
});

/*************************/

function DoSomething()
{
    // Your code here
}
$(document).ready(DoSomething);

/*************************/

// This example shows how something that was previously tied to the
//  window.onload event would look if tied to the document.ready event.

window.onload = function() { alert("window.onload event fired!"); };
$(document).ready(function() { alert("document.ready event fired!"); })

Thursday, October 02, 2008 3:09:55 PM (Central Standard Time, UTC-06:00)  # 

Here are some HTML character codes that I find useful, and feel like I constantly lookup:

& &amp;
  &nbsp;
© &copy;
® &reg;
&trade;
¢ &cent;
× &times;
÷ &divide;
&minus;
± &plusmn;
&sim;
&cong;
&asymp;
&ne;
&equiv;
< &lt;
> &gt;
&le;
&ge;
« &laquo;
» &raquo;
" &quot;
&prime;
&Prime;
&lsquo;
&rsquo;
&sbquo;
&ldquo;
&rdquo;
&bdquo;
&dagger;
&Dagger;
° &deg;
· &middot;
&bull;
&hellip;
&oline;
&ndash;
&mdash;
&larr;
&uarr;
&rarr;
&darr;
&harr;
&crarr;
&lArr;
&uArr;
&rArr;
&dArr;
&hArr;
¹ &sup1;
² &sup2;
³ &sup3;
ˆ &circ;
¼ &frac14;
½ &frac12;
¾ &frac34;
&frasl;

For a more complete list visit http://www.kadifeli.com/fedon/char.htm.

Thursday, August 07, 2008 8:27:02 AM (Central Standard Time, UTC-06:00)  # 

Here are a few regular expressions that I have found useful. A few of these the ones that are "built-in" to Visual Studio 2008. I have seen some other versions of all of these on the internet, but although there may be some extreme edge cases that they don't take into account ... at least they are simple enough to read and understand, and they cover 99.99% of cases.  People don't seem to take simplicity into consideration when designing these things.  Here is a good quote that I think is especially true for regular expressions:

"There are two ways of constructing a software design: one way is to make it so simple that there are obviously no deficiencies, and the other is to make it so complicated that there are no obvious deficiencies." - C.A.R. Hoare

Type Description

Regular Expression

Email Address

\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*

URL

http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?

US Phone Number

((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}

US Zip Code

\d{5}(-\d{4})?

Max Length (Ex: 200)

.{1,200}

U.S. Date (MM/dd/yyyy)

\d{1,2}/\d{1,2}/\d{2,4}

2 Whole Numbers & Up To 3 Decimals

\d{1,2}(\.?|(\.\d{1,3})?)
Tuesday, August 05, 2008 12:58:49 PM (Central Standard Time, UTC-06:00)  # 

I had a scenario where I used Web Parts, and had some content in two of them that I wanted to wrap in update panels.  After I made the changes I got the JavaScript error shown below whenver one of the update panels would try to refresh.  It seemed like the two update panels were conflicting with each other.

Sys.InvalidOperationException: Could not find UpdatePanel with ID 'XXXX'.  If it is being updated dynamically then it must be inside another UpdatePanel.

Sys.InvalidOperationException: Could not find UpdatePanel with ID 'XXXX'.  If it is being updated dynamically then it must be inside another UpdatePanel.

I couldn't figure out what was causing this for a couple days, and I finally ran across a blog post by Ben Rush that helped me out.  All I needed to do was set the UpdateMode to "Conditional", and then explicitly declare what should trigger each of update panels.  Here is an example that shows how to set this up:

<asp:UpdatePanel ID="upMLSListings" UpdateMode="Conditional" runat="server">
    <Triggers>
        <asp:AsyncPostBackTrigger ControlID="ddlCity" EventName="SelectedIndexChanged" />
    </Triggers>
    <ContentTemplate>
        <asp:DropDownList ID="ddlCity" runat="server" AutoPostBack="True" OnSelectedIndexChanged="ddlCity_SelectedIndexChanged" />
        ...
    </ContentTemplate>
</asp:UpdatePanel>

Wednesday, July 23, 2008 6:55:56 AM (Central Standard Time, UTC-06:00)  # 

It was a little frustrating for me to get Dundas Charts for ASP.NET up and running in IIS 7 (on my Vista development machine).  After a couple hours of trying to dumb it down to the simplest possible chart example, I still saw got a broken image link instead of a chart.  I found my way to the Dundas online support, and figured out what was going on ... and hope to save someone from the same frustrations I went through.

First, this is only applicable if you are running the site's application pool's pipeline in "Integrated" mode.  IIS 6 had no concept of "Integrated" mode, and instead just operated in what is now known as "Classic" mode.  In the Dundas support article, they simply suggest you switch your application pool to "Classic" and everything will just work.  But if you do that, you will miss out on all of the benefits that come with the new "Integrated" mode.  You can read more about what those are in this article: How to Take Advantage of the IIS 7.0 Integrated Pipeline.

If you want to keep your application pool in "Integrated" mode, there is one trick that isn't really published anywhere apparent.  That is adding a single line to the <handlers> section under <system.webServer> in the web.config.  When you drop a Dunda chart on a page, it automatically puts in some stuff in the web.config ... but here is a full list of everything that should be in there to get it up and running in your app:

<system.web>
   <appSettings>
     <add key="ChartHttpHandler" value="Storage=memory;Timeout=180;Url=~/temp/;" />
   </appSettings>
   <pages>
      <controls>
        <add tagPrefix="dcwc" namespace="Dundas.Charting.WebControl" assembly="DundasWebChart" />
      </controls>
   </pages>
   <httpHandlers>
      <add path="ChartAxd.axd" verb="*" type="Dundas.Charting.WebControl.ChartHttpHandler" validate="false" />
   </httpHandlers>
</system.web>
<system.webServer>
   <handlers>
      <add name="ChartAxd.axd" path="ChartAxd.axd" verb="*" preCondition="integratedMode" type="Dundas.Charting.WebControl.ChartHttpHandler" resourceType="Unspecified" />

   </handlers>
</system.webServer>

Tuesday, July 22, 2008 10:04:38 AM (Central Standard Time, UTC-06:00)  # 

If you have ever used Visual SourceSafe, you know it allows seemless integration with Visual Studio ... but has its own laundry list of issues and complexity associated with it.  It seems that the fundamental, daily tasks are sometimes intuitive, but move past those concepts and nothing is straight-forward.  One of those topics is branching a solution or project in SourceSafe, which I will cover here.  Most of the info in this post is based on stuff I read in Visual SourceSafe 2005: Software Configuration Management in Practice.

Two Methods To Branch Code
There are actually two common methods to branch code in SourceSafe: "Share, Pin, & Branch" and "Share & Branch."  Here is a quote from the aforementioned book that describes the differences:

Using the Share, Pin & Branch method can save database space because new files aren't created until you branch them.  Also, it can be helpful in checking which files have been modified and which files are unchanged since the configuration was created.

This method has the disadvantage of having to manually branch the files in Visual SourceSafe Explorer as needed.  Another disadvantage is that if you accidentally unpin some files instead of branching them, they will become shared with the files in the mainline where development is being conducted.  This can affect both the mainline and the development line.

The alternative is to use the Share and Branch method and branch all files from the start, when creating a new maintenance codeline. (p274)

To me, it seems like the only time you would consider using Share, Pin & Branch would be scenarios when the you had a project with an enormous amount of files, and you had limited disk space.  Share & Branch would probably be the recommended method for 90% of software projects out there, so that is what I will discuss here.

Branching the Maintenance Line on Creation

  1. Before proceeding you should make a backup of the SourceSafe database.  To learn how to do that see this blog post.
  2. In Visual SourceSafe Explorer, right-click on the folder containing the main line's files and choose "Show History".


  3. In the history window, click "Share"


  4. The new branches must be created outside the solution's hierarchy top folder.  In this example, my solution file is in the $/StuffThatJustWorks.root/StuffThatJustWorks folder.  By default Visual SourceSafe will try to structure your project hierarchically like this (with a ".root" folder), but you can override that behavior.  If you do have it set up this way, just select the ".root" folder.  Otherwise you will have to create new folder in SourceSafe to hold the brached code.  Also, be sure to check "Branch after share."


  5. In the "Share" dialog window, enter the name for the new project, select "Recursive", and enter some optional comments.

After you click "OK" on that last window, the new codeline will be created.  The screenshot below shows how the newly created branch looks in this example.  The "Release 1" project would be the maintenance codeline, and the original project would be my main codeline.  I can now start implementing new features in the main codeline, but if there are bugs found in the "Release 1" code I can easily fix those in that codeline and release an updated version (e.g. Release 1.1) that doesn't contain the new, possibly unfinished features of the main codeline.

Sunday, July 20, 2008 12:51:36 PM (Central Standard Time, UTC-06:00)  # 

Turning on the compression feature in IIS (Internet Information Services) will compress the server's responses, and therefore improve perceived performance while reducing bandwidth-related charges.  Sounds like a good thing, right?  I believe this feature wasn't enabled by default in IIS 6, but it looks like it is at least enabled by default in IIS 7 for static files.  I will walk through how you would set this up in both versions of IIS.


IIS 6
In IIS 6, you can only enable compression for the web server as a whole ... not individual sites.  To enable compression, open IIS 6, and then right-click on the "Web Sites" folder and choose "Properties."

IIS 6 Web Sites Properties

Then find the "Services" tab, and from there you can easily set a few simple options to enable native compression in IIS.

Configure Http compression settings in IIS 6

There is also gzip compression build into IIS that enables you to compress ASPX pages, but the team didn't make it very easy to find.  In fact, I am pretty sure there is no way to enable it in the GUI.  But I followed the instruction provided at http://support.microsoft.com/kb/322603, and even though the instructions on that post say "To enable IIS 5.0 to compress .aspx pages, follow these steps", I have personally tried it on two web servers running IIS 6, and monitored the responses they were sending before and after using the amazing YSlow plugin for Firebug, and sure enough it showed that before I ran the commands the ASPX/HTML content wasn't gzipped, but after it was.  This brought the page I was monitoring from a 23K download on the client to 8K.  Here are the instructions I followed:

  1. Open a command prompt.
  2. Type net stop iisadmin, and then press ENTER.
  3. Type cd C:\InetPub\adminscripts, and then press ENTER.
  4. Type the following, and then press ENTER:
    CSCRIPT.EXE ADSUTIL.VBS SET W3Svc/Filters/Compression/GZIP/HcScriptFileExtensions "asp" "dll" "exe" "aspx"
  5. Type the following, and then press ENTER:
    CSCRIPT.EXE ADSUTIL.VBS SET W3Svc/Filters/Compression/DEFLATE/HcScriptFileExtensions "asp" "dll" "exe" "aspx"
  6. Type net start w3svc, and then press ENTER.


IIS 7
In IIS 7 you can specify compression settings for all web sites, or individual sites.  I will show how to do it for all web sites, although it is straight-forward to extend these same steps to be site-specific.  First open IIS 7, and right in the default view you can see an icon for the "Compression" feature.

IIS 7 Compression Feature

Click on that feature, and you will see the screen like the one shown below.  There are a few different options than those available in IIS 6.  These were the default settings on my machine.

Configure IIS 7 compression settings

Notice that there is an alert that says "The dynamic content compression module is not installed," and the checkbox for that feature is disabled.  That module wasn't installed by default on my machine, but you can easily add it by going to Control Panel > Programs and Features > Turn Windows features on or off, and then selecting the item shown below.  The next time you open up IIS that checkbox will be enabled for you to select, and you won't see that alert message any longer.

Enable dynamic content compression in IIS 7
Saturday, July 19, 2008 5:16:58 PM (Central Standard Time, UTC-06:00)  # 

I have had some trouble before trying to configure IIS 7.0 so that I could run multiple sites at one time on my development machine.  So I just wanted to share some quick notes on how I got it working (mainly so I could refer back to them later).

  1. Open IIS, and add each site.  The binding for each site should including a unique host name that will be used to access the site (e.g. localSTJW).  The rest of the default settings are fine.
    Edit Site Bindings

    Configured sites in IIS 7

  2. Edit the "hosts" file, and add the appropriate host name of each site and associate it with 127.0.0.1.  The file should be located at "C:\Windows\System32\drivers\etc".
    Edit hosts file to add site name

You should then be able to navigate to any of those sites by just entering the host name in the browser (e.g. http://localSTJW).

Wednesday, July 16, 2008 9:42:00 AM (Central Standard Time, UTC-06:00)  # 

I have found a use for this a couple different times.  It is a function that is very similar to the getElementsByTagName that is built-in in JavaScript.  It is pretty quick (due to the use of regular expressions), but should be used sparingly ... especially if there are a lot of elements on the page that it would have to spin through, because there could be some latency there.

function getElementsByClassName(className, parentNode)
{
    // If a parentNode wasn't explicitly passed in ... search the
    // entire "body" element by default.
    if(!parentNode)
        parentNode = document.getElementsByTagName("body")[0];
    
    var results = [];
    var regEx = new RegExp('\\b' + className + '\\b');
    var allElements = parentNode.getElementsByTagName("*");
    
    // Go through every element, testing each against the regular expression,
    // and push matches into the results array.
    for(var i = 0; i < allElements.length; i++)
    {
        if(regEx.test(allElements[i].className))
            results.push(allElements[i]);
    }
    
    return results;
}
There is another good example of this type of function I ran across that is referred to as "The Ultimate getElementsByClassName" (by the author at least) ... but it looked fairly complex.  I went with this one because it is short and simple, and I know it will give me back what I am looking for everytime.
Tuesday, June 24, 2008 7:12:42 AM (Central Standard Time, UTC-06:00)  # 

I find myself needing to do this from time to time, and I can never remember how you are supposed to do it.  It isn't hard ... but I think I always get confused because there was a different naming convention back before .NET 2.0.  However, this example shows how it should would in 2.0, 3.0, or 3.5.  The main reason I have to use this is if I have a user control inside a repeater or some other type of databound list-type control, and I need to access it in a method in the code behind (e.g. ItemDataBound).  Here is a very simple example showing a scenario I might need to do this, and how you can figure it out:

Front-End

...
<%@ Register Src="~/Controls/Calendar/AgendaViewDay.ascx" TagName="AgendaViewDay" TagPrefix="uc" %>
...
<asp:Repeater ID="rptEvents" runat="server" OnItemDataBound="rptEvents_ItemDataBound">
    <ItemTemplate>
        <uc:AgendaViewDay ID="AgendaViewDay1" runat="server" />
    </ItemTemplate>
</asp:Repeater>

Back-End

protected void rptEvents_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
    {
        // Bind the events to the day control
        Controls_Calendar_AgendaViewDay thisDayControl = 
            
(Controls_Calendar_AgendaViewDay)e.Item.FindControl("AgendaViewDay1"); thisDayControl.BindEvents(eventsToDisplay); } }

As you can see, in this scenario you have to explicity call out the data type of the user control so the compiler knows it has a "BindEvents" method.  The type is "Controls_Calendar_AgendaViewDay", which is a pretty obvious naming convention.  Here is the path I used when registering the user control on the page: "~/Controls/Calendar/AgendaViewDay.ascx".  By default, Visual Studio will name a page or user control's class based on it's original location in the folder structure with this type of naming convention (pretty much just replacing "/" with "_").  But beware, when you rename a file or a containing folder Visual Studio won't go back and update all of the class names for you, because that could possibly break some of your references.  So, one sure-fire way to determine the class name is to navigate to the user control's ascx code-behind file and look at the class definition, which will be something like this:

...
public partial class Controls_Calendar_AgendaViewDay : System.Web.UI.UserControl
{
...
Thursday, June 12, 2008 6:41:17 PM (Central Standard Time, UTC-06:00)  # 

Ever noticed that setting the MaxLength for a MultiLine TextBox does absolutely nothing? According to documentation on MSDN "this property is only applicable when the TextMode property is set to TextBoxMode.SingleLine or TextBoxMode.Password." You can just put a RegularExpressionValidator on the textbox that makes sure the length is under a certain threshold, but that isn't always the most user-friendly way.  On single line textboxes, the browser simply doesn't allow the user to insert any more letters once they have reached the max length ... which is pretty intuitive for users to understand "oh, I need to shorten this down a little."  Here is some JavaScript that will create that same type of effect on MultiLine textboxes that users are familiar with on single line textboxes.

Here is the JavaScript function:

function MaxLength(field, maxLength)
{
    if (field.value.length >= maxLength)
        field.value = field.value.substring(0, maxLength - 1);
}

Then you can add an "onKeyDown" event to the TextBox in the aspx file like this:

<asp:TextBox ID="txtMultiLine" TextMode="MultiLine" onKeyDown="MaxLength(this,200);" runat="server" />

... or add it programmatically in the CodeBehind like this:

txtMultiLine.Attributes.Add("onKeyDown", "MaxLength(this,200);");

Note: If it is critical that a user not be able to submit a form with more than the maxLength number of characters in the multiline textbox, then you also need to add a RegularExpressionValidator to double-check the length ... just in case the user has disabled JavaScript on their browser.  This will obviously only work if they have JavaScript enabled, and is more designed to make this a more pleseant and familiar expreience for the end-user.  Here is an example of the RegularExpressionValidator you might drop on the page as well:

<asp:RegularExpressionValidator ID="revMultiLine" ControlToValidate="txtMultiLine" ValidationExpression="(.|\n){1,200}" ErrorMessage="Please enter 200 characters or less" runat="server" />

Friday, May 23, 2008 2:38:51 PM (Central Standard Time, UTC-06:00)  # 

I was trying to figure out how to make the rows in a grid alternate background colors in a SQL Server Reporting Services (SSRS) report using Report Builder/Designer in Visual Studio (like the example shown below).  I searched the internet for a little while and pretty much just found people who said Report Designer was a very simple tool and didn't allow you to do "advanced customizations" like this ... bull crap.  It is a simple tool (with a few quirks) ... but it is pretty extendible too.  There are a ton of places where it allows you to use "Expressions", which are extremely powerful.  Thats what I used to make this alternating background thing work.

Alternate backgrounds in SSRS report

First select the table row you would like to apply this type of alternative style to in the designer.  Then in the properties window for that TableRow object you should see a "BackgroundColor" property ... click the drop down and choose "<Expression...>":

Set TableRow BackgroundColor property to alternate color in report

Then, all you have to do is write an expression like this and replace the colors with the ones that you want.  The expression simply evaluates the current row number mod 2, which will be one for row numbers that are odd (e.g. 1,3,5) and zero for row numbers that are even (e.g. 0,2,4).  It compares the result for the current row and returns the appropriate background color ... simple stuff:

Write custom expression to set row background color

Wednesday, May 07, 2008 1:41:41 PM (Central Standard Time, UTC-06:00)  # 

I have our production web servers set up to email me notifications when unhandled exceptions occur, and if a site is publicly accessible crawlers, spiders, and other types of search bots can make this a pain.  Most crawlers try to go through pages they have in their index, and pull new content.  If you have promo-type pages that are only up for a limited amount of time, they will try to access that URL later on after it is gone, and bam ... another email in my box.

There are CAPTCHA components out there, but they aren't really appropriate for this scenario ... I don't want site users to have to read some squiggly letters and type those out before the site sends me an error email.  So I have written some pretty simple code that helps me filter out if the request came from a crawler.  My 10 second Goggle on the subject didn't turn up much, so maybe this will help someone else out there.  If you figure out a way to improve it, please post some comments or contact me.

The solution is two part.  I have a regular expression pattern stored in the web.config, and then a single "IsBot" method that returns true if the current request is from a crawler and false otherwise.  I store the pattern in the web.config because it is still evolving, and probably far from "all encompassing" ... but when an error email slips through that is from a bot, I will look at the agent it presents itself as and then add a new keyword to the pattern to detect that crawler in the future.  If I was to guess, I would say the pattern posted here probably detects over 90% of hits from crawlers ... as of today.

Current pattern in web.config's AppSetting setting section:

<add key="botRegex" value="bot|crawler|spider|slurp|ask|teoma" />

C# IsBot method:

/// <summary>
/// Returns true if the current request is from a bot crawling the site, and false otherwise.
/// </summary>
public static bool IsBot
{
    get
    {
        // If this method can't access the current context that means the executing thread doesn't have access
        // to the current request's properties ... since we can't pull any agent information we have to assume
        // this is not a bot.
        if(HttpContext.Current == null)
            return false;
        
        string HTTP_USER_AGENT = "";
        if (HttpContext.Current.Request.ServerVariables["HTTP_USER_AGENT"] != null)
            HTTP_USER_AGENT = HttpContext.Current.Request.ServerVariables["HTTP_USER_AGENT"].ToLower();

        // Check to see if the user agent field contains any of the terms in the botRegex set in the web.config
        string expression = ConfigurationManager.AppSettings["botRegex"];
        Regex botRegex = new Regex(expression);
        return botRegex.IsMatch(HTTP_USER_AGENT);
    }
}
Tuesday, April 29, 2008 2:45:22 PM (Central Standard Time, UTC-06:00)  # 
...
using System.Globalization;
using System.Threading;
...

string thisPhrase = "HELLO WORLD!";
lblOutput.Text = Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(thisPhrase.ToLower());

That's it ... this example would convert the string to "Hello World!"  Pretty easy, huh?

Thursday, April 24, 2008 12:04:48 PM (Central Standard Time, UTC-06:00)  # 

A lot of times I have user controls or even entire pages that are customized for the authenticated user ... but the output rendered by that user control or page doesn't change that often.  It makes sense to cache this information, but I still want it to be personalized for each user.  For example, if you have a menu on every page of a site that only contains links relevant for the user that is signed in ... you can actually use the OutputCache directive to allow ASP.NET to cache the output of that control or page for a particular duration for that user, instead of having to recreate the menu on every page load.  This can save a significant amount of processor cycles and memory, and make your site more scalable.  Here is how you do it:

  1. Add this directive to top of your user control (.ascx) or page (.aspx) file:

    <%@ OutputCache Duration="60" VaryByParam="none" VaryByCustom="UserID" Shared="true" %>

    Duration: The amount of time in seconds to cache the content
    VaryByParam: Indicates whether the content should vary based on the QueryString
    VaryByCustom: The custom value used to determine if the content is already in cache
    Shared: Whether multiple pages should be able to access the cached content (default is false)

  2. Then create a new method in the Global.asax file that defines how to find the value for the custom parameter:

    public
    override string GetVaryByCustomString(HttpContext context, string arg)
    {
        if (arg == "UserID")
            return context.User.Identity.Name;
        
        return string.Empty;
    }

The GetVaryByCustomString method is called very early in the life-cycle of a request ... even before the SessionState has been populated, so you can't use a custom value from the SessionState to vary the content.  At least that is 99% true.  If you are using the OutputCache inside a user control, the SessionState is actually available when the method is called.  So if you ran into this problem on a particular page, one (not very elegant) solution would be to move all the content into a user control and then OutputCache that control so you can access the SessionState value in the GetVaryByCustomString method.

Another use for customized OutputCache is on things like product details pages where the ProductID is in the QueryString.  Here is what the URL for a page like this might look like: Details.aspx?ProductID=34.  In this scenario you can OutputCache like this:

<%@ OutputCache Duration="7200" VaryByParam="ProductID" %>

OutputCache is the most effecient way to utilize caching in ASP.NET.  I also use the HttpRuntime.Cache API frequently to cache data, but when most consultants come on-board to help make a site more scalable ... they start with OutputCache.  For more info, check out the OutputCache documentation, or this article on Caching Portions of an ASP.NET Page.

Tuesday, April 22, 2008 2:06:58 PM (Central Standard Time, UTC-06:00)  # 

The new Report Designer in SQL Server 2008 is actually a combination these two products from SQL Server 2005:

  • Report Builder - A stand-alone application distributed through SQL Reporting Services that is pretty high-level and designed for end-user ad-hoc reporting.  The files it generated were the standard .rdl files ... however, there were many limitations this software like the ability to only have one report region (couldn't make a single report that contained a few different types of information or queries) and you could only pull from one data source (typically a single report model).
  • Report Designer - This was a tool that developers and other IT pros could use to design more specialized or complicated reports inside Visual Studio.  The interface was less than intuitive, but it didn't have those limitations found in Report Builder.  It also saved files in .rdl format, but if you created a report with Report Designer ... Report Builder would not be able to edit that report.  However, if the report was created in Report Builder, you could always open it with Report Designer and modify it there.

So now there is just one product ... SQL Server 2008 Report Designer.  It looks more like the stand-alone Report Builder, but has the Office 2007 look and feel including the ribbon control.  This tool is supposed to be a common platform that is easy enough to use so end-users (e.g. management, executives) could create ad-hoc reporting, but also contain all of the functionality needed by IT pros to build more complicated reports as well.  Seems like a tall bill to fill, but it isn't improbable in theory ... so maybe they got it right.

The link below is a very recent "web exclusive" article from SQL Server Magazine that gives fairly in-depth preview on the product, and some examples and screenshots.  Check it out:

http://www.sqlmag.com/Articles/Index.cfm?ArticleID=98830

Saturday, April 19, 2008 9:42:19 AM (Central Standard Time, UTC-06:00)  # 

There has always been a disconnect for storing unstructured data (e.g. documents, media files) in relational databases.  These were big blobs of binary data that couldn't be organized appropriately inside a database (or at least in a way that could be considered optimal).  So, most the time we stored those items in a file system outside of the database, and then simply stored pointers to those files inside the database.  Although this solution works, it is a bit awkward ... because the database isn't self-contained.  Since it refers to those other files, they could be thought of as "part of the data" in the database ... but those files are outside of the control of the database.  When an administer makes a backup of the database ... that isn't really all the data.  Also, if you move the file to another location ... you have to remember to update all of the paths in the database that point to that file.  SQL Server 2008 makes an attempt to bridge this disconnect with a new FILESTREAM data type. 

With this data type, files can still be stored outside of the database, but the data is considered part of the database for transactional consistency. This allows for the use of common file operations while still maintaining the performance and security benefits of the database." - TechNet Magazine - April 2008

You can pass the binary data to the SQL Server, and insert it similar to how you could already do for the existing VARBINARY or IMAGE data types.  However, instead of saving the bits with the rest of the structured, relational data ... SQL pretty much does the same thing we were doing before (i.e. saving the data somewhere on the file system and storing a pointer to the file), but completely takes care of it for us.  It saves all of the binary data in file containers in a special, hidden directory.  It also goes one step further ... only the account under which the SQL Server service account runs is granted NTFS permissions to the FILESTREAM container.

MSDN suggests that you use the FILESTREAM data type when you need fast read access on files larger than 1MB.  If the file is smaller than that size, you will probably get better performance just using VARBINARY data types which store the data in-line with the structured, relational data.  But, I can already see how this new data type is just one of the new features in SQL Server 2008 that will make my life easier.  I can already see how it could used for content management systems.  Such systems often save a lot of unstructured binary data like documents, images, videos, etc. that can be in excess of 1MB ... so the FILESTREAM data type seems like an obvious fit. 

For more info on this new data type, check out this documentation from SQL Books Online.

Friday, April 18, 2008 9:14:57 PM (Central Standard Time, UTC-06:00)  # 

We did some testing with replication a couple months ago to see if we could make use of it on one of our projects.  Some other complications caused us to choose a different path, so we tried to remove replication from the database through SQL Management Studio's UI.  It seemed to clear out some stuff ... but still left hundreds of system views and procedures related replication.  I wasn't sure how to remove them without risking some instability in our sytem, and today I learned just how easy this is ...

EXEC sp_removedbreplication 'databaseName'

Note: This is intended for SQL Server 2005.  Here is a link to the SQL Books Online reference to this procedure.  Microsoft does have a disclaimer that says "This procedure should be used only if other methods of removing replication objects have failed. For more information about these methods, see Removing Replication."

Monday, April 07, 2008 1:59:16 PM (Central Standard Time, UTC-06:00)  # 

Most versions of Vista come with IIS, which allows a computer to host a web site.  However, unlike Microsoft server operating systems the version of IIS that runs on Vista has been limited.  But this isn’t new ... the version of IIS that ran on XP Pro also had limitations, such as only allowing one web site to be hosted and no more than 10 concurrent connections at any one time.  But it looks like Microsoft wanted to throw a little more confusion in the mix with Vista by setting varying limitations on IIS depending on which edition of Vista you are running.  I had original read a couple articles on what those would be before Vista’s RTM, and have tried to remember what they those limitations were when I was trying to decide which version of Vista to install on a particular machine.  Every time I looked for this info on the net it seemed like it was harder to find than it should have been … so I wanted to write this post to help shed some light on the subject.

  Concurrent Connections Limit Number Of Sites Simultaneous Request Processing Limit FTP Advanced Security Features
XP Pro (with IIS 6) 10 1 Unlimited Yes Yes
Vista Starter Not Available
Vista Home Basic
Vista Home Premium Unlimited Unlimited 3 No No
Vista Business Unlimited Unlimited 10 Yes Yes
Vista Enterprise Unlimited Unlimited 10 Yes Yes
Vista Ultimate Unlimited Unlimited 10 Yes Yes

Old Limitations Lifted
One really great feature of IIS 7 on Windows Vista has over IIS 6 on XP Pro, is that you can now have an unlimited number of sites on running on a machine (previous versions of IIS on Windows Client only allowed 1 site).  Also the limitation on the number of clients that can be actively “connected” to the server at any one time has also been removed.  This is discussed more in-depth later in the post.

New IIS Limitations in Vista
Wouldn’t it be nice if Vista ran the same full-blown version of IIS that runs on Windows Server 2008?  Unfortunately this isn’t the case, probably because Microsoft wants to give people hosting medium to large web sites an incentive to purchase their server operating systems … instead of trying make a Vista box their “production web server.”  While there are is no longer a limit on the number of connections or sites, there are some limitations present on IIS 7 on Vista machines.

Request Processing Limit
IIS has a new request processing limit, which means IIS will only process a particular number of requests at any one time and subsequent incoming requests will be put on hold in a process queue.  This is a limitation that is in place no matter which edition of Vista you are running.  However, the maximum number of requests that will be processed actually varies by SKU.  Ultimate, Enterprise, and Business editions will all handle up to 10 requests at a time, while Home Premium will only handle 3 requests at a time.

This may sound similar to the “connection limit” that was in place on IIS 6 running on XP Pro, in fact many places on the internet including some Microsoft employee blogs and even some stuff on the IIS site use the terms interchangeably.  However, I think I have figured out what the difference is and would like to clarify it here.  IIS 6 running on XP Pro had a connection limit of 10, which meant at any one time no more than 10 computers could have active sessions with the web server.  Each of those computers could be submitting multiple requests to the server at one time, but that didn’t matter.  The only thing that mattered was that no more than 10 connections exist at one time.  If there were 9 connections and another computer made a request, then IIS would respond to the request.  However, if there were already 10 connections and there was another request from a computer other than those 10, IIS would simply ignore that new computer and not even respond to its request.

IIS 7 running on Windows Vista doesn’t have a connection limit like that.  There can be 1,000 people connected to the server at one time.  However, Vista will only process a certain number of individual requests from those clients at time.  For example, if 15 people were requesting various content (e.g. images, static html, active server pages) … the server wouldn’t process those requests all at once.  It would process the first 10, and place the other 5 in a process queue.  When it responded to one of the 10 it was working on, it would then pull one of the waiting requests from the queue and start working on it.  So anytime IIS is processing the maximum number of requests (either 3 or 10 depending on version), any subsequent requests will be stuck in limbo until it is their turn.  Although this request processing limit might seem more restrictive than the connection limit on XP … at least in this scenario the client will eventually get a response, even if it took a while to get it.  IIS on Vista isn’t supposed to be a production environment.  If there are so machine requests that come into IIS that this lag becomes an issue … you should either pay to have your site hosted by a 3rd party or move it to a Windows Server 2008 machine.

Additional IIS Limitations on Home Premium Edition
The version of IIS on Vista Home Premium Edition is intended “to support the needs of the casual or hobbyist web developers,” and doesn’t include these features:

  • FTP Functionality
  • Windows Authentication
  • Digest Authentication
  • Client Certificate Mapping Authentication
  • IIS Certificate Mapping Authentication
  • Remote Administration
  • ODBC logging

IIS on Vista Starter & Home Basic?
If you look closely at Vista Starter or Home Basic you will notice that IIS 7 is listed as one of the available Windows features that can be installed.  However, none of the FTP or “web hosting” functionality (relating to static content, Classic ASP, or ASP.NET) is available.  The IIS 7 components available on these editions only serve as supporting infrastructure for Microsoft's Windows Communication Foundation (WCF).

For more info, here is a good article that talks about some of this stuff: http://www.iis.net/articles/view.aspx/IIS7/Deploy-an-IIS7-Server/Installing-IIS7/IIS7-Features-and-Windows-Vista-Editions

Sunday, February 24, 2008 9:02:06 PM (Central Standard Time, UTC-06:00)  # 

I had a particular console application that I used to edit in Visual Studio 2005, which targeted the .NET Framework 2.0.  I recently “converted” and updated that application so that I could edit it using Visual Studio 2008, and also changed it to target the .NET Framework 3.5 so that I could use some of the new features and tools in the framework.  The conversion wizard worked as expected, said it was successful, and then I changed the build to target .NET Framework 3.5.  After that  I thought this was all too easy … and I was right, because I went to try to build the solution and got this compile-time error: Required file 'alink.dll with IAlink3' could not be found.

Required file alink.dll with IAlink3 could not be found

I googled this error message and anything else I could think of for hours, and found about ten different suggestions for how to fix it … none of which worked.  Most of them had to do with finding some obscure Windows Update that would fix whatever was ailing you.  After wasting my time on those jokers, I finally found a post on Channel 9’s forum with a solution that worked.

It turns out there is a new project setting in VS 2008 that allows you to embed a manifest into the application, which determines some specific application settings.  By default the C# compiler was trying to embed a Vista manifest, which is definitely not the desired behavior in my scenario.  All I had to do was right click on the project that contained the error, and open the “Property Pages.”  When you open that it should look like the screenshot shown below.  By default the “Manifest” drop down was set to “Embed manifest with default settings” (which was trying to embed the Vista  stuff) … but all I had to do was change it to “Create application without a manifest” as shown, and the build succeeds and application behaves as expected.  Hopefully you didn’t waste too much time on this before you came across this post.

Create application without a manifest
Monday, February 04, 2008 8:25:29 PM (Central Standard Time, UTC-06:00)  # 
Visual Studio Recent Projects

I use the links listed in "Recent Projects" in Visual Studio constantly.  In fact, I use it so much that I lengthened how many items were displayed, which can be done in Tools > Options > Environment > General.  I think the default is 10.  But sometimes there are items in there that I don't really need shortcuts to, or worse they are not the shortcuts that should be used to open a project (for one reason or another).  It's pretty simple to remove some or all of the items listed in this area of the Start Page, all you have to do is:

  1. Run regedit
  2. Navigate to HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0\ProjectMRUList
  3. Delete any of the values related to projects that you don't want to show up in the "Recent Projects" List

NOTE: These are the instructions for Visual Studio 2005, but the steps are almost identical for Visual Studio 2008 ... you do the same thing except instead of navigating to "8.0" in the registry path, go to "9.0".

Clear Recent Projects registry values
Thursday, January 03, 2008 8:37:18 AM (Central Standard Time, UTC-06:00)  # 

Creating an archive of a SourceSafe database or individual project is a fairly straight-forward task, but I was tired of having to refer back to one of the books I read to "re-learn" how to do it every time it needed to be done.  So here are the steps for how to make an archive for backup purposes.

  1. Log onto the SourceSafe server and open Visual SourceSafe Administrator.  Click on the Archive menu item, and choose Archive Projects ... as shown below:

  2. A wizard appears that allows you to select what you want to include in the archive. Choose $/ if you would like to archive the entire SourceSafe database you are currently logged into, or you can select an individual project (i.e. subfolder). If you want to archive multiple projects you can select a specific one and then add others to the archive by clicking the << Add button.

  3. The next step allows you to select where the archive should go, and a few other options. If you are creating the archive for backup purposes you will probably want to leave the radio button list set to the default selection of Save data to file, so that it doesn't remove the project(s) from SourceSafe after the archive is created.

  4. Next you can choose whether you want to archive all of the data contained in the database, or only a subset by specifying one of the following items in the Version textbox:
    • Project version number (Example: 5)
    • Project date in the format M/D/YY (Example: 11/1/07)
    • Project label string (Example: Beta1)

That's it! Click Finish, and in a few minutes you will have an archive file (.ssa extension) in the location you specified.
Monday, December 10, 2007 9:36:50 AM (Central Standard Time, UTC-06:00)  # 

This is a small, utility class I wrote recently that allows you to pass it an expression like the examples shown below, parse and evaluate it, and return the boolean equivalent.  I use it to evaluate expressions derived from dynamic values at runtime.  This is something I expected to be built into .NET, but couldn't find it anywhere.  However, it is pretty simple code and this implementation seems fairly robust and flexible.  Here are a few random examples of the types of strings you could pass the Evaluate method:

  • true AND (false OR true OR (true AND true)) AND (true OR false)
  • TRUE || FALSE && (FALSE)
  • (1 or 0) and (1 or 0)
  • (0&&0&&1)||1

public class BooleanEvaluator
{
    /// <summary>
    /// Evaluates the given expression and returns the result.
    /// </summary>
    public static bool Evaluate(string expression)
    {
        // Replace all the string elements like: false, true, and, or, with the equivalent bit
        // representation of the expression, and also remove all the whitespace.
        string bitExpression = expression.ToUpper().Replace("FALSE", "0").Replace("TRUE", "1");
        bitExpression = bitExpression.Replace("AND", "&&").Replace("OR", "||").Replace(" ", "");

        // Get a list of rules that can be used to decompose and evaluate an expression.
        Dictionary<string, string> rules = GetRules();

        // Continue looping through a set of rules that will decompose the operators until it is down
        // to a single character (either a "0" or "1").
        while (bitExpression.Length > 1)
        {
            // Loop through all of the rules and if one of the rules can be used to decompose part of
            // the expression, apply the rule and set ruleApplied to true.
            bool ruleApplied = false;
            foreach (KeyValuePair<string, string> thisRule in rules)
            {
                // Check to see if this rule matches any part of the expression
                if (bitExpression.Contains(thisRule.Key))
                {
                    bitExpression = bitExpression.Replace(thisRule.Key, thisRule.Value);
                    ruleApplied = true;
                }
            }

            // If none of the rules could be used to evaluate the expression any further, and we are
            // still not down to a single "0" or "1" ... break out of the loop because we will not
            // be able to evaluate the expression.  An exception will be thrown for this case at the
            // bottom of this method.  (Note: This could be because the expression contained unexpected
            // characters, or if it was not well-formed.)
            if (!ruleApplied)
                break;
        }

        // If the expression evaluated to either "0" or "1", return the appropriate boolean variable.
        if (bitExpression == "0")
            return false;
        else if (bitExpression == "1")
            return true;

        // If the expression didn't evaluate all the way down to 0 or 1, throw an exception
        throw new Exception("Couldn't evaluate expression: " + expression);
    }
    /// <summary>
    /// Returns a list of rules that can be used to decompose and evaluate an expression.
    /// </summary>
    private static Dictionary<string, string> GetRules()
    {
        Dictionary<string, string> rules = new Dictionary<string, string>();
        rules.Add("(0)", "0");
        rules.Add("(1)", "1");
        rules.Add("0&&0", "0");
        rules.Add("0&&1", "0");
        rules.Add("1&&0", "0");
        rules.Add("1&&1", "1");
        rules.Add("0||0", "0");
        rules.Add("0||1", "1");
        rules.Add("1||0", "1");
        rules.Add("1||1", "1");
        return rules;
    }
}

Friday, November 16, 2007 3:42:26 PM (Central Standard Time, UTC-06:00)  # 

There are a ton of crazy new features in Visual Studio 2008, and it seems most people are excited about LINQ.  However, the things I am most excited about are all of the new features related to JavaScript.  I will finally have intellisense in JavaScript.  I can't calculate how many hours I have spent trying to debug issues in JavaScript that turned out to be something stupid like typing in the name of a function or property incorrectly.  In fact, this happened so often that I actually picked up the habit of digging through tons of lines of JavaScript to find a function definition ... just so I can copy and paste the name of it to make sure I got it right.  No more with Visual Studio 2008.  You get full intellisense just like you would with your C# or VB, even on functions or properties that you define. 

How It Works
There was some intellisense in VS 2005 for JavaScript, but seriously ... it was worthless.  Microsoft really came to the plate with this release.  It is actually fairly complex how it has to work, because JavaScript is so loosely typed.  To illustrate this, look at the example below:

var thisVariable = "some text";
// 1st Spot
thisVariable = new Array("Saturn","Mars","Jupiter");
// 2nd Spot

thisVariable is simply of the type var, which means it can pretty much hold whatever you want to put in it.  On the line labeled 1st Spot, it should be considered a string, but on the line labeled 2nd Spot it should be considered an array.  In a strongly-typed language like C# all it has to do is look at what type you defined it to be and that is what it always is (anonymous types throw a slight kink in this, but it is essentially the same).  In JavaScript it is not this straight-forward, because a variable doesn't necessarily have to be associated with a particular data type.

The solution Microsoft came up with was to have the intellisense engine go back and infer the type based on the last value it was assigned.  So that means if you typed "thisVariable."  on the 1st Spot line you would get an intellisense pop-up containing string methods like split, indexOf or even some of the new methods provided by ASP.NET AJAX Client Library for strings like startsWith or trim (more about Client Library extensions).  But, if you did the same thing on the 2nd Spot line you would get methods related to the array object like sort, reverse, or pop (plus more extension methods provided by the Client Library).  How cool is that?

JavaScript XML Comments
You can even comment functions in the same XML style that you can C# or VB functions.  That way when you are calling that method from somewhere else you get the little pop-up summary that tells you a little more about what that function does.  Visual Studio will even automatically dig through all of the JavaScript files that are linked through script tags, and give you the same intellisense whether a function is defined in the file you are working in or somewhere else ... just like you would expect in server-side code.

JavaScript Debugging
And I can't tell you how tired I am of debugging JavaScript by writing alert statements everywhere ... so how about debugging JavaScript like you would server-side code?  In VS 2008 you can set breakpoints in JavaScript, and navigate in an intuitive way through the DOM.

So although LINQ is pretty cool ... I think the new JavaScript features in VS 2008 are a good enough reason for me to make the switch.  Here are a few other blog posts I ran across that explain some of these topics in more depth:

Thursday, November 15, 2007 9:16:08 PM (Central Standard Time, UTC-06:00)  # 

At my company, we use SourceSafe 2005 for source control.  It allows administrators to configure it to have one of the following check-out models:

  • Exclusive Check-Out Model: Lock-Modify-Unlock mode that only allows one user to modify a resource at a time.
  • Multiple Check-Out Model: Copy-Modify-Merge mode that allows multiple users to modify a resource at a time, after which a merge operation is performed at which point any conflicts between the changes can be addressed and resolved.  In my experience, the merge can typically be done automatically without any issues.

Although we usually only have two people working in the project at a time, there are still times when it would be convenient to allow both users to modify different sections of the file at the same time.  However, the few times we have done that one of the developers gets an error message the next time they try to build the solution that says "Access to the path _ is denied."  We didn't notice that this was the file that required the merge for a while, but finally pinpointed that to be the problem.

I found a blog post that explained the issue.  It turns out the "Include inheritable permissions from this object's parent" property of the merge files had been cleared.  If you simply re-check this property, the solution should build properly.  This is just seems to be a bug with the merge functionality in SourceSafe, and will hopefully be something they fix in the future.

Friday, October 19, 2007 1:23:45 PM (Central Standard Time, UTC-06:00)  # 

I use .netTiers as my data access and business logic layers in some of my web applications, and recently started using it on one that is hosted on a server I don't have access to.  .netTiers is based on the Enterprise Library, but allows you to choose which version you want to target.  I am targeting the latest version, Enterprise Library 3.1 (May 2007 release).  My application built and seemed to be working fine on my local machine, but when I tried to move the code out to the hosted server I got an error saying:

Security Exception
That assembly does not allow partially trusted callers.

After a little research, I figured out that the Enterprise Library runs at the "full" trust level out of the box, which is more than the company hosting my application would allow.  Like most major hosting services, they only allow applications to run under "medium" trust ... which is actually a good thing because it isolates your application from others that may be running on the same machine.  Medium trust applications also have no registry access, no event log access, no ability to use reflection, and file system access is limited to the application's virtual directory.

Fortunately, I did find a few "hacks" out there that allowed me to modify the library to run in "medium" trust environments.  I eventually built some new dll's for the Enterprise Library that I used to replace the ones in my application.  Even though the process was a little painful, I finally got it to work.  To save you from going through the same hassle, the link below will allow you to download the customized, medium trust dll files I built.  I bet if you are having a similar problem that the one I described, you can probably just copy the dll's you need over the ones currently in your application.

App Blocks bin Folder.zip (1.12 MB)

To generate those dll's I followed the steps listed below, which are a condensed version I found in this article.

  1. If you don't already have the Enterprise library installed, download and install the Enterprise Library 3.1 (May 2007 release).
  2. Make a complete copy of the EntLibSrc Enterprise Library 3.1 source code folder, and rename it to something like EntLibSrc-PartialTrust.
  3. Go to http://www.codeplex.com/ObjectBuilder/Release/ProjectReleases.aspx?ReleaseId=1613, download ObjectBuilder-1.0.51206.0-source.zip, and unzip it.
  4. Open the solution ObjectBuilder.sln from the root of your ObjectBuilder folder in Visual Studio 2005.
  5. Open the file AssemblyInfo.cs from the Properties folder of the ObjectBuilder project and add the line "[assembly: AllowPartiallyTrustedCallers()]" to the end of the code. (You'll also have to add a reference to the System.Security namespace).
  6. Right-click on the ObjectBuilder project in Solution Explorer and click Rebuild.
  7. Copy Microsoft.Practices.ObjectBuilder.dll from the EntLibSrc-PartialTrust\ObjectBuilder\ObjectBuilder\bin\Debug folder to the Enterprise Library folder EntLibSrc-PartialTrust\App Blocks\Lib, replacing the original signed version of the assembly.
  8. Open the solution EnterpriseLibrary.sln from the folder EntLibSrc-PartialTrust\App Blocks into Visual Studio 2005, and hit CTRL+SHIFT+H to open the "Find and Replace" dialog.  Set the following values:
    • Find what: <Reference Include="Microsoft.Practices.ObjectBuilder, Version=1.0.51206.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
    • Replace with: <Reference Include="Microsoft.Practices.ObjectBuilder, Version=1.0.51206.0, Culture=neutral, PublicKeyToken=null, processorArchitecture=MSIL">
    • Look in: [path]\EntLibSrc-PartialTrust (the full path to your copy of the Enterprise Library source files)
    • Include sub-folders: Yes (checked)
    • Look at these file types: *.csproj
  9. Click Replace All, and the matching files are modified.  VS will give you a few prompts, but just click "Reload" (and maybe "Discard" once) until there are no more alerts.  Click "Save All" from the File menu.
  10. Right-click on the Enterprise Solution entry at the top of the Solution Explorer window and click Rebuild.
  11. Navigate to the EntLibSrc-PartialTrust\App Blocks folder in Windows Explorer and double-click the batch file named BuildLibraryAndCopyAssemblies.bat.
  12. Copy the assemblies you need from the EntLibSrc-PartialTrust\App Blocks\Bin folder into your application's Bin folder.
Thursday, October 18, 2007 6:26:43 PM (Central Standard Time, UTC-06:00)  # 

This is a class I created to handle thumbnail generation and other typical image resizing tasks.  I think it is flexible and simple enough to be used in a variety of situations.  I will define the class and then give a couple of examples of how to use it.

public static class ImageProcessor
{
    /// <summary>
    /// Resizes the image located at the given originalRelativePath, making its longest side equal to the given maxSize 
///
(keeping it proportional), and then saves it to the location indicated by the saveToRelativePath parameter. /// </summary> public static void CreateThumbnail(string originalRelativePath, string saveToRelativePath, int maxSize) { Bitmap originalImage = new Bitmap(HttpContext.Current.Server.MapPath(originalRelativePath)); Bitmap newImage = CreateThumbnail(originalImage, maxSize);         
        FileStream thisFS = new new FileStream(HttpContext.Current.Server.MapPath(saveToRelativePath), FileMode.Create);
        n
ewImage.Save(thisFS, ImageFormat.Jpeg); originalImage.Dispose(); newImage.Dispose(); } /// <summary> /// Returns a Bitmap object that represents the image located at the given relativePath, resized so that it's
/// longest side is
equal to the given maxSize (keeping it proportional). /// </summary> public static Bitmap CreateThumbnail(string relativePath, int maxSize) { Bitmap originalImage = new Bitmap(HttpContext.Current.Server.MapPath(relativePath)); return CreateThumbnail(originalImage, maxSize); } /// <summary> /// Returns a Bitmap object (which is just a generic image object that isn't necessarily related to the .bmp
/// filetype) that is the
same image is the given originalImage, but scaled down to the given size. If it is
/// a vertical image the height would be equal to
the max size, and if it was a horizontal image the width would
/// be the maxSize ... but it always keeps the image proportional.
/// </summary> public static Bitmap CreateThumbnail(Bitmap originalImage, int maxSize) { float newWidth, newHeight; if (originalImage.Width >= originalImage.Height) { newWidth = maxSize; newHeight = (newWidth * (float)originalImage.Height) / (float)originalImage.Width; } else { newHeight = maxSize; newWidth = (newHeight * (float)originalImage.Width) / (float)originalImage.Height; } // Don't ever enlarge the picture ... if the user is trying to resize it to a size that is larger than
// the original, simply
return the same size image as the original with the border around it. if (newHeight >= (float)originalImage.Height || newWidth >= (float)originalImage.Width) { newHeight = (float)originalImage.Height; newWidth = (float)originalImage.Width; } Bitmap newBitmap = new Bitmap(originalImage, (int)newWidth, (int)newHeight); Rectangle recBorder = new Rectangle(0, 0, (int)newWidth - 1, (int)newHeight - 1); Graphics thisGO = Graphics.FromImage(newBitmap); thisGO.DrawRectangle(new Pen(Color.Black), recBorder); originalImage.Dispose(); return newBitmap; } }

You could call the code a variety a ways ... here are two examples or how it might be used:

string originalImagePath = Server.MapPath("/images/Original.jpg");
string thumbnailImagePath = Server.MapPath("/images/Thumbnail.jpg");
ImageProcessor.CreateThumbnail(originalImagePath, thumbnailImagePath, 300);

...

Bitmap thumbnail = ClassLibrary.ImageProcessor.CreateThumbnail(new Bitmap(thisFileUpload.PostedFile.InputStream), 50);
thumbnail.Save(thumbnailImagePath, System.Drawing.Imaging.ImageFormat.Jpeg);
Friday, October 12, 2007 8:15:06 AM (Central Standard Time, UTC-06:00)  # 

In SQL you can use the LIKE operator to perform simple keyword searches.  For example:

SELECT CustomerID, CompanyName, ContactName
FROM   dbo.
Customers
WHERE  CompanyName LIKE '%St%' OR ContactName LIKE '%St%'

Using the Northwind database, the script above would return 21 results:

CustomerID  CompanyName                  ContactName
----------------------------------------------------------------
ALFKI       Alfreds Futterkiste          Maria Anders
BERGS       Berglunds snabbköp           Christina Berglund
EASTC       Eastern Connection           Ann Devon
ERNSH       Ernst Handel                 Roland Mendel
FRANR       France restauration          Carine Schmitt
GALED       Galería del gastrónomo       Eduardo Saavedra
GROSR       GROSELLA-Restaurante         Manuel Pereira
HILAA       HILARION-Abastos             Carlos Hernández
HUNGC       Hungry Coyote Import Store   Yoshi Latimer
...

However, this can be slow because it usually involves a full table scan.  SQL Server has a much more efficient way to perform full-text searches, which utilizes the Microsoft Search Engine.  This involves creating a full-text catalog, which is external to the database (actually stored outside normal database structure) so it requires a little configuration but has the potential to significantly improve search performance.

Defining A Full-Text Index
To define a full-text index, just navigate to the table you want the index on using SQL Management Studio, right click on the table name, and choose "Define Full-Text Index..." as shown below:

SQL Server Define Full-Text Index

A wizard will appear like the one shown below, and you will need to configure what you want to index and how you want SQL to keep that index up-to-date (remember ... it is stored outside the normal database structure, so SQL has to keep it in-sync).  Most the time you don't need to change the wizard's default selections, but the next few screen shots show how I configured a full-text index for Northwind's Customer table.

SQL Server Full-Text Indexing Wizard

Full-Text Indexing Wizard Select Table Columns

Full-Text Indexing Wizard Select a Catalog

After you have the index created, you can use the FREETEXT, FREETEXTTABLE, CONTAINS, and CONTAINSTABLE keywords (for more info on the functionality that each provides go here).  You could rewrite the query from the earlier example to be more like this:

SELECT   C.CustomerID, C.CompanyName, C.ContactName, R.[Rank]
FROM     dbo.Customers C INNER JOIN
         CONTAINSTABLE(dbo.Customers, (CompanyName, ContactName), '"St*"') R ON C.CustomerID = R.[KEY]
ORDER BY R.[Rank] DESC

Which would yield the following results:

CustomerID  CompanyName                  ContactName      Rank
----------------------------------------------------------------
LAZYK       Lazy K Kountry Store         John Steel       112
VICTE       Victuailles en stock         Mary Saveley     112
HUNGC       Hungry Coyote Import Store   Yoshi Latimer    96
LETSS       Let's Stop N Shop            Jaime Yorres     96
QUICK       QUICK-Stop                   Horst Kloss      96

The script using LIKE returned 21 results, but the one using the full-text index only returned 5 results.  However, that is actually a good thing, because the full-text index only returned relevant results (the ones someone would most likely be looking for if they searched for "St").  The LIKE clause returned any row that had "st" somewhere in it ... whether it was the first of a word or buried in the middle of it.  The full-text index is smart enough to only return words that start with "St," but notice that doesn't necessarily mean it is at the start of the string or preceded by whitespace ... because it returned "Quick-Stop" as well.  A hidden benefit of using full-text indexes is that the Microsoft Search Engine will intelligently parse the text, and yield more relevant results.

When you search a full-text index using the CONTAINSTABLE method, you are also able to utilize a new column named rank.  This column indicates how relevant the match is compared to the rest of the results.  In this example, the rank column isn't too useful.  But if you were searching a long product description, this column could become very useful.  I chose this really simple example to just show the bare bones functionality of searching SQL, but when you are searching columns with a lot of text (like a product description) ... that is when this approach really pays off in terms of efficiency, only returning relevant results, and providing a rank of how relevant a result was in comparison to the rest of the set.

Wednesday, October 03, 2007 3:05:10 PM (Central Standard Time, UTC-06:00)  # 

This is based on a post by Vik Thairani that I have referred back to many times, and I just updated it for Outlook 2007 (he actually found the VB script here).  A lot of times I am frustrated when I enter a phone number in my phone (AT&T 8525 running Windows Mobile Pocket PC 5.0), because it always defaults to display contact names by "Last, First."  It seems a lot more intuitive to me if everyone is listed "First Last."  Outlook gives you the option to configure what you want the default format to be, but that functionality isn't available on my phone.  So when the two sync up, I have some listed one way and others listed differently.  The steps below will install a VB Script in Outlook that you can run anytime and it will spin through your every contact in your default contact folder and change them to all display in the "First Last" format ... instead of editing each one.

1. Set your default preference in Outlook
Go to Tools > Options > Contact Options
Set the Default "File As" Order

2. Setup Security to Allow Unsigned Macros
Go to Tools > Macro > Security
Change to "Warnings for all macros"
Restart Outlook

3. Creating the Macro
Go to Tools > Macro > Visual Basic Editor
In the Left hand window double click on "ThisOutlookSession" (you may have to expand the project tree)
Copy and paste the following script into the code window:

Public Sub FormatNamesAndNumbers()
    Dim objOL As Outlook.Application
    Dim objNS As Outlook.NameSpace
    Dim objContact As Outlook.ContactItem
    Dim objItems As Outlook.Items
    Dim objContactsFolder As Outlook.MAPIFolder
    Dim obj As Object
    On Error Resume Next
    Set objOL = CreateObject("Outlook.Application")
    Set objNS = objOL.GetNamespace("MAPI")
    Set objContactsFolder = objNS.GetDefaultFolder(olFolderContacts)
    Set objItems = objContactsFolder.Items
    For Each obj In objItems
        If obj.Class = olContact Then
            Set objContact = obj
            With objContact
                ' Try to file the contact by their first name followed by their last name.
                ' If one of those names are missing, just use the one that is there, but if
                ' both are missing file it by the company name set for the contact.
                If .FirstName <> "" Or .LastName <> "" Then
                    .FileAs = Trim(.FirstName & " " & .LastName)
                Else
                    .FileAs = .CompanyName
                End If
                
                ' Format all of the common types of phone numbers to be in the standard
                ' (XXX)XXX-XXXX format
                If .MobileTelephoneNumber <> "" Then _
                    .MobileTelephoneNumber = FormatPhoneNumber(.MobileTelephoneNumber)
                If .BusinessTelephoneNumber <> "" Then _
                    .BusinessTelephoneNumber = FormatPhoneNumber(.BusinessTelephoneNumber)
                If .HomeTelephoneNumber <> "" Then _
                    .HomeTelephoneNumber = FormatPhoneNumber(.HomeTelephoneNumber)
                 
                .Save
            End With
        End If
        Err.Clear
    Next
    Set objOL = Nothing
    Set objNS = Nothing
    Set obj = Nothing
    Set objContact = Nothing
    Set objItems = Nothing
    Set objContactsFolder = Nothing
End Sub

Private Function FormatPhoneNumber(ByVal number)

    Dim defaultAreaCode As String
    Dim returnValue As String
    defaultAreaCode = "806"

    number = CStr(number)
    number = Replace(number, "-", "")
    number = Replace(number, "(", "")
    number = Replace(number, ")", "")
    number = Replace(number, "+1", "")
    number = Replace(number, " ", "")
    Select Case Len(number)
        Case 7
            ' The number doesn't include an area code ... append the default area code
            returnValue = "(" & defaultAreaCode & ") " & _
                            Mid(number, 1, 3) & "-" & Mid(number, 4, 4)
        Case 10
            returnValue = "(" & Mid(number, 1, 3) & ") " & _
                            Mid(number, 4, 3) & "-" & Mid(number, 7, 4)
        Case 11
            ' The number is prefixed with an unnecessary "1" for long distance
            returnValue = "(" & Mid(number, 2, 3) & ") " & _
                            Mid(number, 5, 3) & "-" & Mid(number, 8, 4)
        Case Else
            returnValue = number
    End Select
    FormatPhoneNumber = returnValue
End Function

4. Saving the Code and Running the Macro
Click File > Save
Close the editor window
Go to Tools > Macro > Macros
Select "ThisOutlookSession.FormatNamesAndNumbers" and click Run

That's it ... after the script completes all of your contacts will be in the "First Last" format, and the changes will be reflected on your phone next time you sync.  It is a good idea to change your macro security settings back to "Warnings for signed macros, all other macros are disabled", which you can do by repeating step 2 and choosing the appropriate option.

I also updated it to format mobile, home, and business phone numbers to be in the common (XXX)XXX-XXXX format.  Notice that the FormatPhoneNumber function has a default area code set in the first few lines.  It will append that to numbers that only have 7 digits (i.e. no area code).

Monday, October 01, 2007 6:47:33 AM (Central Standard Time, UTC-06:00)  # 

.netTiers is the object relational mapping software that we currently use for all our applications.  In my experience it is extremely flexible, and saves me the several hours a week I used to spend writing custom CRUD (Create, Read, Update, Delete) methods to various objects in the database.  Plus since it is open-source and template-based, I can go in and change any part of it I see necessary. 

 

There are a lot of places on the Internet that list what the .netTiers templates provide, but I couldn’t really find anywhere that provided a clear, comprehensive overview of what it provided … without going into too much detail.  So I pieced together the following definition that explains what .netTiers is and the functionality it could provide.  .netTiers really does save me and my team hours of development time every week, so if it sounds like something that might help you out … you should definitely check into it further by going to www.netTiers.com.

 


.netTiers is a set of free CodeSmith templates that targets an existing datasource and automatically generates a personalized architecture to use in your .NET applications.  The generated code is custom to your domain, uses familiar patterns, and follows the guidance of Microsoft's recommended patterns and practices.  In fact, the .netTiers base architecture is built upon the Microsoft Enterprise Library Application Blocks.  Core features include:

 

  • Generate strongly-typed business entities with a 1:1 mapping to the datasource (an entity for each table or view, with a property for each column):
    • All objects are serializable, and support trigger events
    • Implements an IEntity interface, which contains the columns that are present in every table
    • Each object has a concrete and a base class which it inherit from. The concrete class is generated just once, so after the first time you can then add your custom logic straight in the code-gened files
    • Uses a custom generic List for collections that are sortable, filterable, and directly bindable to datagrid and other ASP.NET and WinForm controls
  • Generate Data Access Layer Components (DALC) for tables and views, with following database operations:
    • Support for basic CRUD methods, plus several other useful methods such as Select All, Paged Select, Find (with paging and sorting), etc and allows the addition of your own custom methods
    • Support for queries using primary & foreign keys, as well as keys that are part of an index or junction table
    • Support for Deep loading and saving, with children type selection and optional recursivity
  • Option to use stored procedure or parameterized SQL inside the application
  • Generates a complete nAnt build file, to compile, test and generate documentation
  • Generates a full set of nUnit tests
  • All code is fully nDoc commented and follow the Microsoft naming guidelines
  • Open-source so you can modify the templates if necessary
Sunday, September 30, 2007 1:04:29 PM (Central Standard Time, UTC-06:00)  # 

As IT professionals we are always dumbfounded when we see a user who has passwords written on sticky notes and then used to decorate the edges of a monitor.  We are even guilty of keeping track of some passwords in a spreadsheet from time to time, which is also not a security best practice, and we don't even like to think about people who really just have one or two passwords they use for everything.  Why do we do this?  Too many passwords.  I currently have around 40 passwords I have to remember for various software, networks, and web sites.

Most of my time is spent developing and maintaining my company's intranet/extranet, and I have it set up to use a hybrid forms/windows authentication.  That way if the user is already signed into a computer on our domain, the site will pull their windows credentials, match it up with a user in the database, and automatically log them in through forms authentication.  This happens in less than a second and the user never knows it happened.  But, users outside the company (that aren't signed into our network) can use the classic username/password form and gain access to the site without needing a user account in Active Directory. 

However, there are times when an employee who typically access the site from inside the network need to look at something from home or on the road.  Since they aren't signed into the domain, the site won't automatically pull their windows credentials ... so they are sent to the classic forms authentication log in page.  What credentials should they try to use?  Well, we could give them one more username/password to keep track of ... or the solution I went for is if the credentials don't match up with any forms authentication user in the database, query active directory from code to see if the credentials the user provided are valid domain credentials.  So in reality, on the classic forms authentication page the user could enter forms authentication credentials (validated against the database), or enter domain credentials (validated against Active Directory).

There are other solutions out there (like this one) that allow you to use forms authentication with Active Directory, but really the code that I needed was very simple.  It is based on a snippet I found in Developing More-Secure Microsoft ASP.NET 2.0 Applications.  It uses LDAP queries to authenticate a given set of credentials against Active Directory, but creating a DirectoryEntity object with those credentials and then forcing them to bind.  If it returns an error code of 2147023570, that indicates a login failure ... which means the credentials the user provided are not valid domain credentails.  If no error occurs, that means the credentials matched a user in Active Directory.


/// <summary>
/// Returns true if the given credentials match a valide forms authentication user in the database or an account in Active Directory, 
/// and false otherwise.
/// </summary>
public static bool ValidateCredentials(string Username, string Password)
{
    bool IsValid;
    using (SqlConnection thisConnection = new SqlConnection(Common.ConnectionString))
    {
        string SprocName = "sproc_ValidateCredentials";
        SqlCommand thisCommand = new SqlCommand(SprocName, thisConnection);
        thisCommand.CommandType = CommandType.StoredProcedure;
        thisCommand.Parameters.AddWithValue("@Username", Username);
        thisCommand.Parameters.AddWithValue("@Password", Password);
        thisConnection.Open();
        IsValid = Convert.ToBoolean(thisCommand.ExecuteScalar());
    }

    if (!IsValid)
        IsValid = AuthenticateAgainstActiveDirectory(Username, Password);

    return IsValid;
}

/// <summary>
/// Returns true if the given UserID and Password were valid network credentials according to Active Directory, and false otherwise.
/// The code uses LDAP to communicate with Active Directory, and simply creates a DirectoryEntry object using the given credentials
/// and then takes some action that causes a bind.  If no error occurs, the user would be allowed to log onto the domain.
/// </summary>
public static bool AuthenticateAgainstActiveDirectory(string Username, string Password)
{
    // Strip everything but the Username out from the text the user provided
    Username = Username.ToLower().Replace(@"DomainName\", "");

    // Create the entity that will connect to the LDAP server
    DirectoryEntry thisEntry = new DirectoryEntry(@"LDAP://192.168.0.1", Username, Password);

    try
    {
        // Perform an action that will force the bind to ActiveDirectory ... if this doesn't throw
        // an error then a user would be able to log onto the network with the given credentials
        thisEntry.RefreshCache();
        return true;
    }
    catch (System.Runtime.InteropServices.COMException thisExc)
    {
        // Make sure the error that got thrown was a login failure, and if it wasn't rethrow the error
        if (thisExc.ErrorCode != -2147023570)
            throw;

        return false;
    }
}
Friday, September 28, 2007 6:40:50 AM (Central Standard Time, UTC-06:00)  # 

Back in March, I attended the spring DevConnections conference in Orlando (which was awesome).  I sat in one of Scott Guthrie's sessions named "ASP.NET 2.0, ASP.NET AJAX, and Visual Studio 2005 Tips and Tricks."  He showed a ton of cool stuff in that session, but one of the things that caught my eye was the new $get function available in ASP.NET AJAX.  It is really just an alias to the document.getElementById function in the DOM. 

However, I saw some potential in it that might solve one of the issues that constantly frustrated me when using master pages ... so I sent Scott and email to suggest some functionality that his team might consider building into future versions of the framework.  To my surprise Scott emailed me back the next day (which was a Sunday) with a really simple work-around that has really helped me out.  So I thought I would share it with the rest of the world.  Here is the guts of my original email followed by Scott's response:


The guts of my original email to Scott:

It is my understanding that the $get command is simply a shorthand version of the document.getElementById() method.  One thing that has been frustrating since I switched over to the ASP.NET 2.0 way of doing things, is when you use MasterPages you have to account for how ASP.NET will rename your server-side elements when it renders them to the client.  Since I do find myself writing quite a bit of JavaScript, finding and accounting for these fully qualified names is probably more of a hassle than it should be.  Is there any way you guys could work into the .NET Framework v3.5 a way use the $get command to retrieve values without having to use their fully qualified name.  I realize one option is to render JavaScript from the server to dynamically insert the assigned ID, but that seems like quite a bit of overhead, and if it was built into the framework your team could also tune it for better performance as well.

It seems that MasterPages are so useful, but as JavaScript becomes more prevalent throughout web applications I am tempted to revert back to the “old school” approach of PageStart and PageEnd user controls to encapsulate a page instead of MasterPages.  I have put a lot of thought into this and realize it is a complicated problem to solve, but I just wanted to mention it because I think it is really important and will become more important with the rise of technologies like AJAX.


Scott's response:

Hi Cal,

Right now $get() is indeed just a short-cut for document.getElementByID().  We are looking to improve the ID naming of elements over the next year to give you more control over how they are rendered.

In the meantime, you can use a technique where you render the client name into JavaScript using the Control.ClientID property:

   var myControl = <%= MyControl.ClientID %>;

You can then write this JavaScript code to dynamically load the control regardless of where it is used on the page:

   $get(myControl);

Hope this helps,
Scott


Here is an example of how I use it most the time:

var txtName = $get('<%= txtName.ClientID %>');
txtName.value = "Lorem ipsum";

Monday, September 24, 2007 2:18:50 PM (Central Standard Time, UTC-06:00)  #