Extending Database Objects / Downcasting from Base

131 views Asked by At

I'm learning F#, and in an attempt to become more fluent I am re-writing a backend/data access layer to a program that is currently using C# with an ORM. I'm using the SqlDataProvider type provider (and loving it btw).

My search for partial class type in F# didn't turn up with any promising results. The reason for the partial class is to add some base functionality to the database objects returned from the Sql type provider. So far I'm simply inheriting from the types returned from the Sql type provider, and adding functionality that way. However, when I return a query result from a function, its returning the base class, not the derived class.

I don't want to use reflection to map between the two objects' properties, but I also don't want to have to return a "new fooObject { ...... }" and list every single property every time I want to create a new method.

So here I am, at the source of all the is good in the world. Am I missing something that I can use for partial class creations? Is there an easy way (without using reflection) to map the returned object to the derived object? Am I missing some awesome functionality completely?

type DbContext = SqlDataConnection<ConnectionString="~">

//Database objects 
type Address() = 
    //inheriting base class from SqlDataProvider
    inherit DbContext.ServiceTypes.Address()

    //Adding common functionality
    member this.Add()  =
        use db = DbContext.GetDataContext()
        db.Address.InsertOnSubmit this
        db.DataContext.SubmitChanges()

    member this.Remove() = 
        use db = DbContext.GetDataContext()
        db.Address.DeleteOnSubmit this
        db.DataContext.SubmitChanges()

    member this.Update() =
        use db = DbContext.GetDataContext()
        let mutable old = query {for a in db.Address do
                                 where (a.AddressId = this.AddressId)}
                                 |> Seq.exactlyOne 
        old <- this
        db.Address.Context.SubmitChanges()

     //this method only returns the base object
    static member GetAll() =
        use db = DbContext.GetDataContext()
        db.Address |> Seq.toList

     //as does this one
    static member Find addressId = 
        use db = DbContext.GetDataContext()
        let add = new Address()
        query {for a in db.Address do where (a.AddressId = addressId)}
        |> Seq.exactlyOne
1

There are 1 answers

0
Christopher Stevenson On BEST ANSWER

F# does this kind of thing with type extensions:

type DbContext.ServiceTypes.Address with
    member this.Add() =
        use db = DbContext.GetDataContext()
        db.Address.InsertOnSubmit this
        db.DataContext.SubmitChanges()

    member this.Remove() = 
        use db = DbContext.GetDataContext()
        db.Address.DeleteOnSubmit this
        db.DataContext.SubmitChanges()

    member this.Update() =
        use db = DbContext.GetDataContext()
        let mutable old = query {for a in db.Address do
                                 where (a.AddressId = this.AddressId)}
                                 |> Seq.exactlyOne 
        old <- this
        db.Address.Context.SubmitChanges()

Note that type extensions (in this format) are only accessible from F# code. If you need something interoperable with C#/VB.Net, you should instead use 'normal' extension methods:

[<Extension>]
type ExtraCSharpStyleExtensionMethodsInFSharp () =
    [<Extension>]
    static member this.Add (address:DbContext.ServiceTypes.Address)  =
        use db = DbContext.GetDataContext()
        db.Address.InsertOnSubmit address
        db.DataContext.SubmitChanges()

    [<Extension>]
    member this.Remove (address:DbContext.ServiceTypes.Address) = 
        use db = DbContext.GetDataContext()
        db.Address.DeleteOnSubmit address
        db.DataContext.SubmitChanges()

    [<Extension>]
    member this.Update (address:DbContext.ServiceTypes.Address) =
        use db = DbContext.GetDataContext()
        let mutable old = query {for a in db.Address do
                                    where (a.AddressId = this.AddressId)}
                                    |> Seq.exactlyOne 
        old <- address
        db.Address.Context.SubmitChanges()

As for the GetAll and Find methods, I would be tempted to create a module with functions like GetAllAddresses and FindAddress.

Overall, the repository pattern is kind of awkard in F#.