<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:georss="http://www.georss.org/georss" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>Scott Klueppel's Blog - Database</title>
    <link>http://offroadcoder.com/</link>
    <description>making the hard line look easy</description>
    <language>en-us</language>
    <copyright>Scott Klueppel</copyright>
    <lastBuildDate>Tue, 27 Jul 2010 03:32:07 GMT</lastBuildDate>
    <generator>newtelligence dasBlog 2.1.8102.813</generator>
    <managingEditor>me@offroadcoder.com</managingEditor>
    <webMaster>me@offroadcoder.com</webMaster>
    <item>
      <trackback:ping>http://offroadcoder.com/Trackback.aspx?guid=fb59ebf8-4230-43f8-9226-b02389165fc1</trackback:ping>
      <pingback:server>http://offroadcoder.com/pingback.aspx</pingback:server>
      <pingback:target>http://offroadcoder.com/PermaLink,guid,fb59ebf8-4230-43f8-9226-b02389165fc1.aspx</pingback:target>
      <dc:creator>Scott Klueppel</dc:creator>
      <georss:point>30.109017 -81.497099</georss:point>
      <wfw:comment>http://offroadcoder.com/CommentView,guid,fb59ebf8-4230-43f8-9226-b02389165fc1.aspx</wfw:comment>
      <wfw:commentRss>http://offroadcoder.com/SyndicationService.asmx/GetEntryCommentsRss?guid=fb59ebf8-4230-43f8-9226-b02389165fc1</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
In .NET 1.1, I tried the original MS Data Access Application Block’s SqlHelper (you
can still download it <a href="http://download.microsoft.com/download/VisualStudioNET/daabref/RTM/NT5/EN-US/DataAccessApplicationBlock.msi">here</a>).
It was great for most of the common uses, but was lacking in some areas. The consuming
code looked sloppy and encouraged blind faith that database objects never changed.
It also didn’t support transactions as I would have liked, and didn’t support my obsession
with custom entities. I started out writing an extension library that wrapped SqlHelper,
but that felt very wrong… wrapping the ADO.NET wrapper (SqlHelper). I ended up writing
my own version of SqlHelper called SqlHelper (nice name, eh?). You see, at this time
I was getting over a bad relationship with a series of ORM products that had a negative
effect on my productivity. I decided to revolt with good ol’ fashion data access methods
that have never let us down.
</p>
        <p>
The only thing worse than my ORM experience was the disgusting over-use of DataSet
and DataTable. For my dollar, DataReader is where it’s at. I agree that using the
reader is slightly more dangerous in the hands of an inexperienced or inattentive
developer (did you know you have to close the reader when you’re done with it??).
Nothing can compare with the speed and flexibility of the reader, which is why DataSet
and DataAdapter use it at their core. If you are working with custom entities, instead
of DataSets and DataTables, you would be crazy to not use the DataReader.
</p>
        <p>
My SqlHelper worked in conjunction with my DataAccessLayer class that defined a few
delegates that made reader-to-object-mapping a simple task.  Once the mapping
methods were written to be used with the delegates, which returned object or System.Collections.CollectionBase
because we did not yet have generics (can you imagine??), you simply called the SqlHelper
to do all of the hard work. SqlHelper did not implement all of the craziness that
the original version contained. It was a short 450 lines of code that did nothing
but access data in a safe and reliable way. In the example below, we have the GenerateDocumentFromReader
method that is used by the GenerateObjectFromReader delegate. When SqlHelper.ExecuteReaderCmd
is called, the delegate is passed in to map the reader results to my object… in this
case a Document.
</p>
        <pre class="brush: c#;">// Object generation method 
private static object GenerateDocumentFromReader(IDataReader returnData) 
{
     Document document = new Document();
     if (returnData.Read())
     {
         document = new Document(
             (int)returnData["DocumentId"],
             (byte[])returnData["DocumentBinary"],
             returnData["FileName"].ToString(),
             returnData["Description"].ToString(),
             returnData["ContentType"].ToString(),
             (int)returnData["FileSize"],
             returnData["MD5Sum"].ToString(),
             (bool) returnData["EnabledInd"],
             (int)returnData["CreatorEmpId"],
             Convert.ToDateTime(returnData["CreateDt"]),
             (int)returnData["LastUpdateEmpId"],
             Convert.ToDateTime(returnData["LastUpdateDt"]));
     }     return document;
} 
public static Document GetDocumentByDocumentId(int documentId)
{
     SqlCommand sqlCmd = new SqlCommand();
     SqlHelper.SetCommandArguments(sqlCmd, CommandType.StoredProcedure, "usp_Document_GetDocumentByDocumentId");
     SqlHelper.AddParameterToSqlCommand(sqlCmd, "@DocumentId", SqlDbType.Int, 0, ParameterDirection.Input, documentId);
     DataAccessLayer.GenerateObjectFromReader gofr = new DataAccessLayer.GenerateObjectFromReader(GenerateDocumentFromReader);
     Document document = SqlHelper.ExecuteReaderCmd(sqlCmd, gofr) as Document;
     return document;
}
</pre>
        <p>
This worked wonderfully for years. After converting, I couldn’t imagine a project
that used ORM, DataSets, or DataTables again. I’ve been on many 1.1 projects since
writing my SqlHelper in 2004, and I have successfully converted them all. In early
2006, MS graced us with .NET 2.0. Generics, System.Transactions, and partial classes
changed my life. In my first few exposures to generics, like Vinay “the Generic Guy”
Ahuja’s 2005 Jax Code Camp presentation and Juval “My Hero” Lowy’s <a href="http://msdn.microsoft.com/en-us/library/ms379564">MSDN
article “An Introduction to Generics”</a>, I listened/read and pondered the millions
of uses of generics. I adapted my SqlHelper heavily to use these new technologies
and morphed it into something else that closely represented the newest version of
the DAAB, Enterprise Library 3.
</p>
        <p>
By this point, I wanted to convert to Enterprise Library. It was far better than the
simple SqlHelper. It had better transaction support, though I don’t know if that included
System.Transactions. I could have put my object generation extensions on top of it
and it would have worked well for years. On home projects I had already converted
to use EntLib. At work I was not so lucky. The deep stack trace when something went
wrong scared everyone, and that is still a fear for those starting out in EntLib today.
To ease the fears, I just created my replacement to SqlHelper… the Database class. 
</p>
        <p>
I used a lot of the same naming conventions as Enterprise Library. In fact, much of
the consuming code was nearly identical (except for the fact that it did not implement
the provider pattern and worked only with SQL Server). This was in anticipation of
a quick adoption of Enterprise Library 3 in the workplace. Kind of a “see… not so
bad” move on my part. Just like EntLib, you created a Database class using the DatabaseFactory
that used your default connection string key. Commands and parameters were created
and added with methods off of the Database class. Aside from the SqlCommand/DbCommand,
everything looked and felt the same, but came in a small file with only 490 lines
of code instead of 5 or more projects with 490 files. Using it felt the same, too.
Only my object/collection generation extensions looked different from the standard
reader, scalar, dataset routines. Below is the same code from above using the Database
class and related classes to create a Document from a reader.
</p>
        <pre class="brush: c#;">// Object generation method
private static Document GenerateDocumentFromReader(IDataReader returnData)
{
     Document document = new Document();
     if (returnData.Read())
     {
         document = new Document(
             GetIntFromReader(returnData, "DocumentId"),
             GetIntFromReader(returnData, "DocumentTypeId"),
             GetStringFromReader(returnData, "DocumentTypeName"),
             GetByteArrayFromReader(returnData, "DocumentBinary"),
             GetStringFromReader(returnData, "FileName"),
             GetStringFromReader(returnData, "Description"),
             GetStringFromReader(returnData, "ContentType"),
             GetIntFromReader(returnData, "FileSize"),
             GetStringFromReader(returnData, "MD5Sum"),
             GetStringFromReader(returnData, "CreatorEmpID"),
             GetDateTimeFromReader(returnData, "CreateDt"),
             GetStringFromReader(returnData, "LastUpdateEmpID"),
             GetDateTimeFromReader(returnData, "LastUpdateDt"));
     }
     return document;
} 
public static Document GetDocumentByDocumentId(int documentId)
{
     Database db = DatabaseFactory.CreateDatabase(AppSettings.ConnectionStringKey);
     SqlCommand sqlCmd = db.GetStoredProcCommand("usp_Document_GetDocumentByDocumentId");
     db.AddInParameter(sqlCmd, "DocumentId", SqlDbType.Int, documentId);
     GenerateObjectFromReader&lt;Document&gt; gofr = new GenerateObjectFromReader&lt;Document&gt;(GenerateDocumentFromReader);
     Document document = CreateObjectFromDatabase&lt;Document&gt;(db, sqlCmd, gofr);
     return document;
}
</pre>
        <p>
This, too, worked great for years. Other than a brief period in 2007 when I tried
to wrap all of my data access code with WCF services, .NET 3.0 came and went with
no changes to my data access methodology. In late 2007, I had lost all love of my
SqlHelper and my Database/DataAccessLayer classes. With .NET 3.5 and Enterprise Library
4.0, I no longer felt the need to roll my own. .NET now had extension methods for
me to extend Enterprise Library however I pleased. Enterprise Library supported System.Transactions
making its use a dream if behind a WCF service that allowed transaction flow. With
a succinct 190 lines of extension code, I had it made in the shade with Enterprise
Library 4.0. In fact, I haven’t used anything since.
</p>
        <p>
The consuming code was almost exactly the same. You’ll notice the SqlCommand has changed
to DbCommand. The SqlDbType has changed to DbType. Other than that, it feels and works
the same. 
</p>
        <pre class="brush: c#;">// Object generation method
private static Document GenerateDocumentFromReader(IDataReader returnData)
{
     Document document = new Document();
     if (returnData.Read())
     {
         document = new Document(
             returnData.GetInt32("DocumentId"),
             returnData.GetInt32("DocumentTypeId"),
             returnData.GetString("DocumentTypeName"),
             returnData.GetByteArray("DocumentBinary"),
             returnData.GetString("FileName"),
             returnData.GetString("Description"),
             returnData.GetString("ContentType"),
             returnData.GetInt32("FileSize"),
             returnData.GetString("MD5Sum"),
             returnData.GetString("CreatorEmpID"),
             returnData.GetDateTime("CreateDt"),
             returnData.GetString("LastUpdateEmpID"),
             returnData.GetDateTime("LastUpdateDt"));
     }
     return document;
}
public static Document GetDocumentByDocumentID(int documentId)
{
     Database db = DatabaseFactory.CreateDatabase();
     DbCommand cmd = db.GetStoredProcCommand("usp_Document_GetDocumentByDocumentId");
     db.AddInParameter(cmd, "DocumentID", DbType.Int32, documentId);
     GenerateObjectFromReader&lt;Document&gt; gofr = new GenerateObjectFromReader&lt;Document&gt;(GenerateDocumentFromReader);
     Document document = db.CreateObject&lt;Document&gt;(cmd, gofr);
     return document;
}
</pre>
        <p>
With a full suite of unit test projects available for download with the Enterprise
Library source files, the fear should be abated for the remaining holdouts. Getting
started is as easy as including two DLL references, and adding 5 lines of config.
You can’t beat that!
</p>
        <p>
I downloaded Enterprise Library 5 last week. I’ve been making use of new features
such as result set mapping (eliminating the need for my object generation extensions),
parameter mapping, and accessors that bring them all together. There’s a bunch of
inversion of control features in place as well. I think I’ll be quite comfortable
in my new EntLib5 home.
</p>
        <img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=fb59ebf8-4230-43f8-9226-b02389165fc1" />
      </body>
      <title>My data access story before Enterprise Library 5</title>
      <guid isPermaLink="false">http://offroadcoder.com/PermaLink,guid,fb59ebf8-4230-43f8-9226-b02389165fc1.aspx</guid>
      <link>http://offroadcoder.com/2010/07/27/MyDataAccessStoryBeforeEnterpriseLibrary5.aspx</link>
      <pubDate>Tue, 27 Jul 2010 03:32:07 GMT</pubDate>
      <description>&lt;p&gt;
In .NET 1.1, I tried the original MS Data Access Application Block’s SqlHelper (you
can still download it &lt;a href="http://download.microsoft.com/download/VisualStudioNET/daabref/RTM/NT5/EN-US/DataAccessApplicationBlock.msi"&gt;here&lt;/a&gt;).
It was great for most of the common uses, but was lacking in some areas. The consuming
code looked sloppy and encouraged blind faith that database objects never changed.
It also didn’t support transactions as I would have liked, and didn’t support my obsession
with custom entities. I started out writing an extension library that wrapped SqlHelper,
but that felt very wrong… wrapping the ADO.NET wrapper (SqlHelper). I ended up writing
my own version of SqlHelper called SqlHelper (nice name, eh?). You see, at this time
I was getting over a bad relationship with a series of ORM products that had a negative
effect on my productivity. I decided to revolt with good ol’ fashion data access methods
that have never let us down.
&lt;/p&gt;
&lt;p&gt;
The only thing worse than my ORM experience was the disgusting over-use of DataSet
and DataTable. For my dollar, DataReader is where it’s at. I agree that using the
reader is slightly more dangerous in the hands of an inexperienced or inattentive
developer (did you know you have to close the reader when you’re done with it??).
Nothing can compare with the speed and flexibility of the reader, which is why DataSet
and DataAdapter use it at their core. If you are working with custom entities, instead
of DataSets and DataTables, you would be crazy to not use the DataReader.
&lt;/p&gt;
&lt;p&gt;
My SqlHelper worked in conjunction with my DataAccessLayer class that defined a few
delegates that made reader-to-object-mapping a simple task.&amp;nbsp; Once the mapping
methods were written to be used with the delegates, which returned object or System.Collections.CollectionBase
because we did not yet have generics (can you imagine??), you simply called the SqlHelper
to do all of the hard work. SqlHelper did not implement all of the craziness that
the original version contained. It was a short 450 lines of code that did nothing
but access data in a safe and reliable way. In the example below, we have the GenerateDocumentFromReader
method that is used by the GenerateObjectFromReader delegate. When SqlHelper.ExecuteReaderCmd
is called, the delegate is passed in to map the reader results to my object… in this
case a Document.
&lt;/p&gt;
&lt;pre class="brush: c#;"&gt;// Object generation method 
private static object GenerateDocumentFromReader(IDataReader returnData) 
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Document document = new Document();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (returnData.Read())
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; document = new Document(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (int)returnData["DocumentId"],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (byte[])returnData["DocumentBinary"],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData["FileName"].ToString(),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData["Description"].ToString(),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData["ContentType"].ToString(),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (int)returnData["FileSize"],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData["MD5Sum"].ToString(),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (bool) returnData["EnabledInd"],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (int)returnData["CreatorEmpId"],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Convert.ToDateTime(returnData["CreateDt"]),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (int)returnData["LastUpdateEmpId"],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Convert.ToDateTime(returnData["LastUpdateDt"]));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return document;
} 
public static Document GetDocumentByDocumentId(int documentId)
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlCommand sqlCmd = new SqlCommand();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlHelper.SetCommandArguments(sqlCmd, CommandType.StoredProcedure, "usp_Document_GetDocumentByDocumentId");
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlHelper.AddParameterToSqlCommand(sqlCmd, "@DocumentId", SqlDbType.Int, 0, ParameterDirection.Input, documentId);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DataAccessLayer.GenerateObjectFromReader gofr = new DataAccessLayer.GenerateObjectFromReader(GenerateDocumentFromReader);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Document document = SqlHelper.ExecuteReaderCmd(sqlCmd, gofr) as Document;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return document;
}
&lt;/pre&gt;
&lt;p&gt;
This worked wonderfully for years. After converting, I couldn’t imagine a project
that used ORM, DataSets, or DataTables again. I’ve been on many 1.1 projects since
writing my SqlHelper in 2004, and I have successfully converted them all. In early
2006, MS graced us with .NET 2.0. Generics, System.Transactions, and partial classes
changed my life. In my first few exposures to generics, like Vinay “the Generic Guy”
Ahuja’s 2005 Jax Code Camp presentation and Juval “My Hero” Lowy’s &lt;a href="http://msdn.microsoft.com/en-us/library/ms379564"&gt;MSDN
article “An Introduction to Generics”&lt;/a&gt;, I listened/read and pondered the millions
of uses of generics. I adapted my SqlHelper heavily to use these new technologies
and morphed it into something else that closely represented the newest version of
the DAAB, Enterprise Library 3.
&lt;/p&gt;
&lt;p&gt;
By this point, I wanted to convert to Enterprise Library. It was far better than the
simple SqlHelper. It had better transaction support, though I don’t know if that included
System.Transactions. I could have put my object generation extensions on top of it
and it would have worked well for years. On home projects I had already converted
to use EntLib. At work I was not so lucky. The deep stack trace when something went
wrong scared everyone, and that is still a fear for those starting out in EntLib today.
To ease the fears, I just created my replacement to SqlHelper… the Database class. 
&lt;/p&gt;
&lt;p&gt;
I used a lot of the same naming conventions as Enterprise Library. In fact, much of
the consuming code was nearly identical (except for the fact that it did not implement
the provider pattern and worked only with SQL Server). This was in anticipation of
a quick adoption of Enterprise Library 3 in the workplace. Kind of a “see… not so
bad” move on my part. Just like EntLib, you created a Database class using the DatabaseFactory
that used your default connection string key. Commands and parameters were created
and added with methods off of the Database class. Aside from the SqlCommand/DbCommand,
everything looked and felt the same, but came in a small file with only 490 lines
of code instead of 5 or more projects with 490 files. Using it felt the same, too.
Only my object/collection generation extensions looked different from the standard
reader, scalar, dataset routines. Below is the same code from above using the Database
class and related classes to create a Document from a reader.
&lt;/p&gt;
&lt;pre class="brush: c#;"&gt;// Object generation method
private static Document GenerateDocumentFromReader(IDataReader returnData)
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Document document = new Document();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (returnData.Read())
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; document = new Document(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetIntFromReader(returnData, "DocumentId"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetIntFromReader(returnData, "DocumentTypeId"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetStringFromReader(returnData, "DocumentTypeName"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetByteArrayFromReader(returnData, "DocumentBinary"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetStringFromReader(returnData, "FileName"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetStringFromReader(returnData, "Description"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetStringFromReader(returnData, "ContentType"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetIntFromReader(returnData, "FileSize"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetStringFromReader(returnData, "MD5Sum"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetStringFromReader(returnData, "CreatorEmpID"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetDateTimeFromReader(returnData, "CreateDt"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetStringFromReader(returnData, "LastUpdateEmpID"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetDateTimeFromReader(returnData, "LastUpdateDt"));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return document;
} 
public static Document GetDocumentByDocumentId(int documentId)
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Database db = DatabaseFactory.CreateDatabase(AppSettings.ConnectionStringKey);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlCommand sqlCmd = db.GetStoredProcCommand("usp_Document_GetDocumentByDocumentId");
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.AddInParameter(sqlCmd, "DocumentId", SqlDbType.Int, documentId);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GenerateObjectFromReader&amp;lt;Document&amp;gt; gofr = new GenerateObjectFromReader&amp;lt;Document&amp;gt;(GenerateDocumentFromReader);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Document document = CreateObjectFromDatabase&amp;lt;Document&amp;gt;(db, sqlCmd, gofr);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return document;
}
&lt;/pre&gt;
&lt;p&gt;
This, too, worked great for years. Other than a brief period in 2007 when I tried
to wrap all of my data access code with WCF services, .NET 3.0 came and went with
no changes to my data access methodology. In late 2007, I had lost all love of my
SqlHelper and my Database/DataAccessLayer classes. With .NET 3.5 and Enterprise Library
4.0, I no longer felt the need to roll my own. .NET now had extension methods for
me to extend Enterprise Library however I pleased. Enterprise Library supported System.Transactions
making its use a dream if behind a WCF service that allowed transaction flow. With
a succinct 190 lines of extension code, I had it made in the shade with Enterprise
Library 4.0. In fact, I haven’t used anything since.
&lt;/p&gt;
&lt;p&gt;
The consuming code was almost exactly the same. You’ll notice the SqlCommand has changed
to DbCommand. The SqlDbType has changed to DbType. Other than that, it feels and works
the same. 
&lt;/p&gt;
&lt;pre class="brush: c#;"&gt;// Object generation method
private static Document GenerateDocumentFromReader(IDataReader returnData)
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Document document = new Document();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (returnData.Read())
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; document = new Document(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetInt32("DocumentId"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetInt32("DocumentTypeId"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetString("DocumentTypeName"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetByteArray("DocumentBinary"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetString("FileName"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetString("Description"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetString("ContentType"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetInt32("FileSize"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetString("MD5Sum"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetString("CreatorEmpID"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetDateTime("CreateDt"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetString("LastUpdateEmpID"),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; returnData.GetDateTime("LastUpdateDt"));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return document;
}
public static Document GetDocumentByDocumentID(int documentId)
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Database db = DatabaseFactory.CreateDatabase();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DbCommand cmd = db.GetStoredProcCommand("usp_Document_GetDocumentByDocumentId");
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.AddInParameter(cmd, "DocumentID", DbType.Int32, documentId);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GenerateObjectFromReader&amp;lt;Document&amp;gt; gofr = new GenerateObjectFromReader&amp;lt;Document&amp;gt;(GenerateDocumentFromReader);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Document document = db.CreateObject&amp;lt;Document&amp;gt;(cmd, gofr);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return document;
}
&lt;/pre&gt;
&lt;p&gt;
With a full suite of unit test projects available for download with the Enterprise
Library source files, the fear should be abated for the remaining holdouts. Getting
started is as easy as including two DLL references, and adding 5 lines of config.
You can’t beat that!
&lt;/p&gt;
&lt;p&gt;
I downloaded Enterprise Library 5 last week. I’ve been making use of new features
such as result set mapping (eliminating the need for my object generation extensions),
parameter mapping, and accessors that bring them all together. There’s a bunch of
inversion of control features in place as well. I think I’ll be quite comfortable
in my new EntLib5 home.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=fb59ebf8-4230-43f8-9226-b02389165fc1" /&gt;</description>
      <comments>http://offroadcoder.com/CommentView,guid,fb59ebf8-4230-43f8-9226-b02389165fc1.aspx</comments>
      <category>C#</category>
      <category>Database</category>
      <category>Enterprise Library</category>
      <category>Extensions</category>
      <category>SQL</category>
    </item>
    <item>
      <trackback:ping>http://offroadcoder.com/Trackback.aspx?guid=f6cca0b0-5d91-48bf-a644-338e2b781d30</trackback:ping>
      <pingback:server>http://offroadcoder.com/pingback.aspx</pingback:server>
      <pingback:target>http://offroadcoder.com/PermaLink,guid,f6cca0b0-5d91-48bf-a644-338e2b781d30.aspx</pingback:target>
      <dc:creator>Scott Klueppel</dc:creator>
      <georss:point>30.109017 -81.497099</georss:point>
      <wfw:comment>http://offroadcoder.com/CommentView,guid,f6cca0b0-5d91-48bf-a644-338e2b781d30.aspx</wfw:comment>
      <wfw:commentRss>http://offroadcoder.com/SyndicationService.asmx/GetEntryCommentsRss?guid=f6cca0b0-5d91-48bf-a644-338e2b781d30</wfw:commentRss>
      <slash:comments>7</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
Enterprise applications store their data in a relational database. Our code reads
the data stored in tables with many complex joins and business rule laden queries.
We take the results of those queries and construct an equally complex business entity
that is used by our application logic. Most developers, myself excluded, hate working
with the database. Writing, modifying, or even seeing T-SQL causes some developers
to itch. LINQ to SQL serves as a partially effective Hydrocortisone to relieve the
itch. But they still need to maintain the schema, write SQL-mindful LINQ queries,
and deal with the constant DataContext updates. 
</p>
        <p>
  
</p>
        <p>
Imagine a world where you no longer need to translate your complex business entities
to and from relational tables.  A world where there is no database backing store.
A world where we create our business entities and store them in memory. Even better,
in memory on a shared resource. Does it sound like an inconceivable futuristic developer
heaven? Well it probably is, but this is really cool stuff in the works. 
</p>
        <p>
  
</p>
        <p>
Enter the Microsoft project code-named "<a href="http://msdn.microsoft.com/en-us/data/cc655792.aspx">Velocity</a>."
The blurb on the overview page reads: 
</p>
        <blockquote>
          <p>
            <font face="Arial Rounded MT Bold" color="#000080" size="2">"Velocity" is a distributed
in-memory application cache platform for developing scalable, high-performance applications.
"Velocity" can be used to cache any CLR object and provides access through simple
APIs. The primary goals for "Velocity" are performance, scalability and availability.</font>
          </p>
        </blockquote>
        <p>
I have been working with the <a href="http://digipede.net">Digipede Network</a>, the
leading grid computing software solution, for a few months. The Velocity architecture
sounds remarkably similar to Digipede's. I have seen the great benefits of the Digipede
Network and have high expectations for Velocity.
</p>
        <p>
The Digipede Network, for those of you that haven't seen it yet, consists of a central
Digipede Server and one or many Digipede Agents. The server receives client requests
and assigns tasks to the agents. The client uses the Digipede API to communicate with
the server. The API pretty much wraps client-to-server and server-to-client WSE2 web
service calls. This architecture allows you to take almost any CPU-intensive process
and spread the workload among tens or hundreds of commodity or server grade machines.
The result is a very high performing and easily scaled system with few code changes
from what you do today. 
</p>
        <p>
Digipede Network Diagram:
</p>
        <p>
          <a href="http://www.digipede.net/products/technology.html" target="_blank">
            <img height="453" alt="Digipede Network Diagram" src="http://www.digipede.net/images/how_it_works.gif" width="518" border="0" />
          </a>
        </p>
        <p>
Digipede only works in this configuration, while <a href="http://msdn.microsoft.com/en-us/library/cc645013.aspx" target="_blank">Velocity
has two proposed deployment models</a>. You can have a "caching tier", similar to
Digipede's Server and Agent configuration, or you can house Velocity as a Caching
Service directly in IIS7. I don't know how communications will be handled between
the client API and the "caching tier", but I assume it will be some sort of service
calls (WCF perhaps). All CLR objects stored in the Velocity cache must be marked [Serializable]
just as task worker classes must be to work with Digipede.
</p>
        <p>
The Velocity API looks simple enough too. It exposes intuitive Get() and Put() methods
where you call the cache by name. I can see how versioning of the cached objects might
get tricky. Your application will also need a new configSection that specifies the
deployment mode, locality, and also contains the list of cache hosts. As this is a
distributed solution, the standard virtual machine playground doesn't work too well
to really test this out.
</p>
        <p>
This looks promising, and I'll be following the progress of the project closely.
</p>
        <p>
          <a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=B24C3708-EEFF-4055-A867-19B5851E7CD2" target="_blank">Download
Velocity</a>
        </p>
        <p>
          <a href="http://code.msdn.microsoft.com/velocity" target="_blank">Download the Velocity
CPT 1 samples</a>
        </p>
        <img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=f6cca0b0-5d91-48bf-a644-338e2b781d30" />
      </body>
      <title>Performance = Data Mass x Velocity ^2</title>
      <guid isPermaLink="false">http://offroadcoder.com/PermaLink,guid,f6cca0b0-5d91-48bf-a644-338e2b781d30.aspx</guid>
      <link>http://offroadcoder.com/2008/06/29/PerformanceDataMassXVelocity2.aspx</link>
      <pubDate>Sun, 29 Jun 2008 04:11:00 GMT</pubDate>
      <description>&lt;p&gt;
Enterprise applications store their data in a relational database. Our code reads
the data stored in tables with many complex joins and business rule laden queries.
We take the results of those queries and construct an equally complex business entity
that is used by our application logic. Most developers, myself excluded, hate working
with the database. Writing, modifying, or even seeing T-SQL causes some developers
to itch. LINQ to SQL serves as a partially effective Hydrocortisone to relieve the
itch. But they still need to maintain the schema, write SQL-mindful LINQ queries,
and deal with the constant DataContext updates. 
&lt;p&gt;
&amp;nbsp; 
&lt;p&gt;
Imagine a world where you no longer need to translate your complex business entities
to and from relational tables.&amp;nbsp; A world where there is no database backing store.
A world where we create our business entities and store them in memory. Even better,
in memory on a shared resource. Does it sound like an inconceivable futuristic developer
heaven? Well it probably is, but this is really cool stuff in the works. 
&lt;p&gt;
&amp;nbsp; 
&lt;p&gt;
Enter the Microsoft project code-named "&lt;a href="http://msdn.microsoft.com/en-us/data/cc655792.aspx"&gt;Velocity&lt;/a&gt;."
The blurb on the overview page reads: &lt;blockquote&gt; 
&lt;p&gt;
&lt;font face="Arial Rounded MT Bold" color=#000080 size=2&gt;"Velocity" is a distributed
in-memory application cache platform for developing scalable, high-performance applications.
"Velocity" can be used to cache any CLR object and provides access through simple
APIs. The primary goals for "Velocity" are performance, scalability and availability.&lt;/font&gt;
&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;
I have been working with the &lt;a href="http://digipede.net"&gt;Digipede Network&lt;/a&gt;, the
leading grid computing software solution, for a few months. The Velocity architecture
sounds remarkably similar to Digipede's. I have seen the great benefits of the Digipede
Network and have high expectations for Velocity.
&lt;/p&gt;
&lt;p&gt;
The Digipede Network, for those of you that haven't seen it yet, consists of a central
Digipede Server and one or many Digipede Agents. The server receives client requests
and assigns tasks to the agents. The client uses the Digipede API to communicate with
the server. The API pretty much wraps client-to-server and server-to-client WSE2 web
service calls. This architecture allows you to take almost any CPU-intensive process
and spread the workload among tens or hundreds of commodity or server grade machines.
The result is a very high performing and easily scaled system with few code changes
from what you do today. 
&lt;/p&gt;
&lt;p&gt;
Digipede Network Diagram:
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.digipede.net/products/technology.html" target=_blank&gt;&lt;img height=453 alt="Digipede Network Diagram" src="http://www.digipede.net/images/how_it_works.gif" width=518 border=0&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
Digipede only works in this configuration, while &lt;a href="http://msdn.microsoft.com/en-us/library/cc645013.aspx" target=_blank&gt;Velocity
has two proposed deployment models&lt;/a&gt;. You can have a "caching tier", similar to
Digipede's Server and Agent configuration, or you can house Velocity as a Caching
Service directly in IIS7. I don't know how communications will be handled between
the client API and the "caching tier", but I assume it will be some sort of service
calls (WCF perhaps). All CLR objects stored in the Velocity cache must be marked [Serializable]
just as task worker classes must be to work with Digipede.
&lt;/p&gt;
&lt;p&gt;
The Velocity API looks simple enough too. It exposes intuitive Get() and Put() methods
where you call the cache by name. I can see how versioning of the cached objects might
get tricky. Your application will also need a new configSection that specifies the
deployment mode, locality, and also contains the list of cache hosts. As this is a
distributed solution, the standard virtual machine playground doesn't work too well
to really test this out.
&lt;/p&gt;
&lt;p&gt;
This looks promising, and I'll be following the progress of the project closely.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=B24C3708-EEFF-4055-A867-19B5851E7CD2" target=_blank&gt;Download
Velocity&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://code.msdn.microsoft.com/velocity" target=_blank&gt;Download the Velocity
CPT 1 samples&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=f6cca0b0-5d91-48bf-a644-338e2b781d30" /&gt;</description>
      <comments>http://offroadcoder.com/CommentView,guid,f6cca0b0-5d91-48bf-a644-338e2b781d30.aspx</comments>
      <category>.NET Framework</category>
      <category>C#</category>
      <category>Database</category>
    </item>
    <item>
      <trackback:ping>http://offroadcoder.com/Trackback.aspx?guid=3d7a84f1-e8c6-4e97-ba3d-14a8d332e594</trackback:ping>
      <pingback:server>http://offroadcoder.com/pingback.aspx</pingback:server>
      <pingback:target>http://offroadcoder.com/PermaLink,guid,3d7a84f1-e8c6-4e97-ba3d-14a8d332e594.aspx</pingback:target>
      <dc:creator>Scott Klueppel</dc:creator>
      <georss:point>30.109017 -81.497099</georss:point>
      <wfw:comment>http://offroadcoder.com/CommentView,guid,3d7a84f1-e8c6-4e97-ba3d-14a8d332e594.aspx</wfw:comment>
      <wfw:commentRss>http://offroadcoder.com/SyndicationService.asmx/GetEntryCommentsRss?guid=3d7a84f1-e8c6-4e97-ba3d-14a8d332e594</wfw:commentRss>
      <slash:comments>1</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
A few months after SQL 2005 was released and hit the productions servers, some people
started experiencing some odd behavior in their stored procedures. Simple stored procedures
that normally return in 0 seconds would take upwards of a minute to return. Even more
strange was the fact that the same query, outside of a stored procedure, would still
return in 0 seconds.
</p>
        <p>
It never affected me personally... until today. Three years late to the party. It's
funny how much more interested I am in the causes and solutions for this apparent
problem when it affects me. "Parameter Sniffing" is the term Microsoft uses to describe
the feature that causes this odd behavior. While it appeared as an issue when I encountered
it today, I found that the feature is not only well-intentioned but quite useful.
</p>
        <p>
The execution plan is generated and cached the first time your stored procedure is
called. When the execution plan is being created, SQL Server reads the input parameters
and uses them to optimize the execution plan for those parameters. This is called
"parameter sniffing." If the input parameters used in the first call to the stored
procedure are atypical for the overall use of the stored procedure, a less than ideal
execution plan will be cached for all subsequent calls.
</p>
        <p>
Simply dropping and recompiling the stored procedure does not seem to affect the cached
execution plan. Updating statistics on the tables used in the stored procedure will
cause the execution plan to be regenerated on the next call of the stored procedure.
However, if the same or similar atypical parameters are used on the first execution
of the stored procedure, an equally sub-optimal execution plan will be cached.
</p>
        <p>
You can turn off parameter sniffing. This is accomplished by assigning the input parameter
values to local variables inside the stored procedure and then using the local variables
within the stored procedure. When the execution plan is created, SQL Server will look
at the table statistics to optimize the query for the "average" use. It does this
by looking at the tables used in the query and analyzing row counts, etc. to find
a reasonable plan that will likely suit a majority of situations.
</p>
        <p>
My stored procedure was bringing back multiple resultsets to be used to create a hierarchical
structure in code. It works essentially like the following:
</p>
        <p>
          <font color="#003300">
            <font color="#0000ff">CREATE PROCEDURE</font> [dbo].[usp_Order_GetOrderDetails] 
<br />
( 
<br />
   @StartOrderId <font color="#0000ff">INT</font>, 
<br />
   @EndOrderId <font color="#0000ff">INT</font><br />
) 
<br /><font color="#0000ff">AS 
<br />
BEGIN</font><br />
   <font color="#0000ff">SELECT</font> * 
<br />
   <font color="#0000ff">FROM</font> Order 
<br />
   <font color="#0000ff">WHERE</font> OrderId <font color="#a9a9a9">BETWEEN</font> @StartOrderId <font color="#a9a9a9">AND</font> @EndOrderId 
<br />
  
<br />
   <font color="#0000ff">SELECT</font> * 
<br />
   <font color="#0000ff">FROM</font> OrderLineItem 
<br />
   <font color="#0000ff">WHERE</font> OrderId <font color="#a9a9a9">BETWEEN</font> @StartOrderId <font color="#a9a9a9">AND</font> @EndOrderId 
<br /></font>
          <font color="#0000ff">END</font>
        </p>
        <p>
I was testing the stored procedure for full day using the same ID for @StartOrderId
and @EndOrderId. Since the intended use of this stored procedure is almost always
@EndOrderId = @StartOrderId + 1000, this makes a big difference when calculating the
estimate number of rows returned. I forced SQL Server to assume that my execution
plan should be based on an ID range of 1 instead of 1000. Turning off parameter sniffing
lessens these effects.
</p>
        <p>
To turn off parameter sniffing, it would look like this:
</p>
        <p>
          <font color="#0000ff">CREATE PROCEDURE</font> [dbo].[usp_Order_GetOrderDetails] 
<br />
( 
<br />
   @StartOrderId <font color="#0000ff">INT</font>, 
<br />
   @EndOrderId <font color="#0000ff">INT</font><br />
) 
<br /><font color="#0000ff">AS 
<br />
BEGIN 
<br />
   DECLARE <font color="#000000">@Start</font> INT 
<br />
   DECLARE <font color="#000000">@End</font> INT 
<br />
   SET <font color="#000000">@Start = @StartOrderId</font><br />
   SET <font color="#000000">@End = @EndOrderId</font><br />
 </font><br />
   <font color="#0000ff">SELECT</font> * 
<br />
   <font color="#0000ff">FROM</font> Order 
<br />
   <font color="#0000ff">WHERE</font> OrderId <font color="#a9a9a9">BETWEEN</font> @Start <font color="#a9a9a9">AND</font> @End 
<br />
  
<br />
   <font color="#0000ff">SELECT</font> * 
<br />
   <font color="#0000ff">FROM</font> OrderLineItem 
<br />
   <font color="#0000ff">WHERE</font> OrderId <font color="#a9a9a9">BETWEEN</font> @Start <font color="#a9a9a9">AND</font> @End 
<br /><font color="#0000ff">END</font></p>
        <p>
This immediately improved the performance of my stored procedure. The time to complete
reduced from ~2 minutes to ~2 seconds for my typical 1000 ID range (I know 2 seconds
is a lot, but these tables have millions and millions of rows). But only one piece
of code in the application calls this stored procedure, and 99 out of 100 times it
will have a range of 1000 IDs. Why would I want SQL Server to guess how many Orders
I will typically bring back when I know the exact number? 
</p>
        <p>
I should have the optimal execution plan if I update statistics on Order and OrderLineItem,
and then call usp_Order_GetOrderDetails 1, 1000 after I compile this stored procedure.
This sounds like a lot of work to me, and I did not notice any performance boost by
doing this. I chose to leave parameter sniffing off. 
</p>
        <p>
The only drawbacks to turning off parameter sniffing is the weird looking SQL and
the inevitable questions during code review about the crazy input parameter to variable
mapping. But when you school the doubters on the causes and effects of parameter sniffing,
it will put another notch in your guru stick.
</p>
        <p>
From what I have read, this was not a new feature in SQL 2005. I can't, however, find
any mention of it in SQL 2000 books online, and this feature never showed its face
in SQL 2000.
</p>
        <img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=3d7a84f1-e8c6-4e97-ba3d-14a8d332e594" />
      </body>
      <title>Parameter Sniffing in SQL 2005</title>
      <guid isPermaLink="false">http://offroadcoder.com/PermaLink,guid,3d7a84f1-e8c6-4e97-ba3d-14a8d332e594.aspx</guid>
      <link>http://offroadcoder.com/2008/06/22/ParameterSniffingInSQL2005.aspx</link>
      <pubDate>Sun, 22 Jun 2008 02:24:02 GMT</pubDate>
      <description>&lt;p&gt;
A few months after SQL 2005 was released and hit the productions servers, some people
started experiencing some odd behavior in their stored procedures. Simple stored procedures
that normally return in 0 seconds would take upwards of a minute to return. Even more
strange was the fact that the same query, outside of a stored procedure, would still
return in 0 seconds.
&lt;/p&gt;
&lt;p&gt;
It never affected me personally... until today. Three years late to the party. It's
funny how much more interested I am in the causes and solutions for this apparent
problem when it affects me. "Parameter Sniffing" is the term Microsoft uses to describe
the feature that causes this odd behavior. While it appeared as an issue when I encountered
it today, I found that the feature is not only well-intentioned but quite useful.
&lt;/p&gt;
&lt;p&gt;
The execution plan is generated and cached the first time your stored procedure is
called. When the execution plan is being created, SQL Server reads the input parameters
and uses them to optimize the execution plan for those parameters. This is called
"parameter sniffing." If the input parameters used in the first call to the stored
procedure are atypical for the overall use of the stored procedure, a less than ideal
execution plan will be cached for all subsequent calls.
&lt;/p&gt;
&lt;p&gt;
Simply dropping and recompiling the stored procedure does not seem to affect the cached
execution plan. Updating statistics on the tables used in the stored procedure will
cause the execution plan to be regenerated on the next call of the stored procedure.
However, if the same or similar atypical parameters are used on the first execution
of the stored procedure, an equally sub-optimal execution plan will be cached.
&lt;/p&gt;
&lt;p&gt;
You can turn off parameter sniffing. This is accomplished by assigning the input parameter
values to local variables inside the stored procedure and then using the local variables
within the stored procedure. When the execution plan is created, SQL Server will look
at the table statistics to optimize the query for the "average" use. It does this
by looking at the tables used in the query and analyzing row counts, etc. to find
a reasonable plan that will likely suit a majority of situations.
&lt;/p&gt;
&lt;p&gt;
My stored procedure was bringing back multiple resultsets to be used to create a hierarchical
structure in code. It works essentially like the following:
&lt;/p&gt;
&lt;p&gt;
&lt;font color=#003300&gt;&lt;font color=#0000ff&gt;CREATE PROCEDURE&lt;/font&gt; [dbo].[usp_Order_GetOrderDetails] 
&lt;br&gt;
( 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; @StartOrderId &lt;font color=#0000ff&gt;INT&lt;/font&gt;, 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; @EndOrderId &lt;font color=#0000ff&gt;INT&lt;/font&gt;
&lt;br&gt;
) 
&lt;br&gt;
&lt;font color=#0000ff&gt;AS 
&lt;br&gt;
BEGIN&lt;/font&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;SELECT&lt;/font&gt; * 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;FROM&lt;/font&gt; Order 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;WHERE&lt;/font&gt; OrderId &lt;font color=#a9a9a9&gt;BETWEEN&lt;/font&gt; @StartOrderId &lt;font color=#a9a9a9&gt;AND&lt;/font&gt; @EndOrderId 
&lt;br&gt;
&amp;nbsp; 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;SELECT&lt;/font&gt; * 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;FROM&lt;/font&gt; OrderLineItem 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;WHERE&lt;/font&gt; OrderId &lt;font color=#a9a9a9&gt;BETWEEN&lt;/font&gt; @StartOrderId &lt;font color=#a9a9a9&gt;AND&lt;/font&gt; @EndOrderId 
&lt;br&gt;
&lt;/font&gt;&lt;font color=#0000ff&gt;END&lt;/font&gt; 
&lt;/p&gt;
&lt;p&gt;
I was testing the stored procedure for full day using the same ID for @StartOrderId
and @EndOrderId. Since the intended use of this stored procedure is almost always
@EndOrderId = @StartOrderId + 1000, this makes a big difference when calculating the
estimate number of rows returned. I forced SQL Server to assume that my execution
plan should be based on an ID range of 1 instead of 1000. Turning off parameter sniffing
lessens these effects.
&lt;/p&gt;
&lt;p&gt;
To turn off parameter sniffing, it would look like this:
&lt;/p&gt;
&lt;p&gt;
&lt;font color=#0000ff&gt;CREATE PROCEDURE&lt;/font&gt; [dbo].[usp_Order_GetOrderDetails] 
&lt;br&gt;
( 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; @StartOrderId &lt;font color=#0000ff&gt;INT&lt;/font&gt;, 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; @EndOrderId &lt;font color=#0000ff&gt;INT&lt;/font&gt;
&lt;br&gt;
) 
&lt;br&gt;
&lt;font color=#0000ff&gt;AS 
&lt;br&gt;
BEGIN 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; DECLARE &lt;font color=#000000&gt;@Start&lt;/font&gt; INT 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; DECLARE &lt;font color=#000000&gt;@End&lt;/font&gt; INT 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; SET &lt;font color=#000000&gt;@Start = @StartOrderId&lt;/font&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp; SET &lt;font color=#000000&gt;@End = @EndOrderId&lt;/font&gt;
&lt;br&gt;
&amp;nbsp;&lt;/font&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;SELECT&lt;/font&gt; * 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;FROM&lt;/font&gt; Order 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;WHERE&lt;/font&gt; OrderId &lt;font color=#a9a9a9&gt;BETWEEN&lt;/font&gt; @Start &lt;font color=#a9a9a9&gt;AND&lt;/font&gt; @End 
&lt;br&gt;
&amp;nbsp; 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;SELECT&lt;/font&gt; * 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;FROM&lt;/font&gt; OrderLineItem 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;WHERE&lt;/font&gt; OrderId &lt;font color=#a9a9a9&gt;BETWEEN&lt;/font&gt; @Start &lt;font color=#a9a9a9&gt;AND&lt;/font&gt; @End 
&lt;br&gt;
&lt;font color=#0000ff&gt;END&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
This immediately improved the performance of my stored procedure. The time to complete
reduced from ~2 minutes to ~2 seconds for my typical 1000 ID range (I know 2 seconds
is a lot, but these tables have millions and millions of rows). But only one piece
of code in the application calls this stored procedure, and 99 out of 100 times it
will have a range of 1000 IDs. Why would I want SQL Server to guess how many Orders
I will typically bring back when I know the exact number? 
&lt;/p&gt;
&lt;p&gt;
I should have the optimal execution plan if I update statistics on Order and OrderLineItem,
and then call usp_Order_GetOrderDetails 1, 1000 after I compile this stored procedure.
This sounds like a lot of work to me, and I did not notice any performance boost by
doing this. I chose to leave parameter sniffing off. 
&lt;/p&gt;
&lt;p&gt;
The only drawbacks to turning off parameter sniffing is the weird looking SQL and
the inevitable questions during code review about the crazy input parameter to variable
mapping. But when you school the doubters on the causes and effects of parameter sniffing,
it will put another notch in your guru stick.
&lt;/p&gt;
&lt;p&gt;
From what I have read, this was not a new feature in SQL 2005. I can't, however, find
any mention of it in SQL 2000 books online, and this feature never showed its face
in SQL 2000.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=3d7a84f1-e8c6-4e97-ba3d-14a8d332e594" /&gt;</description>
      <comments>http://offroadcoder.com/CommentView,guid,3d7a84f1-e8c6-4e97-ba3d-14a8d332e594.aspx</comments>
      <category>Database</category>
      <category>SQL</category>
    </item>
    <item>
      <trackback:ping>http://offroadcoder.com/Trackback.aspx?guid=db20d8db-df16-40af-b081-448affaeaf9d</trackback:ping>
      <pingback:server>http://offroadcoder.com/pingback.aspx</pingback:server>
      <pingback:target>http://offroadcoder.com/PermaLink,guid,db20d8db-df16-40af-b081-448affaeaf9d.aspx</pingback:target>
      <dc:creator>Scott Klueppel</dc:creator>
      <georss:point>30.109017 -81.497099</georss:point>
      <wfw:comment>http://offroadcoder.com/CommentView,guid,db20d8db-df16-40af-b081-448affaeaf9d.aspx</wfw:comment>
      <wfw:commentRss>http://offroadcoder.com/SyndicationService.asmx/GetEntryCommentsRss?guid=db20d8db-df16-40af-b081-448affaeaf9d</wfw:commentRss>
      <slash:comments>6</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
          <a href="http://blogs.msdn.com/charlie/archive/2007/11/11/partial-methods.aspx">Here</a> is
a fantastic solution to a common <a href="http://en.wikipedia.org/wiki/Object_relational_mapper">ORMr</a> problem
seen when regenerating code that overwrites changes made to previously generated and
more recently manually-modified code.
</p>
        <img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=db20d8db-df16-40af-b081-448affaeaf9d" />
      </body>
      <title>Partial Methods</title>
      <guid isPermaLink="false">http://offroadcoder.com/PermaLink,guid,db20d8db-df16-40af-b081-448affaeaf9d.aspx</guid>
      <link>http://offroadcoder.com/2007/12/18/PartialMethods.aspx</link>
      <pubDate>Tue, 18 Dec 2007 03:04:16 GMT</pubDate>
      <description>&lt;p&gt;
&lt;a href="http://blogs.msdn.com/charlie/archive/2007/11/11/partial-methods.aspx"&gt;Here&lt;/a&gt;&amp;nbsp;is
a fantastic solution to a common &lt;a href="http://en.wikipedia.org/wiki/Object_relational_mapper"&gt;ORMr&lt;/a&gt; problem
seen when regenerating code that overwrites changes made to previously generated and
more recently manually-modified code.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=db20d8db-df16-40af-b081-448affaeaf9d" /&gt;</description>
      <comments>http://offroadcoder.com/CommentView,guid,db20d8db-df16-40af-b081-448affaeaf9d.aspx</comments>
      <category>.NET Framework</category>
      <category>C#</category>
      <category>Database</category>
    </item>
    <item>
      <trackback:ping>http://offroadcoder.com/Trackback.aspx?guid=00fc7c82-6dbd-4def-99ea-28a1eb7303b4</trackback:ping>
      <pingback:server>http://offroadcoder.com/pingback.aspx</pingback:server>
      <pingback:target>http://offroadcoder.com/PermaLink,guid,00fc7c82-6dbd-4def-99ea-28a1eb7303b4.aspx</pingback:target>
      <dc:creator>Scott Klueppel</dc:creator>
      <georss:point>30.109017 -81.497099</georss:point>
      <wfw:commentRss>http://offroadcoder.com/SyndicationService.asmx/GetEntryCommentsRss?guid=00fc7c82-6dbd-4def-99ea-28a1eb7303b4</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
It's only a week away, and there are still spaces left.
</p>
        <p>
          <a href="http://codecamp07.jaxdug.com/">Information</a>   <a href="http://www.clicktoattend.com/?id=119680">Register</a>    <a href="http://codecamp07.jaxdug.com/Sessions/tabid/72/Default.aspx">Sessions</a></p>
        <img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=00fc7c82-6dbd-4def-99ea-28a1eb7303b4" />
      </body>
      <title>2007 Jacksonville Code Camp</title>
      <guid isPermaLink="false">http://offroadcoder.com/PermaLink,guid,00fc7c82-6dbd-4def-99ea-28a1eb7303b4.aspx</guid>
      <link>http://offroadcoder.com/2007/08/17/2007JacksonvilleCodeCamp.aspx</link>
      <pubDate>Fri, 17 Aug 2007 03:42:12 GMT</pubDate>
      <description>&lt;p&gt;
It's only a week away, and there are still spaces left.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://codecamp07.jaxdug.com/"&gt;Information&lt;/a&gt;&amp;nbsp;&amp;nbsp; &lt;a href="http://www.clicktoattend.com/?id=119680"&gt;Register&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://codecamp07.jaxdug.com/Sessions/tabid/72/Default.aspx"&gt;Sessions&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=00fc7c82-6dbd-4def-99ea-28a1eb7303b4" /&gt;</description>
      <comments>http://offroadcoder.com/CommentView,guid,00fc7c82-6dbd-4def-99ea-28a1eb7303b4.aspx</comments>
      <category>.NET Framework</category>
      <category>AJAX</category>
      <category>ASP.NET</category>
      <category>C#</category>
      <category>Database</category>
      <category>General</category>
      <category>Javascript</category>
    </item>
    <item>
      <trackback:ping>http://offroadcoder.com/Trackback.aspx?guid=4d4e4b7c-411b-4803-a645-255f102b3665</trackback:ping>
      <pingback:server>http://offroadcoder.com/pingback.aspx</pingback:server>
      <pingback:target>http://offroadcoder.com/PermaLink,guid,4d4e4b7c-411b-4803-a645-255f102b3665.aspx</pingback:target>
      <dc:creator>Scott Klueppel</dc:creator>
      <georss:point>30.109017 -81.497099</georss:point>
      <wfw:comment>http://offroadcoder.com/CommentView,guid,4d4e4b7c-411b-4803-a645-255f102b3665.aspx</wfw:comment>
      <wfw:commentRss>http://offroadcoder.com/SyndicationService.asmx/GetEntryCommentsRss?guid=4d4e4b7c-411b-4803-a645-255f102b3665</wfw:commentRss>
      <slash:comments>5</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
I frequently store documents in the database for my ASP.NET apps, eliminating web
farm complications with shared drives, permissions, etc.  When uploading
a file, my Document class reads the uploaded file, zips the file with <a href="http://www.icsharpcode.net/OpenSource/SharpZipLib/">SharpZipLib</a>,
and inserts/updates in the database.   When opening a file, I have always
used an ASPX page that uses the Document class to unzip the file, and then changes
the Content-Disposition and ContentType headers, and then does a BinaryWrite
to the Response object to display the file.
</p>
        <p>
I have been using Handlers a lot lately, and figured that it was time to make this
process a little more elegant.  If you've never written a handler, it's quite
simple.  You need to make a web.config change, and add a new class that implementts
IHttpHandler.  All of the work is done in ProcessRequest.  Using the
default .ashx extension for the handler eliminates the need to make any changes in
IIS.  I thought about changing the handler to accept all requests with known
file extensions with the document ID as the filename, like 3383.pdf.  I just
figured that using the default extnesion would be easier.   Laziness
or efficiency, you decide.  Check out the code.
</p>
        <p>
          <strong>In &lt;system.web&gt; in web.config: 
<hr /></strong>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
              <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
                <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
                  <font color="#0000ff" size="2">&lt;</font>
                  <font color="#800000" size="2">httpHandlers</font>
                  <font color="#0000ff" size="2">&gt;</font>
                  <font size="2">
                    <br />
   </font>
                  <font color="#0000ff" size="2">&lt;</font>
                  <font color="#800000" size="2">add</font>
                  <font color="#ff00ff" size="2">
                  </font>
                  <font color="#ff0000" size="2">verb</font>
                  <font color="#0000ff" size="2">="*"</font>
                  <font color="#ff00ff" size="2">
                  </font>
                  <font color="#ff0000" size="2">path</font>
                  <font color="#0000ff" size="2">="DocumentHandler.ashx"</font>
                  <font color="#ff00ff" size="2">
                  </font>
                  <font color="#ff0000" size="2">type</font>
                  <font color="#0000ff" size="2">="TestingWebApp.DocumentHandler,
TestingWebApp"</font>
                  <font color="#ff00ff" size="2">
                  </font>
                  <font color="#0000ff" size="2">/&gt; </font>
                  <font size="2">
                    <br />
                  </font>
                  <font color="#0000ff" size="2">&lt;/</font>
                  <font color="#800000" size="2">httpHandlers</font>
                  <font color="#0000ff" size="2">&gt; 
<p></p></font>
                </span>
              </span>
            </span>
            <p>
              <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
                <font face="Verdana" size="2">
                  <strong>DocumentHandler.cs: 
<hr /></strong>
                </font>
              </span>
              <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
                <font face="Verdana" size="2">
                  <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
                    <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">using</span> System;<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">using</span> System.Web;<br /><br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">namespace</span> TestingWebApp<br />
{<br />
    <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">class</span> DocumentHandler
: IHttpHandler<br />
    {<br />
        <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">private</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">int</span> DocumentId 
<br />
        {<br />
            get<br />
            {<br />
                <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">if</span>(System.Web.HttpContext.Current.Request.QueryString[<span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"DocumentId"</span>]
!<span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">null</span> &amp;&amp;
System.Web.HttpContext.Current.Request.QueryString[<span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"DocumentId"</span>].ToString().Length
&gt; 0) 
<br />
                    <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">return</span> Convert.ToInt32(System.Web.HttpContext.Current.Request.QueryString[<span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"DocumentId"</span>]);<br />
                <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">else</span><br />
                    <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">throw</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">new</span> ApplicationException(<span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"Document
Handler requires a DocumentId"</span>);<br />
            }<br />
        }<br /><br />
        <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">#region</span> IHttpHandler
Members<br /><br />
        <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">void</span> ProcessRequest(System.Web.HttpContext
context)<br />
        {<br />
            context.Response.Cache.SetCacheability(HttpCacheability.Public);<br />
            context.Response.BufferOutput <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">false</span>;<br />
            <br />
            Document document <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> Document.GetDocumentByDocumentId(<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">this</span>.DocumentId);<br /><br />
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">byte</span>[]
buffer <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> document.UnzippedBinary;<br />
            context.Response.ContentType <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> document.ContentType;<br />
            context.Response.OutputStream.Write(buffer,
0, buffer.Length);<br />
        }<br /><br />
        <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">bool</span> IsReusable<br />
        {<br />
            get { <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">return</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">true</span>;
}<br />
        }<br /><br />
        <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">#endregion</span><br />
    }<br />
}</span>
                </font>
              </span>
            </p>
          </span>
          <img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=4d4e4b7c-411b-4803-a645-255f102b3665" />
        </p>
      </body>
      <title>ASP.NET Document Handler</title>
      <guid isPermaLink="false">http://offroadcoder.com/PermaLink,guid,4d4e4b7c-411b-4803-a645-255f102b3665.aspx</guid>
      <link>http://offroadcoder.com/2006/11/07/ASPNETDocumentHandler.aspx</link>
      <pubDate>Tue, 07 Nov 2006 04:31:15 GMT</pubDate>
      <description>&lt;p&gt;
I frequently store documents in the database for my ASP.NET apps, eliminating web
farm complications with shared drives, permissions, etc.&amp;nbsp;&amp;nbsp;When uploading
a file, my Document class reads the uploaded file, zips the file with &lt;a href="http://www.icsharpcode.net/OpenSource/SharpZipLib/"&gt;SharpZipLib&lt;/a&gt;,
and inserts/updates in the database.&amp;nbsp;&amp;nbsp; When opening a file, I have always
used an ASPX&amp;nbsp;page that uses the Document class to unzip the file, and then changes
the Content-Disposition and ContentType headers,&amp;nbsp;and then does a BinaryWrite
to the Response object to display the file.
&lt;/p&gt;
&lt;p&gt;
I have been using Handlers a lot lately, and figured that it was time to make this
process a little more elegant.&amp;nbsp; If you've never written a handler, it's quite
simple.&amp;nbsp; You need to make a web.config change, and add a new class that implementts
IHttpHandler.&amp;nbsp; All of the work is done in ProcessRequest.&amp;nbsp;&amp;nbsp;Using the
default .ashx extension for the handler eliminates the need to make any changes in
IIS.&amp;nbsp; I thought about changing the handler to accept all requests with known
file extensions with the document ID as the filename, like 3383.pdf.&amp;nbsp; I just
figured that&amp;nbsp;using the default extnesion&amp;nbsp;would be easier.&amp;nbsp;&amp;nbsp; Laziness
or efficiency, you decide.&amp;nbsp; Check out the code.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;In &amp;lt;system.web&amp;gt; in web.config: 
&lt;hr&gt;
&lt;/strong&gt;&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;font color=#0000ff size=2&gt;&amp;lt;&lt;/font&gt;&lt;font color=#800000 size=2&gt;httpHandlers&lt;/font&gt;&lt;font color=#0000ff size=2&gt;&amp;gt;&lt;/font&gt;&lt;font size=2&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color=#0000ff size=2&gt;&amp;lt;&lt;/font&gt;&lt;font color=#800000 size=2&gt;add&lt;/font&gt;&lt;font color=#ff00ff size=2&gt; &lt;/font&gt;&lt;font color=#ff0000 size=2&gt;verb&lt;/font&gt;&lt;font color=#0000ff size=2&gt;="*"&lt;/font&gt;&lt;font color=#ff00ff size=2&gt; &lt;/font&gt;&lt;font color=#ff0000 size=2&gt;path&lt;/font&gt;&lt;font color=#0000ff size=2&gt;="DocumentHandler.ashx"&lt;/font&gt;&lt;font color=#ff00ff size=2&gt; &lt;/font&gt;&lt;font color=#ff0000 size=2&gt;type&lt;/font&gt;&lt;font color=#0000ff size=2&gt;="TestingWebApp.DocumentHandler,
TestingWebApp"&lt;/font&gt;&lt;font color=#ff00ff size=2&gt; &lt;/font&gt;&lt;font color=#0000ff size=2&gt;/&amp;gt; &lt;/font&gt;&lt;font size=2&gt;
&lt;br&gt;
&lt;/font&gt;&lt;font color=#0000ff size=2&gt;&amp;lt;/&lt;/font&gt;&lt;font color=#800000 size=2&gt;httpHandlers&lt;/font&gt;&lt;font color=#0000ff size=2&gt;&amp;gt; 
&lt;p&gt;
&lt;/p&gt;
&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;font face=Verdana size=2&gt;&lt;strong&gt;DocumentHandler.cs: 
&lt;hr&gt;
&lt;/strong&gt;&lt;/font&gt;&lt;/span&gt;&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;font face=Verdana size=2&gt;&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;using&lt;/span&gt; System;&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;using&lt;/span&gt; System.Web;&lt;br&gt;
&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;namespace&lt;/span&gt; TestingWebApp&lt;br&gt;
{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;public&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;class&lt;/span&gt; DocumentHandler
: IHttpHandler&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;private&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;int&lt;/span&gt; DocumentId 
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;if&lt;/span&gt;(System.Web.HttpContext.Current.Request.QueryString[&lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"DocumentId"&lt;/span&gt;]
!&lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;null&lt;/span&gt; &amp;amp;&amp;amp;
System.Web.HttpContext.Current.Request.QueryString[&lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"DocumentId"&lt;/span&gt;].ToString().Length
&amp;gt; 0) 
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;return&lt;/span&gt; Convert.ToInt32(System.Web.HttpContext.Current.Request.QueryString[&lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"DocumentId"&lt;/span&gt;]);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;else&lt;/span&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;throw&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;new&lt;/span&gt; ApplicationException(&lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"Document
Handler requires a DocumentId"&lt;/span&gt;);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;#region&lt;/span&gt; IHttpHandler
Members&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;public&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;void&lt;/span&gt; ProcessRequest(System.Web.HttpContext
context)&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;context.Response.Cache.SetCacheability(HttpCacheability.Public);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;context.Response.BufferOutput &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;false&lt;/span&gt;;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Document document &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; Document.GetDocumentByDocumentId(&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;this&lt;/span&gt;.DocumentId);&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;byte&lt;/span&gt;[]
buffer &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; document.UnzippedBinary;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;context.Response.ContentType &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; document.ContentType;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;context.Response.OutputStream.Write(buffer,
0, buffer.Length);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;public&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;bool&lt;/span&gt; IsReusable&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get { &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;return&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;true&lt;/span&gt;;
}&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;#endregion&lt;/span&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;
}&lt;/span&gt;&lt;/font&gt;&lt;/span&gt;
&lt;/span&gt;&lt;img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=4d4e4b7c-411b-4803-a645-255f102b3665" /&gt;</description>
      <comments>http://offroadcoder.com/CommentView,guid,4d4e4b7c-411b-4803-a645-255f102b3665.aspx</comments>
      <category>.NET Framework</category>
      <category>ASP.NET</category>
      <category>C#</category>
      <category>Database</category>
    </item>
    <item>
      <trackback:ping>http://offroadcoder.com/Trackback.aspx?guid=0901a197-60df-4409-bc23-142122079693</trackback:ping>
      <pingback:server>http://offroadcoder.com/pingback.aspx</pingback:server>
      <pingback:target>http://offroadcoder.com/PermaLink,guid,0901a197-60df-4409-bc23-142122079693.aspx</pingback:target>
      <dc:creator>Scott Klueppel</dc:creator>
      <georss:point>30.109017 -81.497099</georss:point>
      <wfw:commentRss>http://offroadcoder.com/SyndicationService.asmx/GetEntryCommentsRss?guid=0901a197-60df-4409-bc23-142122079693</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
This 2.1 update includes over 60 improvements, including new support for .NET 2.0
and Visual Studio 2005. VistaDB is a small-footprint, embedded SQL database alternative
to Jet/Access, MSDE and SQL Server Express 2005 that enables developers to build .NET
1.1 and .NET 2.0 applications. Features SQL-92 support, small 500KB embedded footprint,
free 2-User VistaDB Server for remote TCP/IP data access, royalty free distribution
for both embedded and server, Copy 'n Go! deployment, managed ADO.NET Provider, data
management and data migration tools. Free trial is available for download.
</p>
        <ul>
          <li>
            <a href="http://www.vistadb.net/overview.asp?ref=blogger">Learn more about VistaDB</a>  
</li>
          <li>
            <a href="http://www.vistadb.net/blogoffer.asp?ref=blogger">Repost this to your blog
and receive a FREE copy of VistaDB 2.1!</a>
          </li>
        </ul>
        <img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=0901a197-60df-4409-bc23-142122079693" />
      </body>
      <title>VistaDB 2.1 database for .NET has been released</title>
      <guid isPermaLink="false">http://offroadcoder.com/PermaLink,guid,0901a197-60df-4409-bc23-142122079693.aspx</guid>
      <link>http://offroadcoder.com/2005/11/09/VistaDB21DatabaseForNETHasBeenReleased.aspx</link>
      <pubDate>Wed, 09 Nov 2005 02:55:01 GMT</pubDate>
      <description>&lt;p&gt;
This 2.1 update includes over 60 improvements, including new support for .NET 2.0
and Visual Studio 2005. VistaDB is a small-footprint, embedded SQL database alternative
to Jet/Access, MSDE and SQL Server Express 2005 that enables developers to build .NET
1.1 and .NET 2.0 applications. Features SQL-92 support, small 500KB embedded footprint,
free 2-User VistaDB Server for remote TCP/IP data access, royalty free distribution
for both embedded and server, Copy 'n Go! deployment, managed ADO.NET Provider, data
management and data migration tools. Free trial is available for download.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://www.vistadb.net/overview.asp?ref=blogger"&gt;Learn more about VistaDB&lt;/a&gt;&amp;nbsp; 
&lt;li&gt;
&lt;a href="http://www.vistadb.net/blogoffer.asp?ref=blogger"&gt;Repost this to your blog
and receive a FREE copy of VistaDB 2.1!&lt;/a&gt; 
&lt;/li&gt;
&lt;/ul&gt;
&lt;img width="0" height="0" src="http://offroadcoder.com/aggbug.ashx?id=0901a197-60df-4409-bc23-142122079693" /&gt;</description>
      <comments>http://offroadcoder.com/CommentView,guid,0901a197-60df-4409-bc23-142122079693.aspx</comments>
      <category>.NET Framework</category>
      <category>C#</category>
      <category>Database</category>
    </item>
  </channel>
</rss>