maandag 28 juli 2008
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 ?
zaterdag 5 juli 2008
As I was playing around with NHibernate today, I came accross a rather inconvenient problem. :).
Let me first explain what I wanted to achieve:
For every domain object that I save, I want to persist in the database when the entity has been created, when it has been last updated and by whom. Nothing special, just regular audit-information.
To make this all possible, I've created the following classes / interfaces:
I think this is pretty straightforward and doesn't require any further explanation.
Then, I continued with creating an NHibernate interceptor which would set the Created and Updated dates. (I could also used the
ILifecycle interface instead, but this meant that I would have a dependency to the NHibernate assembly in my 'domain classes assembly', and I don't like that. In fact, the
ILifecycle interface has been deprecated for exactly that reason).
This is an extract from my
AuditInterceptor which would perform the task I wanted (at least, I thought so ... ).
(Note that my
AuditInterceptor is NOT in the same assembly where the
AuditableEntity and other domain base class reside in. This would create a dependency from my base classes to NHibernate and again, I hate this :) ).
The AuditInterceptor (snippet):
As you can see, it is very simple: I only had to implement 2 methods of the IInterceptor interface:
OnSave, which is called when an entity is saved for the first time in the database (INSERT)
OnFlushDirty, which is called when an existing entity is dirty and has to be updated
IAuditableinterface, and if so, I just set the necessary properties (
Updated) to the appropriate values (the current DateTime).
Easy enough, simple, understandable and clean... If only this would work...
During testing, I got the following exception:
----> System.Data.SqlTypes.SqlTypeException : SqlDateTime overflow.
Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object fields,
Boolean notNull, SqlCommandInfo sql, Object obj, ISessionImplementor session)
As it turns out, NHibernate doesn't 'see' the changes you make to the entity parameter that is passed to the Interceptor methods:
You can however, change the values that are in the
state array parameter. Then NHibernate will correctly persist the changes.
But, I do not like to 'hard-code' property names as strings for obvious reasons (if you change a property, the compiler will not detect that you should change your 'hardcoded property name string', etc...).
Anyway, in order to get my interceptor to work, I have no other choice then messing around with the
In order to get rid of the 'weak-typing', I added a little bit more code.
So, now my classes look like this:
This solution is, IMHO, elegant enough to live with, and it works.
However, maybe someone else has a better, more elegant solution for this ? If so, I'd like to hear from you ...
dinsdag 1 juli 2008
I know that there has been written a lot about this topic, but somehow, I haven't found the 'sweet spot' concerning NHibernate Session Management in WinForms applications yet.
Some time ago, I've created a simple abstraction around the NHibernate ISession which would make it easier to use the ISession in my Winforms application.
Why do I want to clutter my presentation layer with NHibernate stuff, you ask ? Because Context is King.
The Repository has no notion of transactions, since the Repository doesn't know the context in where it's used.
Therefore, I like to start my Transaction in my WinForm app for instance, and pass the 'Transaction' to my repository, like this:
In the code above, the
UnitOfWorkclass is just a simple wrapper around the NHibernate ISession which allows me to start and commit or rollback a transaction, disconnect the ISession from the Database, etc... with a minimum amount of code.
The UnitOfWork class looks like this:
This approach also allows me to have multiple NHibernate ISessions opened in one application instance.
This approach also gives me full control about when to start a new UnitOfWork, and when to close a UnitOfWork.
I've been convinced that this was the way to go. Especially because I thought that you had to commit the changes you've made to an object using the same ISession as the ISession which you've used to retrieve the object if you want to avoid unnecessary
But, thanks to my collegue Thierry (who's starting to use NHibernate as well, and acted as some kind of catalysator to me so that I picked up my NHibernate quest again), it seems that my assumptions where not true:
I thought that, when you save an object to the datastore, using another ISession then the ISession you've used to retrieve the object, NHibernate would first perform a
SELECT query in order to find out whether an
INSERT or an
UPDATE statement should be executed.
This seems to be false if you do not use the 'assigned' generator class for your Id property.
So, now I'm in doubt:
- do I really need to be able to have concurrent ISessions in the same application instance ? Until now, I haven't needed it yet (so, yes, that makes it a YAGNI in fact).
- I haven't seen anyone on the net using a similar approach. I see that everyone uses some kind of 'SessionManager' like the one Billy McCafferty has written here, so this makes me doubt as well ...
This last point is also the reason for this blogpost: I'm in doubt :)
Using some kind of 'SessionManager' class allows me to do the transaction demarcation where ever I want as well. Next to that, I also do not have to pass my UnitOfWork to the repository, since the repository has access to the current Session via the SessionManager as well ...
I know that, maybe, I should just give it a try. However, I'd like to hear experiences and thoughts of other people who are using (N)Hibernate in a Rich Client environment as well.
How are you dealing with those (session management) issues ? What difficulties did you encounter ?
Note: another post of me regarding this subject can be found here
assumptions are the mother of all fuckups.