Typed DataSets are a type safe wrapper around a DataSet which mirrors your database structure. It was created to make sure that the code accessing the database is type safe and any changes in the database structure that changes tables, columns or column types will be caught at compilation time rather than runtime.

If you have a big typed dataset that contains a lot of tables, columns and relations it might be quite expensive to create it in terms of memory and time.

The main reason that creating a big typed dataset is expensive is due to the fact that all of the meta data that is contained within the typed data sets (tables, columns, relations) is created when you create a typed dataset even if eventually all you’ll use it for is to retrieve data from a single table.

I can speculate that the reason all of the typed dataset meta data is created during the instantiation of the typed dataset is due to the fact that it inherits from a generic DataSet and accessing the meta data (tables, columns) can also be done in a non type safe manner (i.e. access the Tables collection and/or Columns collection of a table).

If you are using a typed dataset (or dataset in general) you might be interested in the following tips:

  • If you have a big typed dataset, avoid creating it too many times in the application. This is specifically painful for web applications where each request might create the dataset. You can use a generic DataSet instead, but this might lead to bugs due to database changes and the fact that you’ll only be able to find these bugs during runtime rather than compilation time (which basically misses the whole point of using a typed dataset in the first place).
  • DataSets (typed datasets included) inherits from the MarshalByValueComponent class. That class implements IDisposable which means DataSets will actually be garbage collected after finalization (you can read more about finalization and the finalizer thread here). To make sure datasets are collected more often and are not hagging around waiting for finalization make sure you call the “Dispose” method of the dataset (or typed dataset) or use the “Using” clause which will call “Dispose” for you at the end of the code block.
  • Don’t use DataSets at all 🙂 Consider using a different data access layer with a different approach such as the one used in the SubSonic project.

I guess it would be rather trivial creating a typed dataset that is lazy in nature which creates the meta data objects only when they are accessed for the first time. That would reduce the memory footprint of a large typed dataset but will make the computation used to create these objects a little less predictable. If you are up to it or already did a lazy typed dataset ping me through the contact form 🙂

  • Although DataSet does inherit from MarshalByValueComponent which does override Finalize, the DataSet constructor calls GC.SuppressFinalize(this), which means that a DataSet does not have to wait for finalization to be reclaimed. Also, DataSet does not override Dispose(bool disposing), and all MarshalByValueComponent.Dispose(bool disposing) does is remove the instance from the site’s container (which is only important at design time) and raise the Disposed event. So it turns out that calling Dispose on a DataSet doesn’t accomplish anything useful.

  • Niek

    Calling GC.SuppressFinalize() makes sure that the Finalize() method is not called during garbage collection. That it is called from the constructor implies that the designer of the DataSet class anticipated that the finalizer (which only calls Dispose(false), implemented in MarshalByValueComponent) is not called. This makes perfect sense, since Dispose(false) does absolutely nothing.

    The implication: DataSet derived classes are not meant to hold unmanaged resources!

    However, Disposing a DataSet does do something: it calls the Dispose(true) method. David describes what it does now, and this MIGHT be useful in some cases. But if your dataset is not sited, and no-one has attached an OnDispose event handler, and it is not used in a derived class which overrides the Dispose(bool) implementation, THEN calling Dispose() does not do anything useful.

    PERFECTLY ALL RIGHT:
    // publish coding guidelines that say not to use IComponent features of DataSets
    DataSet ds = Reader.ExecuteDataSet(…);
    // do something entirely local with this dataset

    EVEN BETTER FOR ALL PURITANS:
    using ( DataSet ds = Reader.ExecuteDataSet(…) ) {
    // expose the dataset to functions that might rely on the Disposing event handlers
    // or in case MS decides to do something vital in DataSet.Dispose() in next releases of .NET
    }

    Similar stories can be held for some other classes derived from IMarshalByValueComponent.