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 @ Tuesday, October 13, 2009 4:44 PM

Comments on this entry:

Gravatar # re: F# - Fluent NHibernate - POCO Class Object
by Tony at 10/13/2009 7:38 PM

Any way that you can give us a link to the complete source code zipped with a Visual Studio solution?

Thanks for you good work,
Tony
  
Gravatar # re: F# - Fluent NHibernate - POCO Class Object
by Steve at 10/14/2009 12:47 AM

Have you looked at your class in Reflector? I suspect that you will find your backing fields are default public; and setting them as private fields like this

[<DefaultValue(true)>]
val mutable private _Id : int

would avoid having to suppress the visibiity warnings.
  
Gravatar # re: F# - Fluent NHibernate - POCO Class Object
by pete w at 10/14/2009 11:27 AM

If you use "default-lazy = false, then there is no need for the check for virtual properties.

You need to understand what this does, however. If a class does not have the virtual qualifiers, then NHibernate cannot make a dynamic proxy of the class. If NHibernate cannot make a dynamic proxy, then the class cannot be lazy-loaded.

This can potentially result in a massive load of queries generated for every object up an association tree with eager fetching, thereby hammering your database if you are not careful.
  
Gravatar # re: F# - Fluent NHibernate - POCO Class Object
by Chris Bilson at 10/27/2009 9:07 AM

I ran into a similar problem. When I look at the compiled assembly in reflector, the f# compiler seems to always generate _internal_ fields instead of private, which is what I want when I specify private and it's what NHibernate is complaining about. The only solution seems to be turning off proxy validation.
  
Gravatar # re: F# - Fluent NHibernate - POCO Class Object
by simi at 12/30/2009 2:23 AM

hi,

First of all. Thanks very much for your useful post.

I just came across your blog and wanted to drop you a note telling you how impressed I

was with the information you have posted here.

Please let me introduce you some info related to this post and I hope that it is useful

for .Net community.

There is a good C# resource site, Have alook

http://www.csharptalk.com/2009/09/c-class.html
http://www.csharptalk.com

simi
  
Gravatar # re: F# - Fluent NHibernate - POCO Class Object
by best tips to play blackjack at 1/8/2010 11:23 PM

I think to some degree the use of Java as a whipping boy, it reflects a number of fairly superficial factors. One is that... Java, ten years ago if you wanted to enhance your resume you probably wanted to jump from VB or C# to Java and that had a competitive benefit for you and you got to learn something new and interesting.
  

Your comment:

Title:
Name:
Email:
Website:
 
Italic Underline Blockquote Hyperlink
 
 
Please add 8 and 5 and type the answer here: