The Salty Economist

Things I Should Have Learned in High School
posts - 56, comments - 0, trackbacks - 0

F# - Fluent NHibernate - POCO Class Object

The next thing I want to try is to use an F# Class (instead of C#) with Fluent NHibernate.

In my last post I worked with a C# Class & Fluent NHibernate.  The C# Class looked as follows:

------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace StockPrices {

    public class COMPANY {

        public virtual int Id { get; set; }
        public virtual string COMPANY_NAME { get; set; }
        public virtual string COMPANY_TICKER { get; set; }
        public virtual int ASSET_TYPE_ID { get; set; }
        public virtual int IS_ACTIVE { get; set; }

    }

}
------------------------------------------------------------------

So how do we make the same class in F#?

The first thing I tried was this:


------------------------------------------------------------------
#light

namespace StockPrices

open System
open System.Collections.Generic

    // -----------------  1
    type COMPANY = class

        [<DefaultValue>] val mutable Id : int
        [<DefaultValue>] val mutable COMPANY_NAME : string
        [<DefaultValue>] val mutable COMPANY_TICKER : string
        [<DefaultValue>] val mutable ASSET_TYPE_ID : int
        [<DefaultValue>] val mutable IS_ACTIVE : int

        new() = {}
       
    end
------------------------------------------------------------------

This is the simplest way of setting up a class in F#.

In this example, I use the default constructor -- these are the open parentheses in the New() statement.  Therefore, my properties are not initialized.  In F# this is a no no and is not allowed.  When you do not initialize a variable you need to use the [<DefaultValue>] attribute to initialize the value to Zero, it that is possible. 

From Microsoft:  "The DefaultValue attribute is required on explicit fields in class types that have a primary constructor. This attribute specifies that the field is initialized to zero. The type of the field must support zero-initialization."

So, the above statement gives me a class with five public variables -- the integers are initialized with Zeros; the strings are initialized with Nulls.


The following is my main F# program.  Note that my COMPANY class is in a module called StockPrices that I open at the start of the program.

-----------------------------------------------------------------------------------------------
#light
open System
open System.Collections.Generic
open System.IO
open StockPrices

open FluentNHibernate.Automapping
open FluentNHibernate

let properties = new Dictionary<string, string>()

properties.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider")
properties.Add("dialect", "NHibernate.Dialect.MsSql2000Dialect")
properties.Add("connection.driver_class", "NHibernate.Driver.SqlClientDriver")
properties.Add("show_sql", "true")

properties.Add("proxyfactory.factory_class", "NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle")

let connString = "server='BIG_ROCK\LOGGERSEDGE';Initial Catalog=SMDATA;User ID=sa;Password=XXXXX"
properties.Add("connection.connection_string", connString)

//-> The New
let autoMappings = (FluentNHibernate.Automapping.AutoMap.AssemblyOf<StockPrices.COMPANY>()).Where(fun t -> (t.Namespace="StockPrices"))

let aConfig = (new NHibernate.Cfg.Configuration()).AddProperties(properties).AddAutoMappings(autoMappings)

let sessionFactory = aConfig.BuildSessionFactory()

let aSession = sessionFactory.OpenSession()

aSession.BeginTransaction()

//AT&T is Id 100
let coID = 100

let someObj = aSession.Load(typeof<StockPrices.COMPANY>, coID) :?> StockPrices.COMPANY

printfn "Company Name: %s,  Ticker: %s" someObj.COMPANY_NAME someObj.COMPANY_TICKER


aSession.Close()

let userresp = Console.ReadLine()
-----------------------------------------------------------------------------------------------

This program craps out on the line:

let aConfig = (new NHibernate.Cfg.Configuration()).AddProperties(properties).AddAutoMappings(autoMappings)

with the error:

{"(XmlDocument)(2,4): XML validation error: The element 'class' in namespace 'urn:nhibernate-mapping-2.2' has incomplete content. List of possible elements expected: 'meta, subselect, cache, synchronize, comment, tuplizer, id, composite-id' in namespace 'urn:nhibernate-mapping-2.2'."}

To be honest, I really have no clue what this all means.

So, I decide to take another tack.  I try an F# class that actually uses the 'member' syntax:

-----------------------------------------------------------------------------------------------
    type COMPANY() = class
   
        let mutable _Id : int = 0
        let mutable _COMPANY_NAME : string = ""
        let mutable _COMPANY_TICKER : string = ""
        let mutable _ASSET_TYPE_ID : int = 0
        let mutable _IS_ACTIVE : int = 0

        member x.Id with get() = _Id and set(v) = _Id <- v
        member x.COMPANY_NAME with get() = _COMPANY_NAME and set(v) = _COMPANY_NAME <- v
        member x.COMPANY_TICKER with get() = _COMPANY_TICKER and set(v) = _COMPANY_TICKER <- v
        member x.ASSET_TYPE_ID with get() = _ASSET_TYPE_ID and set(v) = _ASSET_TYPE_ID <- v
        member x.IS_ACTIVE with get() = _IS_ACTIVE and set(v) = _IS_ACTIVE <- v

    end
-----------------------------------------------------------------------------------------------

I rerun the my program.

This time, I make it one more line before it crashes on the line:

let sessionFactory = aConfig.BuildSessionFactory()

This time I get this big ugly:

NHibernate.InvalidProxyTypeException was unhandled
Message="The following types may not be used as proxies:
StockPrices.COMPANY: method get_Id should be 'public/protected virtual' or 'protected internal virtual'
StockPrices.COMPANY: method set_Id should be 'public/protected virtual' or 'protected internal virtual'
StockPrices.COMPANY: method get_COMPANY_NAME should be 'public/protected virtual' or 'protected internal virtual'
StockPrices.COMPANY: method set_COMPANY_NAME should be 'public/protected virtual' or 'protected internal virtual'
StockPrices.COMPANY: method get_COMPANY_TICKER should be 'public/protected virtual' or 'protected internal virtual'
StockPrices.COMPANY: method set_COMPANY_TICKER should be 'public/protected virtual' or 'protected internal virtual'
StockPrices.COMPANY: method get_ASSET_TYPE_ID should be 'public/protected virtual' or 'protected internal virtual'
StockPrices.COMPANY: method set_ASSET_TYPE_ID should be 'public/protected virtual' or 'protected internal virtual'
StockPrices.COMPANY: method get_IS_ACTIVE should be 'public/protected virtual' or 'protected internal virtual'
StockPrices.COMPANY: method set_IS_ACTIVE should be 'public/protected virtual' or 'protected internal virtual'
StockPrices.COMPANY: field _Id should not be public nor internal
StockPrices.COMPANY: field _COMPANY_NAME should not be public nor internal
StockPrices.COMPANY: field _COMPANY_TICKER should not be public nor internal
StockPrices.COMPANY: field _ASSET_TYPE_ID should not be public nor internal
StockPrices.COMPANY: field _IS_ACTIVE should not be public nor internal"

This message provides a few clues.  The messages seem to indicate that my class needs to use virtual properties (ala C#).  I also assume this to mean that my F# syntax does not yield virtual properties.

So, I need to poke around a find how to create virtual properties in F#

This is what I came up with:

-----------------------------------------------------------------------------------------------
    type COMPANY() = class
   
        let mutable _Id : int = 0
        let mutable _COMPANY_NAME : string = ""
        let mutable _COMPANY_TICKER : string = ""
        let mutable _ASSET_TYPE_ID : int = 0
        let mutable _IS_ACTIVE : int = 0

        abstract Id : int with get, set
        default x.Id with get() = _Id and set(v) = _Id <- v

        abstract COMPANY_NAME : string with get, set
        default x.COMPANY_NAME with get() = _COMPANY_NAME and set(v) = _COMPANY_NAME <- v

        abstract COMPANY_TICKER : string with get, set
        default x.COMPANY_TICKER with get() = _COMPANY_TICKER and set(v) = _COMPANY_TICKER <- v

        abstract ASSET_TYPE_ID : int with get, set
        default x.ASSET_TYPE_ID with get() = _ASSET_TYPE_ID and set(v) = _ASSET_TYPE_ID <- v
   
        abstract IS_ACTIVE : int with get, set
        default x.IS_ACTIVE with get() = _IS_ACTIVE and set(v) = _IS_ACTIVE <- v

    end
-----------------------------------------------------------------------------------------------

It is very similar to my class above, however, it uses the keyword 'abstract' to create an abstract property.  The 'default' keyword (replacing the member keyword) then heads of the implementation of the property in the current class.

This is from MSDN:

"Properties can be abstract. As with methods, abstract just means that there is a virtual dispatch associated with the property. Abstract properties can be truly abstract, that is, without a definition in the same class. The class that contains such a property is therefore an abstract class. Alternatively, abstract can just mean that a property is virtual, and in that case, a definition must be present in the same class. Note that abstract properties must not be private, and if one accessor is abstract, the other must also be abstract."http://msdn.microsoft.com/en-us/library/dd483467(VS.100).aspx

If one were to refer to a POCO class in F#, this would probably be along the lines you would expect.

OK, let's give this puppy a twirl!

Kaboom!

I crash again on the same line as before.

This is my error:

NHibernate.InvalidProxyTypeException was unhandled
  Message="The following types may not be used as proxies:
StockPrices.COMPANY: field _Id should not be public nor internal
StockPrices.COMPANY: field _COMPANY_NAME should not be public nor internal
StockPrices.COMPANY: field _COMPANY_TICKER should not be public nor internal
StockPrices.COMPANY: field _ASSET_TYPE_ID should not be public nor internal
StockPrices.COMPANY: field _IS_ACTIVE should not be public nor internal"

Shorter, but still a problem.  At least I have gotten rid of the 'virtual property' errors and I am now left with a bunch of errors that seem to say that my private variable placeholders are illegal.  I find this odd, as I do not get the same message with this type of syntax in VB:

-------------------------------------------------------------------------

Option Explicit On

Namespace StockPrices

    Public Class COMPANY

        Private _ID As Integer
        Private _CompanyName As String
        Private _CompanyTicker As String

        Public Overridable Property Id() As Integer

            Get
                Id = _ID
            End Get

            Set(ByVal value As Integer)
                _ID = value
            End Set

        End Property

        Public Overridable Property COMPANY_NAME() As String

            Get
                COMPANY_NAME = _CompanyName
            End Get

            Set(ByVal value As String)
                _CompanyName = value
            End Set

        End Property

        Public Overridable Property COMPANY_TICKER() As String

            Get
                COMPANY_TICKER = _CompanyTicker
            End Get

            Set(ByVal value As String)
                _CompanyTicker = value
            End Set

        End Property

    End Class

End Namespace

-------------------------------------------------------------------------

The above outline for a class works fine with Fluent NHibernate.

What to do?

Well, I know my class would be OK if I could just get rid of the proxy errors.  I mean these variables should be irrelevant, no?

So, I found a way to suppress them:

properties.Add("use_proxy_validator", "false")

I just need to add the above line to my properties collection.  This kills the proxy validator.

I know this could have unknown repercussions, so if anyone knows a legitimate way around this problem, I would sure like to know.

Low and behold, this actually works.

I get exactly what I expect!

My output:

-------------------------------------------------------------------------

NHibernate: SELECT company0_.Id as Id0_0_, company0_.IS_ACTIVE as IS2_0_0_, company0_.ASSET_TYPE_ID as ASSET3_0_0_, company0_.COMPANY_TICKER as COMPANY4_0_0_, company0_.COMPANY_NAME as COMPANY5_0_0_ FROM [COMPANY] company0_ WHERE company0_.Id=@p0;@p0 = 100
Company Name: AT&T,  Ticker: T

-------------------------------------------------------------------------

 We are now good!

Print | posted on Tuesday, October 13, 2009 4:44 PM |

Powered by:
Powered By Subtext Powered By ASP.NET