Datasets vs. Custom Entities

So you want to build your own entity objects? Maybe you are even purchasing or authoring a code-gen tool to do it for you. I like to use Datasets when possible and people ask why I like them so much. To be fair, I'll write a list of reasons to not use datasets and create your own entities - but for now, this post is all about the pros of datasets. I've been on a two week sales pitch for DataSets with a client so let me summarize.

  • They are very bindable.
    This is less of an issue for Web forms which don't support 2 way databinding. But for Win forms, datasets are a no brainer. Before you go and say that custom classes are just as bindable and could be, go try an example of implementing IListSource, IList, IBindingList and IEditableObject. Yes you can make your own custom class just as bindable if you want to work at it.
  • Easy persistence.
    This is a huge one. Firstly, the DataAdapter is almost as important as the DataSet itself. You have full control over the Select, Insert, Update and Delete sql and can use procs if you like. There are flavours for each database. There is a mappings collection that can isolate you from changes in names in your database. But that's not all that is required for persistence. What about optimistic concurrency? The DataSet takes care of remembering the original values of columns so you can use that information in your where clause to look for the record in the same state as when you retrieved it. But wait, there's more. Keeping track of the Row State so you know whether you have to issue deletes, inserts, or updates against that data. These are all things that you'd likely have to do in your own custom class.
  • They are sortable.
    The DataView makes sorting DataTables very easy.
  • They are filterable.
    DataView to the rescue here as well. In addition to filtering on column value conditions - you can also filter on row states.
  • Strongly Typed Datasets defined by XSD's.
    Your own custom classes would probably be strongly typed too...but would they be code generated out of an XSD file? I've seen some strongly typed collection generators that use an XML file but that's not really the right type of document to define schema with.
  • Excellent XML integration.
    DataSets provide built in XML Serialization with the ReadXml and WriteXml methods. Not surprising, the XML conforms to the schema defined by the XSD file (if we are talking about a strongly typed dataset). You can also stipulate whether columns should be attributes or elements and whether related tables should be nested or not. This all becomes really nice when you start integrating with 3rd party (or 1st party) tools such as BizTalk or InfoPath. And finally, you can of course return a DataSet from a Web Service and the data is serialized with XML automatically.
  • Computed Columns
    You can add your own columns to a DataTable that are computed based on other values. This can even be a lookup on another DataTable or an aggregate of a child table.
  • Relations
    Speaking of child tables, yes, you can have complex DataSets with multiple tables in a master detail hierarchy. This is pretty helpful in a number of ways. Both programmatically and visually through binding, you can navigate the relationship from a single record in master table to a collection of child rows related to that parent. You can also enforce the the referential integrity between the two without having to run to the database. You can also insert rows into the child based on the context of the parent record so that the primary key is migrated down into the foreign key columns of the child automatically.
  • Data Validation
    DataSets help with this although it's not typically thought of as an important feature. It is though. Simple validations can be done by the DataSet itself. Some simple checks include: Data Type, Not Null, Max Length, Referential Integrity, Uniqueness. The DataSet also provides an event model for column changing and row changing (adding & deleting) so you can trap these events and prevent data from getting into the DataSet programmatically. Finally with the SetRowError and SetColumnError you can mark elements in the DataSet with an error condition that is can be queried or shown through binding with the ErrorProvider. You can do this to your own custom entities with implementation of the IDataErrorInfo interface.
  • AutoIncrementing values
    Useful for columns mapped to identity columns or otherwise sequential values.

This is not an exhaustive list but I'm already exhausted. In a future post, I'll make a case for custom entities and not DataSets, but I can tell you right now that it will be a smaller list.

Coming at the debugger the other way.

When writing services, I often find myself having to attach to processes manually from within VS.NET. When you can't simply run the code directly from VS.NET and step your way through it this is a common choice. Every time I have to do this though I cringe because I'm walking on thin ice. Sometimes it doesn't work, or you hang, or weird things seem to happen.

I was having a particularly difficult time with a client yesterday who was debugging through some HTTP handlers when I remember a question from one of the MCSD.NET exams that clued me into the fact that you can programmatically cause a break point - that's right - I said “programmatically“.

System.Diagnostics.Debugger.Break();

That nifty little class & function call will bring up a dialog box offering to allow you to attach to a new instance of VS.NET or an existing one you might have open - similar to when you get an unhandled exception. For this to work the user running the process requires UIPermission. Not surprisingly the default aspnet user that asp.net normally runs under when the machine.config processmodel section's user is set to “machine” does not have this permission by default. If you are a developer running IIS locally, consider temporarily changing it to “system” or some other user but be careful because doing so causes asp.net to run under the local system account - which is a security risk.

Too bad there is no T-SQL version of this function - maybe in Yukon.

Active Directory Application Mode

I haven't had much chance to use many of the cool things in Windows 2003 to date, but one of the new things (that incidentally also runs on XP Pro) is a new mode of Active Directory called Application Mode - in total ADAM. I'm finally getting to do some real playing around with this for a large application I've just started working on for a client.

It's basically a standalone active directory that is ideal for storing your own users and roles etc. to be used by your application in an active directory style - even if your company isn't using active directory. If you do go to AD down the road - it's a simple migration for your app. ADAM also acts as an LDAP server as well which makes it a bit more open. You can really put whatever you want into ADAM as it's schema is extensible (not unlike Active Directory). The idea though is that you can have multiple instances of ADAM installed on your server - each containing data specific to a unique application - while AD would store more globally required data throughout the enterprise.

It's pretty typical to store this type of application specific data historically into a SQL database. While that's possible, ADAM - and more specifically the underlying AD is more geared to this type of data. A relational DB remains an ideal choice for transactionally updated data, but ADAM is a great place to store any kind of administrative data that is, for the most part, written to once, and then read frequently by your application.

I'm going to be playing more with this, and specifically doing some performance testing and seeing what kind of improvements can be made by using it in the middle tier, caching some of the data in a wrapper object that is hosted in COM+ and pooled.

As an aside, I find it kind of strange that Whidbey - and specifically the new ASP.NET membership/roles stuff that is built in doesn't use ADAM - but instead opts for the classic database solution. Fortunately the membership/role model in ASP.NET Whidbey is an extensible provider model so I may just take a crack at creating my own provider that uses ADAM.

I should probably google that now as someone has probably already been there and done that.

Share "source" files between projects.

By default, each project in your solution has an AssemblyInfo.??. Amongst other things, it contains an AssemblyInfo attribute that will end up stamping the dll or exe with it's version number. This is the version number used by the CLR to make sure that when you reference a dll - it finds the correct version.

In a solution comprised of many projects, you may want them all to share the same build number. By default, VS.NET sets this version to 1.0.* -- * meaning that it will increment the number. Sometimes you build just one project, sometimes all of the projects in a solution. Some developers may even have their own solution files to just work on a subset of the projects in the master solution.

What I'm saying here is that you really ought to take better care (and control) of this version number. It would be nice to have all of your dll's in the solution share the same assembly version. Sure you can hard code it, but manually incrementing it then becomes tedious. The secret to this tip is that the AssemblyVersion attribute doesn't have to be in the AssemblyInfo.?? file. It can be in it's own file. In fact, that file doesn't have to even physically exist in the same subdirectory as the project thanks to “Linked“ files.

So follow these steps.

  1. Remove the “AssemblyVersion“ attributes from the AssemblyInfo in each of your projects.
  2. Create a “VersionInfo.cs“ (or .vb) in the root of your solution - probably one level up from your projects. It should include an AssemblyVersion attribute like the one you took out of each of your AssemblyInfo files. It should also include a using System.Reflection since that is the required namespace.
  3. In each of your projects make a shortcut or link reference to this new file. To do this, right click on each project and select “Add Existing Item“. Browse to the VersionInfo.cs (or .vb) file and instead of clicking “Open“ select “Link“ from the drop down on the open button in the File Open Dialog.

Now you have only one place to increment the version for your entire solution. If you are using NAnt, you can have it do this for you with this simple task:

 

<version path="VersionInfo.txt" startDate="2003-10-1" buildType="monthday" prefix="assembly." />

<asminfo output="VersionInfo.cs" language="CSharp">

<imports>

<import name="System" />

<import name="System.Reflection"/>

</imports>

<attributes>

<attribute type="AssemblyVersionAttribute" value="${assembly.version}" />

</attributes>

</asminfo>

The first version task increments the build number and stores it in both the assembly.version variable and also the VersionInfo.txt file. The second task recreates a new AssemblyInfo file and uses the assembly.version variable.

C# vs. VB.NET Interfaces

Visual Basic has an interest syntax for implementing interface members.

Public Interface IAdd

Function Execute(ByVal i As Integer, ByVal j As Integer) As Integer

End Interface

Public Interface ISubtract

Function Execute(ByVal i As Integer, ByVal j As Integer) As Integer

End Interface

Public Class Calculator

Implements IAdd, ISubtract

Public Function Add(ByVal i As Integer, ByVal j As Integer) As Integer Implements IAdd.Execute

Return i + j

End Function

Public Function Subtract(ByVal i As Integer, ByVal j As Integer) As Integer Implements ISubtract.Execute

Return i - j

End Function

End Class

The key here is that the function names don't have to be the same as they are in the interface definition. I have two interfaces that both expose a Execute method. They are implemented separately as Add and Subtract. Thanks VB team for the “Implements“ keyword. C# is a different story - and up until a few days ago I didn't think you could do this at all in in C#. Here you go:

interface IAdd

{

int Execute(int i, int j);

}

interface ISubtract

{

int Execute(int i, int j);

}

public class Calculator: IAdd, ISubtract

{

#region IAdd Members

public int Execute(int i, int j)

{

return i + j;

}

#endregion

#region
ISubtract Members

int InterfacesCS.ISubtract.Execute(int i, int j)

{

return i - j;

}

#endregion

}

I used the auto-magic interface template generator built into the VS.NET C# editor. After you type ISubtract on the class declaration, wait for a little tool tip that tells you to hit TAB for a default implementation template. You'll notice the fully qualified function name “InterfacesCS.ISubtract.Execute“ on the second declaration of “Execute”. The whipper snappers out there will also notice that the second method is not public. So it's private you might say. The class view and object browser would agree with you. Don't try putting “private” in front of the declaration. The compiler won't like that. Don't even think of trying to call it directly though. The only way to call this second method either internally to the class (why not it's private) or externally is by casting the reference to the interface, like so..

Calculator calc = new Calculator();

int result = ((ISubtract)calc).Execute(5,4);

 

If you really want to call the method directly without casting it, you'll have to create a wrapper method and expose it publicly or privately if that suits your needs.

public int Subtract(int i, int j)

{

return ((ISubtract)this).Execute(i, j);

}

Thanks to Alex Bershadski for pointing me to a few code snippets in the MS Press C# book about this. The book could really do a better job of identifying the limitations of C# in this regard. At least it can be done. Up until Tuesday, I didn't think it was possible.

Good turnout to VS.NET tricks bof

I was totally impressed with the 200 or so people turn out at our VS.NET Tips & Tricks bof. More than one bof organizer was a bit concerned about us bringing a projector - but I don't know how you'd fully appreciate some tips without getting to see them on the screen. It worked well. The O# posse showed up with enough tips to fill the 60 minutes, but we didn't really get to use many of them. The crowd involvement was terrific. At one point, I had to tell people to keep it down because the banter was running wild. This is a good problem. This might have been better done as a VS.NET Tips Cocktail Reception, maybe next year.

My favourite tip? Well it's a tip and a best practice from Dave Lloyd. During and after a build, the output window is kind of important, but VS.NET immediately hides it by default with the Tasks window. We tend to look at the first task to see what caused a failed build but it isn't often the right one. You really need to look at the build output, and you really need it to be bigger than a typical docked window. I'm forever resizing that thing up during a build post-mortem - and back down when I'm done. Right click on the output window title bar and uncheck Dockable. Presto! The output window is now in your editor space as another tab. Beautiful.

Visual Studio .NET Tips and Tricks @ PDC

I'm hosting a birds of a feather Sunday night from 8-9pm at the Microsoft PDC. The topic is Visual Studio .NET Tips and Tricks. Hope to see you there - and grab a beer or three afterwards.

Code:
BoF29
Room: Room 402AB

MS Professional Developers Conference...

...here we come.

Myself and fellow ObjectSharpies DennisLee and DaveLloyd are heading out Saturday morning for the PDC in L.A. Stay tuned for our perspectives on Whidbey (essentially .NET 2.0), Yukon (SQL Server 2004?), and Longhorn (Windows 2005?). Key pieces of technology I'll be researching are Indigo (new web services framework) and Avalon (new windows forms framework).

I'm also looking forward to seeing MSBuild and trying to understand how it will compare, compete or compliment NAnt. I still don't see anything about the next version of Visual Source Safe - what's up with that?

If you are in town, look me up and I'll buy you a beer.