ExtensionMethod.NET Home of 880 C#, Visual Basic, F# and Javascript extension methods

IDataRecord.Field<'a>

This mimics the DataRow.Field<T> extension method found in System.Data.DatasetExtensions.dll, except it supports the F# Option type, and works on an IDataReader/IDataRecord/DataRow. Pair this with the IDataReader.AsSeq extension for extra utility.

Source

namespace Foo

open System
open System.Data
open System.Reflection
open System.Runtime.CompilerServices

/// Ported from System.Data.DatasetExtensions.dll for the sake of performance and supporting 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


[<AutoOpen>]
module FsDataEx =

    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

Example

match reader.Field<int option>("MyColumn") with
| Some x -> printfn "Found value: %d" x
| None   -> printfn "Value was null."

Author: Joel Mueller

Submitted on: 29 okt. 2010

Language: F#

Type: System.Data.IDataRecord

Views: 5910