navigation
 Wednesday, April 11, 2007
A nice blog post highlighting some aspects of Haskell and the new functional elements of C# 3.0 http://themechanicalbride.blogspot.com/2007/04/haskell-for-c-3-programmers.html
posted on April 11, 2007  #    by Adam Anderson  Comments [0] Trackback
 Monday, April 09, 2007

I love the new features of ADO.Net, the Object Datasources and the table adapters that the XSD designer can generate. But they kind of have a will of their own! They open and close connections as they see fit, and dont really care about transactions. If you have a set of related tables that you make changes to, and then want to write all of the changes to the database in one transaction, you need to do things a certain way to make it work...

What about the TransactionScope class?

My first attempt was to use the new TransactionScope functionality in ADO.Net 2.0. You can do something like this:

using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required))
{
   // do stuff that manipulates data
   ts.Complete();
}

While this is a very elegant solution, it is very tricky to get to work. The mechanism uses a Local Transaction Coordinator, but as soon as you execute your second database update command, it escalates the transaction to a distributed transaction (even if you are just doing sequential operations in the same SqlConnection), and tries to do 2 way communication with the DTC (Distributed Transaction Coordinator) running on the SqlServer. This communication turns out to be a major headache to get working. Asides from opening all kinds of firewall holes and configuring lots of complicated DTC options on both the server machine and your local machine, the server needs to be able to do a reverse DNS lookup to call back to your client machine, which proves to be unfeasable if you are developing from home across a VPN or have a dynamic IP address that the server cant lookup. It really only works when your client and the SQL Server are part of the same AD domain.

Long story short, TransactionScope is too complicated right now to actually use in normal development. So I wanted to stick with the traditional transaction support.

Getting the TableAdapters to use the correct Connection object

First of all, you need to make sure all of your TableAdapters use the same Connection object. TableAdapters are not an actual class in ADO.Net, they are code generated classes, kind of like typed DataSets, but unlike typed DataSets they dont inherit from some common TableAdapter class, they just inherit from Component. However, they do expose a Connection property, so I use that to hook up all my TableAdapters to the same Connection object. Something like this:

void InitTableAdapters( SqlConnection conn )
{
  taReportSetGroupAccountNotAssigned.Connection = conn;
  taReportSetGroupAccount.Connection = conn;
  taReportSetGroup.Connection = conn;
  taReportSet.Connection = conn;
}

Setting up the transaction

Now, you can encapsuluate your database operations in a transaction as follows:

SqlConnection conn = new SqlConnection( connStr );

InitTableAdapters( conn );

conn.Open();
SqlTransaction tran = conn.BeginTransaction();
try
{
  // manipulate data

  tran.Commit();
}
catch()
{
  tran.Rollback();
}
finally
{
  conn.Close();
}

However, there is still a problem with this. The SqlCommands inside the TableAdapter need to use the transaction of the Connection object in the Table Adapter, but the generated code does not do that for you. And you don't want to start messing with the generated code, or your changes will be wiped out next time it is regenerated. What to do?

Making the SqlCommand enlist in your transaction

First I tried subclassing the generated TabeAdapter, as I saw there was a protected CommandCollection property. However, it turned out that this collection only contains the Select Command, and I wanted the Insert, Update and Delete commands to participate in the transaction. These are part of the SqlDataAdapter member of the TableAdapter, which is private. Duh!

Well, partial classes come to the rescue! The TableAdapter class is generated as a partial class, which allows you to extend it by adding in additional methods in your own partial class. One gotcha is that you need to use the same namespace for your partial class as for the generated partial class, and that namespace is actually not the same as that of the typed dataset. It has .XXXTableAdapters added on to the end of it, where XXX is the name of your typed dataset. For instance, if your dataset namespace is Falafel.Data, and its name is FalafelDataset, then your TableAdapter namespace will be Falafel.Data.FalafelDatasetTableAdapters.

Armed with this knowledge, we can extend the partial class of each TableAdapter as follows:

namespace Falafel.Data.FalafelDataSetTableAdapters
{
  public partial class ReportSetGroupAccountTableAdapter
  {
    public void JoinTransaction( SqlTransaction tran )
    {  
      TransactionHelper.JoinTransaction(_adapter, tran);
    }
  }
  public partial class ReportSetGroupTableAdapter
  {
    public void JoinTransaction( SqlTransaction tran )
    { 
      TransactionHelper.JoinTransaction(_adapter, tran);
    }
  }
}

Here I am using my own helper class:

public class TransactionHelper
{
  public static void JoinTransaction( SqlAdapter adapter, SqlTransaction tran)
  {
      CheckTransaction(tran, adapter.InsertCommand);
      CheckTransaction(tran, adapter.DeleteCommand);
      CheckTransaction(tran, adapter.UpdateCommand);
      CheckTransaction(tran, adapter.SelectCommand);
  }
  public static void CheckTransaction( SqlAdapter adapter, SqlCommand cmd)
  {
    if (cmd != null)
      cmd.Transaction = tran;
  }
}

Putting it all together

Now that I have injected a public JoinTransaction method into each TableAdapter, I can get them to enlist in the transaction by surreptitiously calling the trojan horse helper:

void EnlistTableAdaptersInTransaction( SqlTransaction tran )
{
  taReportSetGroupAccountNotAssigned.JoinTransaction( tran );
  taReportSetGroupAccount.JoinTransaction( tran );
  taReportSetGroup.JoinTransaction( tran );
  taReportSet.JoinTransaction( tran );   
}

Adding a call to this in my update logic, this is the final sequence of events:

SqlConnection conn = new SqlConnection( connStr );
InitTableAdapters( conn );
conn.Open();
SqlTransaction tran = conn.BeginTransaction();
try
{
  EnlistTableAdaptersInTransaction( tran );
  // manipulate data
  tran.Commit();
}
catch()
{
  tran.Rollback();
}
finally
{
  conn.Close();
}

It's not the most elegant or compact code, but it gets the job done! I hope this will save you some time and allow you to productively use TableAdapters in transactions until the TransactionScope class becomes more development environment friendly!

posted on April 9, 2007  #    by John Waters  Comments [0] Trackback
 Tuesday, April 03, 2007

When I first installed Windows Vista I was a little worried that I would not be able to use all my development tools, like Microsoft Visual Studio 2005 and Microsoft SQL Server Management Studio, so I wanted to install an XP virtual machine just in case. I soon found out that the only version of Microsoft Virtual PC that would run on Vista was a beta of Virtual PC 2007, so I installed that, and it actually worked nicely.

Well, now the release version of Virtual PC 2007 is here. You can download the full version for free from the Microsoft Download Center. I downloaded it and installed it (you need to uninstall the beta version first – don’t worry, your virtual machines are left intact).

VPC 2007 runs on most host Windows versions, all the way back to Windows 98. It can of course host Vista, so this is a great way to start testing Vista and application compatibility in a sandbox environment.

posted on April 3, 2007  #    by John Waters  Comments [0] Trackback
 Wednesday, March 21, 2007

I am guessing a lot of people out there are like me and have projects that they started working on in ASP.Net 1.1, and later migrated to 2.0. This blog is about how to move your old files into the brave new world...

One of the neat features with web forms created from scratch in 2.0 is the separation of the Code Behind files into two partial classes: one partial class in a file called formname.aspx.designer.cs, and one in formname.aspx.cs. The designer file is autogenerated and contains declarations of all the form level variables, for instance if you drop a TextBox tbMyTextBox onto your form, it will add a line

System.Web.UI.WebControls.TextBox tbMyTextBox

to your designer file.

The other Code Behind file is the one where you write your code, event handlers, etc. The two partial class files are then combined at compile time into one class.

So, how do I take an old 1.1 Code Behind file and separate it into the two files I would have had if I had created it in 2.0? Well, follow these steps:

1. Go to the aspx (same applies for asxc files) file, and change the Page directive to say CodeFile="formname.aspx.cs" instead of Codebehind="formname.aspx.cs"

2. Go to your Code Behind file and delete all the member control declarations that should be in the designer file, for instance the tbMyTextBox in my example (be careful not to delete any other private member variables you may have interspersed with the generated code).

3. Still in the old Code Behind file, add partial in front of the class name of the form, for instance

public partial class formname : System.Web.UI.Page

These three steps are sometimes enough for the IDE to kick in and generate your Code Behind file, but if not, you can prod it on its way a little:

4. Create a file called formname.aspx.designer.cs in the same directory as your formnam.aspx.cs, and copy the basic partial class skeleton structure from your code behind (dont include the : System.Web.UI.Page, and use the same namespace as your partial class):

namespace YourNameSpace {

public partial class formname {

   }
}

5. Add this new file to the project (Project, Add Existing Item). It should line up nicely with your Code Behind file under the aspx node in the Solution Explorer.

6. Go back to the aspx file and change the ID of some arbitrary form level control, then hit Save.... change it back and save again. Now look at your empty stub of a partial class again from step 4...TADA! It now contains declarations for all of your form controls (not just the one you changed!).

By the way, if you for some reason want to declare a member control differently from the way the designer autogenerates it, you can just move it from the generated file back into your file and change for instance the visibility of the variable. The generator checks for existing declarations before it adds them to the generated file (hence step 2 above).

I know this is a tad tedious, I wish there was a wizard, but at least you now know how to make the conversion manually.

posted on March 21, 2007  #    by John Waters  Comments [0] Trackback
 Thursday, March 15, 2007

When querying an external data source through a linked server, you have two choices: you can write a typical T-SQL query, specifying the external tables with their four-part name in the FROM clause, or use the OPENQUERY function. The first option has advantages, as long as the external provider supports enough SQL so that SQL Server can take your query and translate it into a query that the external data source can use. On the other hand, if th external provider doesn't support enough SQL for SQL Server to optimize the query, or if the external provider is just too plain buggy, you will have to resort to passing through your own provider-specific queries with OPENQUERY. This gives you a great deal of control over how the external data source is accessed, but comes with a frustrating and puzzling limitation: OPENQUERY requires that the query be a string constant. That means no string variables you built with conditional logic, no expressions that result in a string, and especially no support for parameter substitution!

Fortunately, there is an option besides pulling the entire external table over and then processing it on SQL Server. It's an extremely powerful built-in procedure called sp_executesql. This stored procedure accepts a string containing SQL statements and attempts to execute it. However, with this great power comes great responsibility! Since you are constructing a string of ad-hoc SQL, you must be extremely careful to validate all external input lest you fall victim to a SQL Injection attack. Let's look at a simple example of how to use sp_executesql to create a parameterized OPENQUERY against a linked server named NAVISION.

CREATE PROCEDURE OpenQuery_Customer_ByDate( @date datetime )
AS
set nocount on

declare @stmt nvarchar(max)
set @stmt = 'select * from OPENQUERY( NAVISION, ''select * from Customer where "Last Date Modified" >= ' + convert( char(10), @date, 120 ) + ''' )'

exec sp_executesql @stmt

This procedure illustrates the basic idea of the technique: build a string containing the OPENQUERY query, dynamically building the pass-through query within this string. After the string has been built and passed to sp_executesql, the pass-through query appears as if it were a string constant to OPENQUERY. The same technique could be used to dynamically create a view or function with a predictable name that other SQL could use in a SELECT statement. With sp_executesql, you are limited only by what you can write to a string, and that could be anything!

posted on March 15, 2007  #    by Adam Anderson  Comments [2] Trackback
 Wednesday, March 14, 2007
Today I battled with some interesting problems writing changes to a hierarchical datatable to a SQL Server database. Hopefully by writing about this I can save you some time.
posted on March 14, 2007  #    by John Waters  Comments [0] Trackback
 Tuesday, March 13, 2007

You may need the public key token for purposes such as registering an HTTP module.  Use the strong naming tool with the -T option to extract the public key.  Be sure to call sn from the command line that comes with the .NET framework SDK:

C:\Program Files\telerik\r.a.d.controlsQ4 2006\NET2\bin>sn -T radupload.net2.dll

Microsoft (R) .NET Framework Strong Name Utility  Version 2.0.50727.42
Copyright (c) Microsoft Corporation.  All rights reserved.

Public key token is b4e93c26a31a21f0

posted on March 13, 2007  #    by Noel Rice  Comments [0] Trackback
 Tuesday, March 06, 2007

There was a bug in SP2 regarding maintenance plans, see official note below:

An issue was recently discovered with SQL Server 2005 SP2 which might impact customers using cleanup tasks in Maintenance Plans and SQL Server Integration Services packages. The issue is described in detail at http://support.microsoft.com/kb/933508. We have created a General Distribution Release (GDR) #50000939 to address this issue which is available for download and we have included the GDR in a refresh of the SP2 installation package

More information can be found here.

posted on March 6, 2007  #    by John Waters  Comments [0] Trackback