DataSets in SOA

Last November, I engaged Udi Dahan (the Software Simplist) on using DataSets in OLTP situations. It turns out that Udi uses a question I posed that kind of preceded this dialogue for his latest podcast at Dr. Dobbs Journal titled DataSets and Web Services. Yes, I am the Jacob he mentions.

You can tell that he's not a great fan of DataSets in general, though he takes pains to treat them fairly. Which puts him ahead of most of the developers I track, really.

Row State

One of the things that Udi doesn't like about DataSets is that they track row state. Unfortunately, he seems to be wrapping together two different consequences of doing so and it's a little bit confusing. His point actually makes more sense if you break it apart a bit.

The first thing he brings up is that because a DataSet tracks row state, many developers build that tracking into the web service as an unstated expectation. I've seen this, too, and it is hideous. In the most extreme cases, you'll get a web service for each of the CRUD operations for your objects—essentially making your web service a thin proxy for your data layer.

The real problem with using row state, however, is that it muddies the contract for your services. This is where Udi is talking about "passing along the intent." Services should have an explicit intent for the data that they receive (or return). Without an explicit intent, it can be hard to discern what any specific service is supposed to do and therefore what business logic that service will enforce. The narrower you can define your intent the better. If your service only knows that a customer record has changed, that service doesn't know if it's supposed to change the address, the name, the marital status, or what all. Which means that the service will not only have to handle all business logic associated with a customer record every time, it also has to try to reconstruct what has happened if it wants to have any kind of concurrency at all.

Incidentally (and more importantly, I think), this also means that those using that service don't know what all it is supposed to do or what criteria it uses to do it. Developers don't deal well with uncertainty. Before you know it, you'll see business logic creep into the calling code "just to be sure." Or worse, you'll see new services pop up duplicating pieces of the larger service.

The problem with row state isn't deterministically bad—just because it's easier to screw up by relying on that additional information doesn't mean you have to screw it up. In other words, just because DataSets track row state doesn't mean you have to give row state significance in your services.

Too Much Information

Row State is really just a specific example of a wider problem, though—namely that DataSets encode a lot of details beyond the data that your service needs to do its stuff. These details make DataSets unsuited for including as a part of an SOA contract because alterations in those implementation details will break a service contract even when the data structures remain the same.

Udi's conclusion is that DataSets should never be used in SOA. I'm forced to agree with him for this last reason. Versioning objects across an enterprise is a tricky business. Minimizing the number of things that break downstream stuff when changed is important to good architecture. The more that you can isolate, the happier you will be.

So the key question for me becomes: are you actually implementing SOA or just using web services? If you're simply using web services, DataSets have limitations to watch for but can be useful. If you're going SOA, keep DataSets to yourself.

I can live with that.

28. March 2007 20:36 by Jacob | Comments (0) | Permalink

Out-Cleverring Yourself

Have you ever hacked a product to do something it wasn't intended to do in order to "simplify" things for your users and have that blow up in your face? This is an account of my experiences doing just that with MS Reporting Services.

If you've used Reporting Services at all, you'll know that there are two virtual directories that are created on IIS when you first install it to a server: ReportServer actually serves up the reports by passing the requested data to external applications via whatever protocol you have configured and Reports (aka ReportManager) which serves as a user interface for reports on the server.

ReportManager is, by far, the most visible of the two. Using ReportManager, you can organize your reports and data sources and set permissions on who can view and change them. Often, this is enough of a user interface that it is a viable deployment mechanism to simply point your users to the Url for a report under the ReportManager directory.

Ingenious people, like myself, will often designate a new Web instance on IIS just for Reporting Services, generally naming it something clever like "reports". When you do this, it is very tempting to point the root directory of that web instance to the ReportManager directory. This means that you can point your users to "http://reports/Invoice" instead of "http://reports/Reports/Invoice". You can see why you'd want to make this change (assuming you are similarly obsessive).

There is, however, an unintended side-effect to this change. Once you do this, your ReportServer will begin throwing errors if you ever decide you want to use direct Url access to display reports. Not a lot of people do this unless they're using a Reporting Services "Integration" that uses this functionality for showing reports. Personally, I ran into this situation when trying to tie our Reporting Services forms into Great Plains. Since Great Plains integrations (both native and third party) expect to use ReportServer for the report display, I was shocked and dismayed to find myself staring at this error:

The type Microsoft.ReportingServices.UI.WebControlConnection, ReportingServicesWebUserInterface, Version=, Culture=neutral, PublicKeyToken=89845dcd8080cc91 does not implement IReportServerConnection or could not be found

Hitting Google for this error is more than a little bit frustrating because the vast majority of issues where it shows up is in mis-configuring a ReportViewer object in an ASP.Net web application. This is manifestly not the case here.

This was a dome-scratcher for a lot longer than I like to admit. All of the advice pieces that I could find for this error are simply inapplicable. Fortunately, I ran across a Usenet post by Dave Green from last July. Dave reported that he could fix ReportServer if he removed a couple of settings from the <appSettings> section of the ReportManager. This is most odd because the two simply shouldn't be linked--at least, in that direction. If they were linked, I'd expect that link to go the other way (i.e. something in ReportServer breaking ReportManager). After all, ReportManager is essentially a UI on top of ReportServer.

I tested his findings and sure enough, removing the appSettings fixed ReportServer for me. Since removing those settings broke ReportManager, this was an unappetizing solution.

It was then that I remembered that IIS does an interesting little trick with web.config files and subdirectories. You see, each subdirectory on a website inherits the configuration from parent directories (also machine.config, but that's not relevant to our story). It doesn't matter how those directories are physically arranged, what is important is that IIS uses the Url to determine inheritance. So in a situation where "http://reports" is pointing at ReportManager and "http://reports/ReportServer" is pointing to ReportServer, that means that ReportServer is inheriting the web.config settings from ReportManager. Thus, the appSettings for ReportManager are being read by ReportServer and misapplied (I've no idea why the presence of an appSettings entry would break ReportServer)

Fortunately, the fix for this issue is pretty simple. The <appSettings> element includes a spec for a clear directive that will wipe out all inherited values. Adding an appropriate entry to the web.config in ReportServer cleared the problem right up. Here's the section for the curious (and/or lazy):

    <clear />

So there you go. I hoisted myself by my own petard. Fortunately, I was able to figure out how to get myself unhoisted. I now have both ReportManager and ReportServer humming along nicely without having to undo my clever directory mapping. Since I haven't been able to find this solution anywhere, I figured I'd post this for those in similar situations.

21. February 2007 21:47 by Jacob | Comments (0) | Permalink


<<  February 2017  >>

View posts in large calendar