All,
I'm going to post his email to better explain what he was trying to
do. he did not want DTOs as he wanted real business objects. He
wasn't building a service he was opening up for other businesses or
over the public internet. He wanted his windows client apps to call
WCF to get domain objects and then send the domain objects back to the
server for NH to save.
Below is his email. Does anyone have any suggestions?
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Short story (I ditched it and went to the Entity Framework) and
actually like using it a lot, unlike the 400 or so Alt.Net guys that
live in Nhibernate world and gave it the vote of no confidence. Below
I'll talk about why Nhibernate is a TERRIBLE platform for SOA and
disconnected clients. For web apps, it's a great solution, but it's
just a nightmare for marshalling.
Let me first give you a rundown of the stack I was using.
NHibernate 2.0
Rhino.Commons - NHRepository
NHibernate Query Generator
Castle Windsor
--
Codesmith Nhibernate Templates for entities and mappings
--
Services - WCF
--
WPF XBAP & Silverlight
(Summary, except I wasn't doing web)
http://frickinsweet.com/ryanlanciaux.com/post/RhinoCommons2c-NHibernate-and-ASPNET-MVC.aspx
Problem # 1: Getting Rhino.Commons Repository and UnitOfWork to work
in WCF. The reason is because you want the session to be created and
ended at the beginning and end of each request. This is sort of the
worm-hole of an issue that led me into doing a week worth of
integration programming inside of WCF with Several Custom WCF Endpoint
Behaviors, Custom WCF Context Extensions, re-implementing Ayende's
Rhino.Commons Repository to support creating the UnitofWork in the WCF
Context Extensions rather than in Threading Local Storage, because
it's not always available when you need it.
I learned afterwards that there is an AspnetCompatibilityMode, which
will then have all .SVC requests actually run through the Asp.net
pipeline so that you can use the out of the box Rhino.Commons
Repository and UnitOfWork and just have it be stored in
HttpContext.Current. But talk about inefficient.
Resources I used to fix this:
http://www.ayende.com/Blog/archive/2007/06/14/Using-NHibernate-Session-Per-Request-with-WCF-Windsor-Integration.aspx
http://vanryswyckjan.blogspot.com/2008/05/integrating-castle-windsor-and.html
Problem #2: So i thought I was out of the woods when I got the session
working right, and it was actually just the beginning. The next issue
is that the proxy objects don't serialize using the default
DataContractSerializer for several reasons.
1) They don't know about the Iesi collection classes, anything that
inherits the PersistableCollections, HashedSet, Bag, etc.
2) KnownTypes don't always work for these types.
3) Any reasonable complext object graph will not work in
serialization. The serializer will work great for tree style entity
graphs, but any type that has an entity that references a parent, you
get stack overflow exceptions (supposedly .net 3.5 ServicePack1 fixes
this issue)
For 1 and 2, you can use instead the NetContractSerializer, which will
remove cross-platform compatibility, but will work if you specify the
KnownTypes for the Iesi collection. The issue there is like I said if
you have any sort of complex object graph, with circular
relationships, it fails.
Resources:
http://lunaverse.wordpress.com/2007/05/09/remoting-using-wcf-and-nhibernate/
The DataContractSerializer is the one with built in support for
circular references.
http://www.jameskovacs.com/blog/CommentView.aspx?guid=477b077c-e65e-4547-8289-4e1bc17b3de7
1) So instead, I used the DataContractSerializer, with preserve
references and replaced the proxy collections with my knownType entity
collections. Which were all just List<Entity>, no sets or anything
like that. But there are still situations in which this doesn't work.
Resources I used for this:
http://timvasil.com/blog14/post/2008/02/WCF-serialization-with-NHibernate.aspx
My implementation for Nhibernate 2.0 is at the last comment on the
page, the reason for the stack overflow is the circular references,
anything other than a complex object graph serializes fine.
Problem #3) So I scaled back my domain model, made it ridiculously
simple just to try and get out of the weeds with this crap. The
database is about 130 tables big, so it's a fairly large domain. It's
for managing product life-cycle for Pier 1, so almost every table
touches the product table. So now I get into NHibernate gripes: I
went to turn turn off lazy loading on my entities since they were
disconnected, the session wasn't available and it throws errors.
Well, there's no way to specify a way to laod the bare minimum of an
entity, instead, it will load the *ENTIRE* object graph. So when I
went to load a single Product by a sku, it executed 5k+ queries.
Nice.
So then I re-enabled lazy loading, and thought, well, i can just eager
load only what i need and then turn off lazy loading at runtime, and
send the entity over the wire after that so that it doesn't try to get
the data it doesn't need. Turns out there is no way of turning off
lazy loading at runtime, so everytime I serialized the object and it
went over the wire, it still tries to go out and load any properties.
Argh!!!
Hrmm, write my own proxies that turn off ???
http://blechie.com/WPierce/Default.aspx
http://www.ayende.com/Blog/archive/2007/04/17/Advance-Extending-NHibernate-Proxies.aspx
http://code.google.com/p/nhibernateproxygenerator/
Or there are two other solutions that would've worked in my testing.
1 ) Manual clone of every session connected proxied object over to one
of your defined entity types before you send it over the wire. (ick)
2) Use strictly DTO's instead of exposing the domain entities. So
setup Nhibernate to map to non-persistant DTO's, so it doesn't do lazy
loading, it just pre-fetches into the DTO's. Then once the client is
done with them, re-attach to a session, re-query the data, make those
changes and persist it back to the db.
Now, normally i wouldn't mind DTO's but the architecture was already
getting a little bit hairy, and the devs that actually work there
wouldn't know the first thing to do once i've left or to try to
support it.
So basically I proved to them that NHibernate is great for web apps
and terrible for SOA clients in 3 weeks.
I tried EntityFramework and got everything wired up and working in 1
day. Although it's not perfect by any means, it's still a very nice
implementation with linq and works very well with complex object
graphs.
Sorry about the Rants, and the reallllly long email, it's the first
time i've gotten to get it off my chest :-)
Let me know if you need anything at all.
HTH,
Robert
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------