DataSetExtensions for F#, DataReaders
System.Data.DataSetExtensions.dll adds the ability to treat a DataTable as an IEnumerable<DataRow> as well as unboxing the values of individual cells in a way that gracefully handles DBNull by supporting System.Nullable. For example, in C# we can get the value of an integer column that contains nulls, and specify that DBNull should default to zero with a very concise syntax:
var total = myDataTable.AsEnumerable()
                       .Select(row => row.Field<int?>("MyColumn") ?? 0)
                       .Sum();
There are two areas where DataSetExtensions are lacking, however. First, it doesn't support IDataReader and second, it doesn't support the F# option type. The following code does both - it allows an IDataReader to be treated as a seq<IDataRecord>, and it can unbox values from either a reader or a dataset, with support for F# options or System.Nullable. Combined with the option-coalescing operator in another answer, this allows for code such as the following when working with a DataReader:
let total =
    myReader.AsSeq
    |> Seq.map (fun row -> row.Field<int option>("MyColumn") |? 0)
    |> Seq.sum
Perhaps a more idiomatic F# way of ignoring database nulls would be...
let total =
    myReader.AsSeq
    |> Seq.choose (fun row -> row.Field<int option>("MyColumn"))
    |> Seq.sum
Further, the extension methods defined below are usable from both F# and from C#/VB.
open System
open System.Data
open System.Reflection
open System.Runtime.CompilerServices
open Microsoft.FSharp.Collections
/// Ported from System.Data.DatasetExtensions.dll to add support for the Option type.
[<AbstractClass; Sealed>]
type private UnboxT<'a> private () =
    // This class generates a converter function based on the desired output type,
    // and then re-uses the converter function forever. Because the class itself is generic,
    // different output types get different cached converter functions.
    static let referenceField (value:obj) =
        if value = null || DBNull.Value.Equals(value) then
            Unchecked.defaultof<'a>
        else
            unbox value
    static let valueField (value:obj) =
        if value = null || DBNull.Value.Equals(value) then
            raise <| InvalidCastException("Null cannot be converted to " + typeof<'a>.Name)
        else
            unbox value
    static let makeConverter (target:Type) methodName =
        Delegate.CreateDelegate(typeof<Converter<obj,'a>>,
                                typeof<UnboxT<'a>>
                                    .GetMethod(methodName, BindingFlags.NonPublic ||| BindingFlags.Static)
                                    .MakeGenericMethod([| target.GetGenericArguments().[0] |]))
        |> unbox<Converter<obj,'a>>
        |> FSharpFunc.FromConverter
    static let unboxFn =
        let theType = typeof<'a>
        if theType.IsGenericType && not theType.IsGenericTypeDefinition then
            let genericType = theType.GetGenericTypeDefinition()
            if typedefof<Nullable<_>> = genericType then
                makeConverter theType "NullableField"
            elif typedefof<option<_>> = genericType then
                makeConverter theType "OptionField"
            else
                invalidOp "The only generic types supported are Option<T> and Nullable<T>."
        elif theType.IsValueType then
            valueField
        else
            referenceField
    static member private NullableField<'b when 'b : struct and 'b :> ValueType and 'b:(new:unit -> 'b)> (value:obj) =
        if value = null || DBNull.Value.Equals(value) then
            Nullable<_>()
        else
            Nullable<_>(unbox<'b> value)
    static member private OptionField<'b> (value:obj) =
        if value = null || DBNull.Value.Equals(value) then
            None
        else
            Some(unbox<'b> value)
    static member inline Unbox =
        unboxFn
/// F# data-related extension methods.
[<AutoOpen>]
module FsDataEx =
    type System.Data.IDataReader with
        /// Exposes a reader's current result set as seq<IDataRecord>.
        /// Reader is closed when sequence is fully enumerated.
        member this.AsSeq =
            seq { use reader = this
                  while reader.Read() do yield reader :> IDataRecord }
        /// Exposes all result sets in a reader as seq<seq<IDataRecord>>.
        /// Reader is closed when sequence is fully enumerated.
        member this.AsMultiSeq =
            let rowSeq (reader:IDataReader)  =
                seq { while reader.Read() do yield reader :> IDataRecord }
            seq {
                use reader = this
                yield rowSeq reader
                while reader.NextResult() do
                    yield rowSeq reader
            }
        /// Populates a new DataSet with the contents of the reader. Closes the reader after completion.
        member this.ToDataSet () =
            use reader = this
            let dataSet = new DataSet(RemotingFormat=SerializationFormat.Binary, EnforceConstraints=false)
            dataSet.Load(reader, LoadOption.OverwriteChanges, [| "" |])
            dataSet
    type System.Data.IDataRecord with
        /// Gets a value from the record by name. 
        /// DBNull and null are returned as the default value for the type.
        /// Supports both nullable and option types.
        member this.Field<'a> (fieldName:string) =
            this.[fieldName] |> UnboxT<'a>.Unbox
        /// Gets a value from the record by column index. 
        /// DBNull and null are returned as the default value for the type.
        /// Supports both nullable and option types.
        member this.Field<'a> (ordinal:int) =
            this.GetValue(ordinal) |> UnboxT<'a>.Unbox
    type System.Data.DataRow with
        /// Identical to the Field method from DatasetExtensions, but supports the F# Option type.
        member this.Field2<'a> (columnName:string) =
            this.[columnName] |> UnboxT<'a>.Unbox
        /// Identical to the Field method from DatasetExtensions, but supports the F# Option type.
        member this.Field2<'a> (columnIndex:int) =
            this.[columnIndex] |> UnboxT<'a>.Unbox
        /// Identical to the Field method from DatasetExtensions, but supports the F# Option type.
        member this.Field2<'a> (column:DataColumn) =
            this.[column] |> UnboxT<'a>.Unbox
        /// Identical to the Field method from DatasetExtensions, but supports the F# Option type.
        member this.Field2<'a> (columnName:string, version:DataRowVersion) =
            this.[columnName, version] |> UnboxT<'a>.Unbox
        /// Identical to the Field method from DatasetExtensions, but supports the F# Option type.
        member this.Field2<'a> (columnIndex:int, version:DataRowVersion) =
            this.[columnIndex, version] |> UnboxT<'a>.Unbox
        /// Identical to the Field method from DatasetExtensions, but supports the F# Option type.
        member this.Field2<'a> (column:DataColumn, version:DataRowVersion) =
            this.[column, version] |> UnboxT<'a>.Unbox
/// C# data-related extension methods.
[<Extension; AbstractClass; Sealed>]
type CsDataEx private () =
    /// Populates a new DataSet with the contents of the reader. Closes the reader after completion.
    [<Extension>]    
    static member ToDataSet(this:IDataReader) =
        this.ToDataSet()
    /// Exposes a reader's current result set as IEnumerable{IDataRecord}.
    /// Reader is closed when sequence is fully enumerated.
    [<Extension>]
    static member AsEnumerable(this:IDataReader) =
        this.AsSeq
    /// Exposes all result sets in a reader as IEnumerable{IEnumerable{IDataRecord}}.
    /// Reader is closed when sequence is fully enumerated.
    [<Extension>]
    static member AsMultipleEnumerable(this:IDataReader) =
        this.AsMultiSeq
    /// Gets a value from the record by name. 
    /// DBNull and null are returned as the default value for the type.
    /// Supports both nullable and option types.
    [<Extension>]
    static member Field<'T> (this:IDataRecord, fieldName:string) =
        this.Field<'T>(fieldName)
    /// Gets a value from the record by column index. 
    /// DBNull and null are returned as the default value for the type.
    /// Supports both nullable and option types.
    [<Extension>]
    static member Field<'T> (this:IDataRecord, ordinal:int) =
        this.Field<'T>(ordinal)