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
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
Sent from my iPhone
On Dec 31, 2009, at 7:52 AM, forki23 <steffen.forkmann@msu-
Sent from my iPhone
On Dec 31, 2009, at 7:52 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)?
Regards,
forki
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
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
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?
Seems I have to change it further...
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
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