New mapping style (a functional approach)

2 views
Skip to first unread message

forki23

unread,
Dec 31, 2009, 8:52:10 AM12/31/09
to FunctionalNHibernate
Hi,

before I commit my current changes I would like to know what you think
about this new mapping approach:


let configuration =
[ClassMap<Invoice>
|> Id <@fun x -> x.Id @>
|> Field <@fun x -> x.InvoiceNumber @>
|> Field <@fun x -> x.InvoiceDate @>
|> Field <@fun x -> x.Amount @>
|> Finish;
ClassMap<Employee>
|> Id <@fun x -> x.Id @>
|> Field <@fun x -> x.FirstName @>
|> Field <@fun x -> x.LastName @>
|> Finish;
ClassMap<Product>
|> Id <@fun x -> x.Id @>
|> Field <@fun x -> x.Name @>
|> Field <@fun x -> x.Price @>
|> Finish;
ClassMap<Store>
|> Id <@fun x -> x.Id @>
|> Field <@fun x -> x.Name @>
|> HasMany <@fun x -> x.Staff @>
|> Cascade All
|> HasManyToMany <@fun x -> x.Products @>
|> Cascade All
|> Table "StoreProduct"
|> Finish]

It's a more functional API and allows to use standard list
combinators.

Regards,
forki

forki23

unread,
Jan 1, 2010, 12:01:06 PM1/1/10
to FunctionalNHibernate
Hi,

please review my changes and tell me what you think.

The new mapping API doesn't need so much reflection and we don't need
the extra map classes.

I'm still not really satisfied with the Quotations. The syntax is so
weird in my eyes. Maybe we can find a way to use normal lambdas - I
know it's complicated but maybe someone finds a nice hack (e.g.
analyzing the method IL?!).

Regards,
forki

Ryan Riley

unread,
Jan 1, 2010, 9:26:12 AM1/1/10
to functional...@googlegroups.com
I wonder if we couldn't do one better. While yours is much more F#-
ish, it still looks a lot like FluentNH. What if we break from the
similar look and create an NH.map function that takes a list or seq?
Wouldn't that be more functional still? Then you should be able to
continue adding maps from, say, plugins that alter the db schema by
adding another list of maps.

Ryan

Sent from my iPhone

On Dec 31, 2009, at 7:52 AM, forki23 <steffen.forkmann@msu-

Ryan Riley

unread,
Jan 1, 2010, 9:19:34 AM1/1/10
to functional...@googlegroups.com
I like it.

Sent from my iPhone

On Dec 31, 2009, at 7:52 AM, forki23 <steffen.forkmann@msu-
solutions.de> wrote:

Robert Pickering

unread,
Jan 1, 2010, 1:16:39 PM1/1/10
to functional...@googlegroups.com
Thanks for all the great work so far Steffen.

Your changes seems like a good idea, I'll have a more detailed look at them and let you know what I think when I have a bit more time.

I'll planning to send this group a more detail email about what features I'd planning, just need to find time to write it.

Happy new year everyone, have a great 2010!

Cheers,
Rob

2010/1/1 Ryan Riley <ryan....@panesofglass.org>
I like it.


Sent from my iPhone

forki23

unread,
Jan 2, 2010, 4:57:15 AM1/2/10
to FunctionalNHibernate
Hi Ryan,

could you please describe the idea of this NH.map function in detail
(and give a signature)?

Regards,
 forki

Ryan Riley

unread,
Jan 2, 2010, 12:09:55 PM1/2/10
to functional...@googlegroups.com
On Jan 2, 2010, at 3:57 AM, forki23 <steffen.forkmann@msu-
solutions.de> wrote:
> could you please describe the idea of this NH.map function in detail
> (and give a signature)?

My thoughts are based on a modular/plugin-oriented platform I am
building, so I will try to describe it below from that perspective.

I am still trying to think of a good signature, and I don't dislike
yours. I was thinking more of a map of type Type and 'Mapper seq,
where 'Mapper is the 'NH.map'. That would give you more options for
list comprehensions, though that might not be necessary. It also more
closely follows the structure of a .hbm, though that, too, may be
undesirable. I do like your current structure but wonder what happens
if you compose the list and have multiple, conflicting ClassMaps. I
don't have the source handy to see for myself.

The idea is to think about the core idea behind FNH and do that in a
functional style. As I understand FNH, it is a conventional DSL for
creating .hbm files, which are XML. The advantages of doing this in
F#, as you have noted with your changes, are list comprehensions, lazy
evaluation, pure function transformations, and more evident
compositionality.

I think the combination of these make this project a killer addition
to NH and not merely a F#-friendly alternative to FNH. In DDD, you
often have different contexts and may want different constructions
from the same tables. One way is to just name the types differently,
but sometimes different namespaces are sufficient, esp if you want to
compose from a very loosely-coupled, modular arch.

We should be able to lazily construct the .hbm file based on the
mappings composed from a given context and adhering to any constraints
(filters, folds, etc.) for that context. I don't believe that FNH
currently offers that.

I wrote this in a very disorderly fashion, so I hope it makes sense.
As soon as I can, I will try to hack this idea into a branch.

Ryan

Cameron Taggart

unread,
Jan 3, 2010, 11:57:56 PM1/3/10
to functional...@googlegroups.com
What about using F# records and lists for the configuration? I was
experimenting with something along the lines of:

let mappedClasses =
[
{ new MappedClass<Employee>() with
override m.Properties e =
[
m.Property <@ e.FirstName @>;
m.Property <@ e.LastName @>;
{ m.Property <@ e.MobilePhone @> with Nullable=true };
]
}
]

You simply have a list of the mapped classes and within the mapped
classes, a list of the mapped properties. The rest of the code for
the experiment is:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
//open NHibernate.Mapping

type Employee =
{ Id: int;
FirstName: string;
LastName: string;
MobilePhone: string }

let getPropertyInfo (expr:Expr) =
match expr with
| PropertyGet (_, propertyInfo, []) -> propertyInfo
| _ -> failwith "Expr not of type PropertyGet"

type MappedProperty =
{ Name : string;
DataType: string;
Nullable: bool } // TODO add others

[<AbstractClass>]
type MappedClass<'a>() =
abstract member Properties: 'a -> MappedProperty list
member m.Property (expr:Expr) : MappedProperty =
let pi = getPropertyInfo expr
{ Name=pi.Name; DataType=pi.PropertyType.FullName; Nullable=false; }
member m.GetProperties() =
let d = Unchecked.defaultof<'a>
m.Properties d
// TODO used the properties when building the configuration

Cameron

forki23

unread,
Jan 4, 2010, 5:31:37 AM1/4/10
to FunctionalNHibernate
Hi Cameron,

I like the idea of changing <@fun x -> x.FirstName @> to <@
x.FirstName @>. This syntax looks much nicer. So I changed my approach
to:

let configuration =
[ClassMap (fun (x:Invoice) ->
[Id <@ x.Id @>
Field <@ x.InvoiceNumber @>
Field <@ x.InvoiceDate @>
Field <@ x.Amount @>])
ClassMap (fun (x:Employee) ->
[Id <@ x.Id @>
Field <@ x.FirstName @>
Field <@ x.LastName @>])
ClassMap (fun (x:Product) ->
[Id <@ x.Id @>
Field <@ x.Name @>
Field <@ x.Price @>])
ClassMap (fun (x:Store) ->
[Id <@ x.Id @>
Field <@ x.Name @>
HasMany <@ x.Staff @>
|> Cascade All
HasManyToMany <@ x.Products @>
|> Cascade All
|> Table "StoreProduct"])]

What do you think?

forki23

unread,
Jan 4, 2010, 5:53:23 AM1/4/10
to FunctionalNHibernate
Sorry my last version doesn't work, because I can't create a temporary
instance of Invoice.
==> I can't evaluate the (constant) lambda (fun (x:Invoice) -> y).

Seems I have to change it further...

Robert Pickering

unread,
Jan 4, 2010, 6:04:16 AM1/4/10
to functional...@googlegroups.com
Hello all,

I agree that <@ x.Id @> is better than <@ fun x -> x.Id @>. I would be good if you could get this working.

Otherwise, I think Steffen's approach is not too different to Cameron’s. I think having a function that creates a record type is a nicer than having to create a instance of an object. The only other really difference is that Steffen provides functions for manipulating the record type you don’t have to explicitly do this using the “with” keyword, I think this is a cleaner apporach.

I like the approach Steffen as taken. I was a bit cynical that it could be made to work at first since I tried to come up with something that had a similar look and feel but using union types to build up a tree like representation of a mapping. However using union types gets a bit awkward since you really want a flat record type representation for when you generate the mapping.

The one thing I’m not too keen on is having to use the “Finish” function on each mapping. This transformation could be done on the function that consumes the list of mappings. I’ll probably go ahead and implement this change soon unless there are any objections.

I’m not opposed to considering other styles of mapping but it’s probably easier if you set up a project fork rather than just posting your code here. Do feel free to send a link to the fork or other proposed changes to this list.

Cheers,
Rob

2010/1/4 forki23 <steffen....@msu-solutions.de>

forki23

unread,
Jan 4, 2010, 6:43:02 AM1/4/10
to FunctionalNHibernate
Hi Rob,

sorry for directly commiting such a change to your repository. I
created a fork at http://bitbucket.org/forki/functionalnhibernate/.

The Finish function was my first attempt to convert the 'a ClassMap to
IMappingProvider in order to put all ClassMaps into one typed list.
In my current implementation it's no longer needed.

I'm still trying to change <@ fun x -> x.Id @> to <@ x.Id @> ;-)

Regards,
Steffen

forki23

unread,
Jan 5, 2010, 5:45:00 AM1/5/10
to FunctionalNHibernate
Hi,

I added a first version of "Mapping Conventions" to my mapping
approach:

/// Sets the ColumnName = "PKey" for all Id fields
let idConvention = IDPropertyConvention (fun x -> {x with ColumnName =
Some "PKey"})

// Create a customized class map function which applies the
conventions
let ClassMap<'a> = ClassMapWithConvention<'a> [idConvention]

let configuration =
[ClassMap<Invoice>
[Id <@fun x -> x.Id @>
Field <@fun x -> x.InvoiceNumber @>
Field <@fun x -> x.InvoiceDate @>

Field <@fun x -> x.Amount @>]


ClassMap<Employee>
[Id <@fun x -> x.Id @>
Field <@fun x -> x.FirstName @>

Field <@fun x -> x.LastName @>]


ClassMap<Product>
[Id <@fun x -> x.Id @>
Field <@fun x -> x.Name @>

Field <@fun x -> x.Price @>]


ClassMap<Store>
[Id <@fun x -> x.Id @>
Field <@fun x -> x.Name @>

HasMany <@fun x -> x.Staff @>
|> Cascade All
HasManyToMany <@fun x -> x.Products @>
|> Cascade All
|> Table "StoreProduct"]]

Regards,
Steffen

Reply all
Reply to author
Forward
0 new messages