maandag 28 juli 2008

NHibernate in a remoting / WCF scenario

I am thinking on how I could use NHibernate in a remoting scenario (using .NET remoting, webservices, WCF ... ), but I can already see some problems which I will likely encounter on my path.

This is how I see the big picture of the application:



Let me explain it in short:
The client application (a rich Windows client for instance) communicates via some kind of technique, be it WCF or the old .NET remoting, with the Service Layer.
This means that the client application calls a (remote) method on the Service Layer to retrieve a Customer for instance. The client can make some changes to that object and later, the client can call the remote 'SaveCustomer' method so that the Service Layer can persist the changes back to the datastore.
In order to do this, the Service Layer uses a Repository that uses NHibernate to retrieve or persist objects.
Note that the Client Application and the Remote service layer use the same Domain Entities. This means that the domain classes need to be [Serializable].

The problem that I will be facing is this:
- Since (N)Hibernate uses its ISession as a UnitOfWork, which keeps track of the objects that have been created, deleted, inserted, the Client Application doesn't know whether it is necessary to perform a remote call to save the entity or not.
(The client application doesn't know anything of some thing called an 'NHibernate Session', and my business object (entity) has no state tracking as well. (In other words: my entity itself doesn't know whether it has been created, changed or deleted).

- The remote method which will save my entity, will use another ISession then the method that has retrieved it. (Remote methods should be stateless, since multiple callers can call the same method. Client x should not know anything of client Y).
The fact that the 'SaveCustomer' method will use another ISession, means that it is possible that NHibernate will perform unnecessary UPDATE statements. This could be problematic if you use an AuditInterceptor, since this Interceptor will update the LastUpdated, Version, etc... columns in the DB, while this was not necessary. In other words: this leads to wrong information in the database.

How could these problems be tackled:
- For the first problem, you could implement some kind of 'state tracking' in your entities, and add a property which tells you whether the entity has modified , etc...

- Implementing state-tracking in your domain entities may also solve the 2nd problem; in your repository you can check whether you've to Update or Save (for new entities) your entity. However, I don't know yet how this will behave in situations where an entity contains a collection of other entities ...


I'd like to know from other people how they have tackled these kind of problems ? Did you implement some kind of state tracking in your business entities ?
Or, did you choose not to expose your business entities to the client application, and use Data Transfer Objects instead ? If so, how did you map these DTO's to your business classes ?

6 opmerkingen:

Patrick zei

Frederik,

We are building a WCF service with the backend based on NHibernate. We have chosen not to expose our domain objects to the clients since we want to be free to update/refactor the domain model whenever we want.

We have built mappers in order to translate our domain objects to DTO's.

Concerning your 'state-tracking' question I would choose to let NHibernate handle this. I think the Lock methods locks the object in a new session and thus performs the correct update/insert statement.

P.

Jan Van Ryswyck zei

Hi,

I wrote two posts on how we currently integrated NHibernate with WCF:

http://elegantcode.com/2008/05/18/integrating-castle-windsor-and-nhibernate-with-wcf/

http://elegantcode.com/2008/07/14/integrating-castle-windsor-and-nhibernate-with-wcf-throwing-the-wcf-facility-and-some-rhino-tools-in-the-mix/

Grtz,

Jan

Frederik Gheysels zei

@patrick: thx for your opinion.
I've been thinking on using separate DTO's as well, but if I can, I'd like to avoid this since this requires yet another extra mapping. But if this is the only way ... How did you implement the mapping between Domain Entities and DTO's ?
Did you describe the mapping in an xml format and used reflection in order to translate between the 2 types of classes ?

Concerning the Lock method: I'm not so sure that this will work correctly ...
When I receive the object in my remote service, the object already contains the changes.
When I lock the object in a new session, those changes will be overwritten I guess ? Or am I having it wrong here ?

@jan van ryswyck: thx for the links, I'm going to have a look ...

Frederik Gheysels zei

@patrick: instead of 'Locking' the entity in a new Session, I could use the SaveOrUpdateCopy method.
This method will load the entity out of the DB, and compare the contents of the given entity with the one that has been retrieved from the datastore so that NHibernate can determine whether an UPDATE is necessary.
Unfortunately, the additional SELECT is still being executed but I understand that it is necessary.
(If I decide to use DTO's, I guess I can use this method as well:
convert the DTO that I've been given back to a domain class, and use SaveOrUpdateCopy on that domain class.)

Now, only the first problem remains: my client needs to know if something has changed to my domain class ... But I do not see a clean solution for this.

Samuel Berthelot zei

Hello Frederik,

Thanks for the post, I can see that I'm not the only one having the same dilemma !

Out of interest, what did you choose to go for in the end ? Are you using DTOs ? I'd like to avoid them if possible as I don't want to add this extra layer.

Is the method SaveOrUpdateCopy really was the solution ?

Thanks !

VG zei

HI,

I am trying to design a application that uses NHibernate with WCF..
Client is Silverlight..

Can you guide me how to design the project structure?

Is it okay to use WSSF (WebService Software Factory) for this?

Otherwise can you share your code? (only the project structure, not the logic). I just wanted to see how you create project structure and how you use NHibernate.

Thanks