Saturday, September 24, 2005

Debugging and testing serviced components

I've been working over the past couple of weeks on a new data access component at work. For many and varied reasons I decided to implement it as a serviced component. I wanted to take advantage of COM+'s ability to pool and maintain objects, as well as its tried and tested transaction management capabilities for slinging stuff in and out of a SQL Server database.

Since we're using VS 2005 to develop this new project, I wanted to take advantage of the integrated Unit Testing tools that come with it straight out of the box to make sure my component was working as expected. It was here that I encountered one or two things that I'm noting here so I can remember them:

You'll never get good code coverage stats
One of the things that needs to be configured when you enable Code Coverage in VS 2005 is exactly which DLL's it's going to create code coverage stats for.

Unfortnately there are 2 things that prevent VS from allowing it for DLL's that contain serviced components.

  • COM+ only looks in the GAC

If you want COM+ to recognise your component and run it as a server component (as opposed to a library component - more in that in a minute) you must install it in the GAC. This is no biggie. You just open up you WINDOWS/Assembly folder and drag/drop your DLL into it. Job done (thanks to the Fusion shell extension). You can also use GACUTIL.exe, but isn't dragging and dropping so much easier?

So you've got you component installed in the GAC, you then need to register it as a serviced component. Easy. Just use REGSVC.exe and bob's your uncle. One serviced component, configurable through the Component Services management utility. Cool.

The problem arises, however, when you try to set up code coverage to get stats on your component. You can only reference DLL files directly, and by default the 'Select Artifacts to Instrument' dialog only incldes those DLL's in your solution.

You can add other DLL's to the list, but try to navigate to the GAC and add them from there, and no dice. It somply won't allow you. This is either an intentional feature, or a side-effect of Fusion. Although in real life, the GAC is organised as a bunch of folders with subfolders and DLL files within them, when viewed in Explorer (as in, for instance, the Open File dialog) the assemblies appear as assembly objects. Since these aren't actual DLL files, the dialog won't allow you to add them.

Oh well. I'll have a play about and see if I can't work out a way around it. I've got a couple of ideas, but they all generally revolve around changing the way the component is referenced, and as such, change the overall context in which the abject will be used. Change the test environment too much from how it's actually going to be deployed, and you've negated your tests - they're no longer valid. Some (and I really do mean a very small amount) things can be changed for the purposes of testing and debugging, but not too much.

  • Serviced Components don't run in the normal application context

Actually, this isn't strictly true. Serviced components can be run inside the context of the calling application. And it's really simple to do, too. On the property sheet for the COM+ Application you've created, under the 'Activation' tab, select 'Library Application'. This will create the objects created by COM+ in the context of the calling application.

This can even be demonstrated really easily - just step through some code that calls a serviced component you've made in the debugger. If the application's running as a Server application (i.e. objects are actually created and run in dllhost.exe, rather than in the calling app) the debugger will just step over that code. Press F11 all you like, it won't do any good. Because the object isn't in the same process as the app being debugged, it won't step into it. However if you change the configuration to a Library application, you will be able to debug to your heart's content.

So what's that got to do with Code Coverage? Well, (I think) VS only produces results for code running in the TestHost process, or the ASP.NET equivalent, if you configure it that way. So, even if I did manage to add the GAC'ed DLL, my code coverage stats would be poor. UNLESS, for the purposes of testing I configured the app as a LIbrary Application.

I guess you'd only need to do it the once, though. Once you know your unit tests cover x% of your component's code, you can configure it how it'd be in production and leave it - if you're using Team System you'll be able to publish the results and keep a record of the coverage stats to take away. Nice!

Ignore the error messages
Okay. Don't ignore the error messages, just don't drill down to fixing the exact problem in the message. Because when you're working with Serviced components, you're working with (at least!) 2 sets of technology, the COM+ stuff and the .NET framework. As a result, the errors that get caught and passed between the 2 can lose a little in the translation. I had several errors crop up with null references and invalid security priviledges. They were invariably caused by either not installing the DLL in the GAC, or forgetting to register the component in the first place. Doh! Still, it took a bit of investigating, and I spent a while looking through the code trying to work out what was wrong. Guess I learnt a bit of a lesson - It's not always just about the code you've written.

Remember which actual file's being loaded
I spent a while building, rebuilding and generally buggering about with the code the first couple of times I made significant changes to how the component worked. Either I spotted a mistake and corrected it, or I got rid of a method, or I added a method and tried to call it. Some of the corrections got me all confused since I'd made various changes to the behaviour, but the results weren't changing.

Or, I'd add a method, write some code to call it, and then get a Mising Method exception.

What was going on? It's obvious now that I've spotted it, and it's even more obvious after what you've just read about Unit Testing, but the wierdness was being caused by the DLL not being re-installed in the GAC. I'd rebuild, and then when the test app called that assembly, it wasn't the fresh up-to-date assembly being called. It was the assembly that I'd already identitfied as being wrong that was running.


All in all, though, everything seems to be working nicely now, though. I just wanted to get some stuff to remember written down to help me remember it.

Jurt a quick plea for help, I wrote this pretty much off the top of my head. If anyone spots anything that's very incorrect, please drop a comment and let me know. Just to re-iterate, I certainly wouldn't take this post as gospel. They're just some things I wanted to make a note of, and generally thought I'd share.

1 comment:

Anonymous said...

Thanks much. This helped me a lot.
I have ben searching for the info on how to debug the COM+ code from managed C# code.

Thanks again.