These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 
Author Message
 Post subject: NHibernate only for web applications ???
PostPosted: Thu Aug 10, 2006 9:40 pm 
Beginner
Beginner

Joined: Wed Aug 09, 2006 10:15 am
Posts: 20
Location: Vitoria - ES - Brazil
When we decided to move from our in-house ORM tool the problems found in the development of the project were getting bigger and bigger to the point that it becomes almost inviable to use it in an Winform Enterprise Application and that is why I am post this topic.

I am strugling here with my Winform project to make nHibernate work and all the documentation I found in this website and other sources are about web applications.

I found three main problems difficult to solve in a organized way:

Problem 1: How to use the same Domain Objects in UI and Domain Logic layers ??

The suggested way to use sessions is open, use it and close is:
* Open session
* business logic
* Close session

Once the session is closed, I can not access the data and relationships from objets attached this session due to lazy initialization.

However in Winform apps, as opposed to webform apps , Domain Objects can be selected directly from combobox and listbox components.
Check out example below: ( forget about programming details )

class WinClientRegistration
{
public WinClientRegistration( )
{
session = SessionFactory.Instance().Open();
// Find in ClientDAO returns a list of clients
this.cmbbxClients.Datasource = ClientDAO.Instance(session).Find();
session.Close();
}
public void ButtonInsert_Click( .... )
{
// Business Logic

session = SessionFactory.Instance().Open();

client = (Client)this.cmbbxClients.selectedItem;

if (client.Name != "James") <--- doesn't work session was closed above
{
...........
}
.................

session.Close();
}
}

Off course I can get away from that situation by doing session.Lock(client) before executing it. But later, if I want to update the UI layer, I have to lock the object back to session UI and so on so on.
In this specific example it might look simple but when you have several domain objects and several UI components that becomes what I call "Session Lock Hell".

The same problem arises when I have UI components that are dependent on the domain object selected in another UI component to bring dynamic results. I have to keep spreading open/close/lock session all over my code.

Problem 2: How can I have a Domain Object working as a singleton with lazy initialization ??

It is not expected from a ORM tool to force developers to change the way they implement a basic pattern like singleton. Unless I delegate this funcionality do DAO class to bring an instance attached to a session I dont know how can I do it with lazy initialization.

Normal singleton:
instance = ClassX.GetInstance(); <----- Get an instance in memory

Singleton solution for nHibernate:
instance = ClassXDAO.GetSingletonInstance(session); <--- Get instance by id (stored previously in class) for the session informed from DB

By the way bringing an object by id from the database doesn't configure in a singleton pattern.


Problem 3: Sessions are acumulating connections to the database. ( This is the worst one !! )(Also applies to web apps !)

Finally, when I thought I had enough problems I got this one.
I debugged my code upside down and it is really happening.
We are using Visual Studio 2003, nHibernate 1.0.2 and Ms SQL Server 2000.

Just try using SQL Trace Monitor and count how many connections are generated by your application.

We tried to close and dispose the ADO connection from the session but it didnt work.

The application started with 2 connections, (one was created during the SessionFactory building process) but this number increased up to 8 connections. Each connection was created by a session open.

Instead of the sessions to close its connections immediatly these connections are closed after some time-out. "If" they are closed because sometimes it seems not to happen.
Because of that an application using only one connection is now ( after using nHibernate ) using 8 connections and this number can be higher depending how many operations are realized by the user in a certain amount of time.





If you have a design pattern or solution to suggest please let me know.
I also think these feedback is important for nHibernate designers and if these problems are solved the adoption of this ORM tool can be much faster and it can be used in several other IT projects.


Top
 Profile  
 
 Post subject: Solution of Problem 3 ( Closing connection )
PostPosted: Fri Aug 11, 2006 9:03 am 
Beginner
Beginner

Joined: Wed Aug 09, 2006 10:15 am
Posts: 20
Location: Vitoria - ES - Brazil
By default, ADO connection is configured to work as connection pooling.
Connection pooling prevents a connection to be closed immediatly and lets the same connection to be reused in other parts of the application.

It seems that the default connection pooling configurations were tuned for web apps because there are many different users accessing the server code at the same time.

For winform apps, there is only one user for app so all I have to do is turn connection pooling off from the connection string in SQL Server:

Example:

user = XXXX; initial catalog = XXXX; server = XXXX; Pooling = false

Now the connection is closed right after the session is closed.
On the other hand I can not reuse connections previously opened.

More Info:
http://msdn.microsoft.com/library/defau ... ovider.asp


Top
 Profile  
 
 Post subject: Re: Solution of Problem 3 ( Closing connection )
PostPosted: Fri Aug 11, 2006 9:39 am 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
hcmarchezi wrote:
user = XXXX; initial catalog = XXXX; server = XXXX; Pooling = false

Now the connection is closed right after the session is closed.
On the other hand I can not reuse connections previously opened.


In this case, maybe You should configure the maximum pool size (defaults to 100) instead?
user = XXXX; initial catalog = XXXX; server = XXXX; Pooling = true; Max Pool Size = 1


Gert

_________________
If a reply helps You, rate it!


Top
 Profile  
 
 Post subject: SOLUTION FOR PROBLEM 1
PostPosted: Mon Aug 28, 2006 11:47 am 
Beginner
Beginner

Joined: Wed Aug 09, 2006 10:15 am
Posts: 20
Location: Vitoria - ES - Brazil
Finally I found a solution for problem 1.
This solution solves all combination of data inputs we have in our applications here.
Taking the problem above as an example:

Code:
class WinClientRegistration
{
// A session for the entire window is created
Session winSession = SessionFactory.Instance().Open();

public WinClientRegistration( )
{
    // Executes DAO with winSession ....
    // Inside the window implementation ony winSession
    // must be used
    this.cmbbxClients.Datasource =
    ClientDAO.Instance(winSession).Find();
}

public void ButtonInsert_Click( .... )
{
    try
    {
       // Business Logic Layer Session
       //  Another session is created for the lower layer
       // It prevents Exceptions to invalidate the entire window session
       Session businessSession = SessionFactory.Instance().Open();

        Client client = (Client)this.cmbbxClients.selectedItem;

        // Win session was not closed so this line below works
        if (client.Name != "James")
        {
            ...........
        }
   
        if ( client.Debts.Count == 0 )
        {
            ..............
        }       

        ............... the rest of the business logic
 
        // Now client james is saved with business session
        //
        // Inside this method below SaveOrUpdateCopy is used to prevent
        // the error "object is with 2 session" to occur.
        //
        DAOFactory.Instance().ClientDAO(businessSession).Save(client); 
     
    }
    catch(Exception exception)
    {
        //// Exception Hangling code
    }
    finally
    {
        businessSession.Close();
    }
}

public OnWinClientRegistration_Close(......)
{
    // Clears win session to prevent unwanted automatic dirty check
    // to happen
    winSession.Clear();
    // Releases session
    winSession.Close();
}

}


Top
 Profile  
 
 Post subject: Re: SOLUTION FOR PROBLEM 1
PostPosted: Thu Aug 31, 2006 9:35 am 
Beginner
Beginner

Joined: Wed Aug 03, 2005 8:06 am
Posts: 40
Location: Netherlands
This really is interesting. I think many of us winforms types are struggling with similar problems and have coined our own solutions. I remember a discussion in the old forum, which resembles what you propose here, but was propbably outmoded by another item in this forum.
We are moving to an approach that resembles yours. Now I'm very curious how you deal with the following issues:

- As you see in the linked item in this forum the (N)Hibernate team is dead against starting a new transaction in order to perform a lazy load of a collection in an object that was fetched in a previous transaction - and I agree. You probably evict and re-update objects in your long-lived session (to prevent the error "object is with 2 sessions")? How then do you deal will stale data and any data inconsistencies?

- If you open long-lived sessions in separate windows in an MDI interface these session can get out of sync among themselves, do you face that problem as well?

- The NHibernate session is an identity map. Now imho a session is too vulnarable (not exception-proof) to use it as an application-wide identity map, e.g. in a service layer. But if such an identity map is desirable, how can NHibernate help us?

I would really appreciate further discussion on this subject - from any contributer :)


Top
 Profile  
 
 Post subject: Explainning better the solution for problem 1
PostPosted: Mon Sep 04, 2006 8:47 am 
Beginner
Beginner

Joined: Wed Aug 09, 2006 10:15 am
Posts: 20
Location: Vitoria - ES - Brazil
Quote:
- As you see in the linked item in this forum the (N)Hibernate team is dead against starting a new transaction in order to perform a lazy load of a collection in an object that was fetched in a previous transaction - and I agree. You probably evict and re-update objects in your long-lived session (to prevent the error "object is with 2 sessions")? How then do you deal will stale data and any data inconsistencies?


Sorry, I didn´t understand well what you mean exactly with stale data.
By using SaveOrUpdate method I can get an object from one session and perform operations of save/update/delete by making use of another session.( * )
Data-inconsistencies are part of business logic, whenever something wrong happens, I raise an ApplicationException explainning to the user what went wrong.

Ex: ( Here is another example without DAO logic for clarifying purposes )

Code:

UI Layer:
--------------------
// Query operations are executed by the window session
Session winSession = SessionFactory.Instance.Open();
Client client = winSession.Load(Id);


Business Layer:
------------------------
Session businessSession = null;
try
{

    businessSession = SessionFactory.Instance.Open();

    businessSession.BeginTransaction();

    // Business Logic ..........


    // Insert/Update/Delete operations are performed by business session
    // That is the secret !! client object is attached to winSession and is being saved by businessSession.
    // This is possible because of SaveOrUpdateCopy method.
    businessSession.SaveOrUpdateCopy(client);


    // Business logic ..............

    businessSession.CommitTransaction();   


    // As window session still sees the old object data, so I force object refresh
    // by calling evict and load.
    // It only must happen if business logic was successfully executed above
    // Many times this code below is not necessary since the data was already modified by the user in the GUI interface
    winSession.Evict(client);
    client = winSession.Load(Id);

}
catch(Exception exception)
{
    businessSession.RollbackTransaction();

    // Exception handling code .............
}
finally
{
    if (businessSession != null)
    {
        businessSession.Close();
    }
}



Sorry if I am a bit repeatitive.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 04, 2006 9:10 am 
Beginner
Beginner

Joined: Wed Aug 09, 2006 10:15 am
Posts: 20
Location: Vitoria - ES - Brazil
Quote:
- If you open long-lived sessions in separate windows in an MDI interface these session can get out of sync among themselves, do you face that problem as well?


We try to solve problems here as they appear and so far we didn´t face that problem yet.

We dont try to keep updated objects everywhere in the application because we never had this problem before using NHibernate. GUI interfaces get naturally outdated the more time passes.

Quote:
- The NHibernate session is an identity map. Now imho a session is too vulnarable (not exception-proof) to use it as an application-wide identity map, e.g. in a service layer. But if such an identity map is desirable, how can NHibernate help us?


Because Session is so vulnerable, we decided to one session per window and one session per business logic transaction. (per service in your case)
If an exception occurs, most of time it is in business logic.(service layer)
Exceptions in window sessions are very rare and usually happens due to wrong nhibernate mappings or problems related to the database such as time out limit.
Anyway we decided to take that risk and if a window session exception occurs, the user will have to close and re-open it again.


This approach has recently proved to be sufficiently stable to put our software in production here.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 04, 2006 9:10 am 
Beginner
Beginner

Joined: Wed Aug 09, 2006 10:15 am
Posts: 20
Location: Vitoria - ES - Brazil
Quote:
- If you open long-lived sessions in separate windows in an MDI interface these session can get out of sync among themselves, do you face that problem as well?


We try to solve problems here as they appear and so far we didn´t face that problem yet.

We dont try to keep updated objects everywhere in the application because we never had this problem before using NHibernate. GUI interfaces get naturally outdated the more time passes.

Quote:
- The NHibernate session is an identity map. Now imho a session is too vulnarable (not exception-proof) to use it as an application-wide identity map, e.g. in a service layer. But if such an identity map is desirable, how can NHibernate help us?


Because Session is so vulnerable, we decided to one session per window and one session per business logic transaction. (per service in your case)
If an exception occurs, most of time it is in business logic.(service layer)
Exceptions in window sessions are very rare and usually happens due to wrong nhibernate mappings or problems related to the database such as time out limit.
Anyway we decided to take that risk and if a window session exception occurs, the user will have to close and re-open it again.


This approach has recently proved to be sufficiently stable to put our software in production here.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.