Mad Props!

Omniscience is just a Google-search away.

Login

You're reading Mabsterama, the weblog of Matt Hamilton. Enjoy your stay.

Typed DataSets and SqlTransaction

I've recently been using typed DataSets to talk to a database for the first time. Strange, I know, but I learned how to do database access using raw ADO.NET back in the 1.1 days, and never really bothered with the nice visual DataSet designers or the new TableAdapter helpers.

Anyway, I have a project here with a DataSet containing four tables (and hence four TableAdapter classes). When the client program sends the changes in the DataSet back to the server, I want to update the tables, but I want to do it in a transaction so that if any one thing fails, no changes are written to the database.

Turns out that this is non-trivial. Support for transactions is not built in to the generated TableAdapter classes. Luckily it's possible to implement it yourself using .NET 2.0's partial class feature.

Sahil has written a bit about how to do this if you just have the one TableAdapter. In his post, he talks about creating a BeginTransaction method on that TableAdapter. That works well, but if you have more than one, you need some way to share a transaction across all your adapters.

So here's my solution. Instead of a BeginTransaction method, I have implemented a Transaction property on my TableAdapters, like this:

partial class CategoriesTableAdapter 
{ 
    public SqlTransaction Transaction 
    { 
        get 
        { 
            return _adapter.SelectCommand.Transaction; 
        } 
        set 
        { 
            if (_adapter == null) InitAdapter(); 
            Connection = value.Connection; 
            _adapter.InsertCommand.Transaction = value; 
            _adapter.UpdateCommand.Transaction = value; 
            _adapter.DeleteCommand.Transaction = value;
        } 
    } 
} 

As you can see, the property assigns the given transaction to the Transaction property on all its commands. This means I can do this in my Update method:

CategoriesTableAdapter catsTA = new CategoriesTableAdapter(); 
CategoryCodesTableAdapter codesTA = new CategoryCodesTableAdapter(); 
AllocationsTableAdapter allocsTA = new AllocationsTableAdapter(); 
CostsTableAdapter costsTA = new CostsTableAdapter(); 

catsTA.Connection.Open(); 
try 
{ 
    SqlTransaction txn = catsTA.Connection.BeginTransaction(); 
    try 
    { 
        catsTA.Transaction = txn;
        codesTA.Transaction = txn; 
        allocsTA.Transaction = txn; 
        costsTA.Transaction = txn; 

        // update each table, commit transaction or 
        // rollback if there's a problem 

What do you think? It certainly seems to work. There's probably more to it ... like I should probably check that there's not already a Transaction set on the _adapter commands etc. It's doing the job for me, though, so I hope it helps someone else out there.

Contravariance and Event Handlers

Before you read this, go over and watch last week's DotNetRocks TV. I'll wait.

Finished?

Ok, in that episode, Venkat talks about "covariance" and "contravariance" in delegates in .NET 2.0. The idea is that a delegate that takes a certain type as a parameter can be pointed at a method that actually takes a different type, provided the method's parameter is an ancestor class of the delegate's parameter.

The example he gives is a "StockSellDelegate" which takes a "Stock" parameter, and the method that gets called is an "EquitySold" method, which actually takes an "Equity" parameter. Because "Stock" derives from "Equity", the code compiles and it works fine.

Venkat, however, doesn't mention the one really cool result of this added feature.

As you know, all events in .NET follow a certain pattern. They all take a "sender" parameter of type "object", and an "e" parameter of some other type. Typically it's "EventArgs" but sometimes it's some other type. For example, the "KeyDown" event has a "KeyEventArgs" as its second parameter.

Contravariance in delegates means that it's possible to use the same event handler to handle both the "Click" and "KeyDown" event of a control, because "KeyEventArgs" derives from "EventArgs". So I can have this code:

 private void button1_Click(object sender, EventArgs e) { MessageBox.Show("button1 clicked or key pressed!"); } private void Form1_Load(object sender, EventArgs e) { button1.Click += button1_Click; button1.KeyDown += button1_Click; } 

And it compiles and runs just fine.

Obviously this is a contrived example, but the ability to use the same event handler for events that take different "e" parameters can be very handy.

Imagine a special kind of MenuItem, for example, that passed extra information in its "e" parameter. It's certainly nice for the Click event on that MenuItem to point to the same event handler as the Click event for the toolbar button that does the same thing.

Note that the IDE is still a bit dumb when it comes to this stuff. It won't show you non-matching event handlers when you drop down the combobox next to the event in the Properties window. You have to do it in code. No biggie, though.

We Met a Cheetah (or three)

Here's what happened on Saturday when Sal and I met a couple of Cheetahs at the National Zoo and Aquarium in Canberra.

A Cheetah Each

At the designated time we were led into the enclosure by a zookeeper/ranger. She explained to us the various guidelines to avoid being ... well, eaten. Some things were obvious - keep away from the mouth and claws etc. But others were more interesting. Like, if the cheetah decides to roll over while you're patting it, step back quickly or its claws will catch on your clothes and tear them. Or, stay behind the cheetah at all times so that if something spooks it, you're not the first thing it sees. You don't want to be treated as a threat by a 55kg ball of muscle with claws and fangs.

Cheetah Posing

The cheetahs themselves were amazing. Reasonably docile, although the other keeper, a big guy who has obviously worked with these cats for years, was quite happy to get in there and tussle with 'em to stir 'em up. They really are like big housecats.

A Happy Cheetah

So we got a bunch of photos taken (some of which you can see here), and for fifteen minutes we chatted with the keepers whilst patting the cats. Sound boring? It was an awesome experience and one I'll always remember.

Trip to Canberra

Sitting in our apartment here in Canberra, and already our "meet a cheetah" weekend has been excitement-packed.

We stopped at Gundagai on the way up for some tea, and upon returning to the car we noticed that we were running precariously low on petrol. Believing that the low-fuel warning (40km remaining) was due to the car being parked on a slope, I decided not to fill up, and instead resumed our trip towards Yass ... about 90km away.

So we watched the "distance to empty" meter as it slowly ticked up, as it is wont to do when you travel at a steady pace on the freeway. It made it back up to about 65km, and then began to descend.

When it was in single figures I began to worry. When we passed a sign saying that Yass was still 40km away, I really began to worry.

To cut a short story even shorter, we ended up driving for just under 40km with 0km remaining on the fuel guage. I pretty much held my breath the entire time. Needless to say, we did indeed make it to the service station just outside of Yass, and $87 worth of fuel later we were on our way again.

And here we are in our nation's capital, in a quite-decent apartment, ready for our cheetah-meeting encounter tomorrow morning.

I Have No Idea What to Name This Post

Look, I'll just say it here:

Ball Toucher

There. Go play it. It's safe for work, if you're worried, apart from the terrible Michael Jackson soundtrack, which you can thankfully mute.

A New Hope for DVD

Gaa! Lucas has either caved, or he was planning this all along:

Original, Unaltered Star Wars Trilogy on DVD

Finally! Han can shoot first on DVD! There is a god!

Typed DataSets, TableAdapters and RowUpdating

Pete and I for the past few weeks have been playing around with a project that returns a typed DataSet from a web service. Today we started on the "UpdateData" web method that accepts the typed DataSet and applies the changes back to the database.

One of the things we wanted to do was a bit of preprocessing on each row before it was applied. Coming from a code-based background (I've done all my ADO.NET web-services in code rather than using the visual DataSet desginer), I pointed out that SqlDataAdapter has a handy "RowUpdating" event that I've used, and so it logically must follow that the generated TableAdapters must have something similar.

Uh-uh.

Turns out that the only way you can get to any kind of RowUpdating/RowUpdated events is to handle them from within the TableAdapter, and the only way we could find to do that was to manually add a new class file to our web service project and code up the "other half" of the TableAdapter's partial class. We ended up adding a handler for _adapter.RowUpdating, and adding a public "InitEvents" method that hooked it up to the event. We then had to remember to call that "InitEvents" when we created the TableAdapter instance in the web method.

I don't know if I've missed something, but this seems like a very strange way to go about something as simple as handling an event. Why don't TableAdapters surface the RowUpdating and RowUpdated events of their internal "_adapter" variables to the IDE?

Metadata in Reporting Services

If you'd like the ability to store metadata (keywords, tags, whatever) along with reports in Microsoft SQL Server Reporting Services, head on over here and vote for my feature suggestion.

I think the ability to add keywords to assist in searches would be really helpful.

Comic Publishers and RSS

Tonight I wanted to add a cool feature to Comicster - a "new releases" tree node (or series of tree nodes), that would feature new releases from the various publishers.

Turns out that the only publisher with an official new-releases feed is Marvel. You can view it here. None of the other major comic publishers seem to have heard of web feeds of any kind. That's really disappointing.

Before you tell me: Yes, I have heard of comiclist.com. Those guys produce a "what's new" feed, too, but it's a little too verbose for my tastes. That's why I was looking for a single feed per publisher.

So anyway, Marvel gets their own special feed node in Comicster. Everyone else misses out. You watch how quickly DC goes out of business now that they're missing out on Comicster support! ;-)

User Group!

You may recall some time ago that I blogged about a possible .NET User Group in my region. Well, at CodeCampOz Anthony Johnston from Intechrity announced his intention to form just such a group! The inaugural meeting will be next month. Once I get confirmation I'll update this post with time/date details.

This oughtta be really interesting - I already have one or two people who are keen to come along ... it'll be great to meet other .NET developers in the Albury/Wodonga region.

Update:

When: 16th of May, 7:30pm
Where: The Committee Room, Commercial Club Albury.

See you there!