Jason,

Thank you again for such a detailed response.  Slowly, I am beginning
to understand your suggested solution and the negatives in the
direction I was heading.  I think I can implement very close to you
have above only difference is that the UI will not be directly updated
when the SynchronizationContext moves the data to the main thread.
The main thread is a service layer which clients connect to via .NET
remoting.  So at a high level the architecture is 1 or more  clients
call a remote service to save/retrieve data.  The remote service sends
the data request to the database using the CLR threadpool and
NHibernate.  Data is sent back to the service layer who sends it back
to the requesting client via .NET remoting again.  But I do have a few
questions

1.  I think your IoC container is setting the session in the service
to session.GetCurrentSession.  Due to reasons out of my control and
the ramp up time it would take to learn and shift the  application to
use IoC, I cannot implement that now.  Perhaps the next iteration...
Where would I set the session to GetCurrentSession if I am not using
an IoC?  Would there be other major changes to your approach if you
could not use an IoC.

2.  Currently our application has domain objects which contain a
considerable amount of business logic.  A property of each domain
object is a dumb POCO dto which is automapped using Fluent and gets
sent to the database and back.  It seems unnecessary overhead to
completely separate out the dtos from the domain objects and have a
mapping layer to and from each domain and dto.  If I close the
nhibernate session after a database call then the entity is no longer
attached to a session and can be safely sent back to the main thread
where the domain entity is updated with the updated dto property.
What are your thoughts on this?

3.

On Nov 10, 4:29 pm, Jason Meckley <[email protected]> wrote:
> you do not need a singleton to move data between threads. consider the
> AsyncExecutor in the code sample. the SynchronizationContext is
> handling moving the data from the background to the UI thread.
>
> I would not transmit the domain entities across threads either,
> session is not thread safe, and moving a persisted object across
> threads will cause cross threaded sessions. Once you are ready to
> leave a thread I would transmit a DTO specific to the portion of the
> UI you are updating. you will have many small objects. but each
> objects has 1 purpose.
>
> The code is complex, but so is threading. There is no way around that.
> the code I provided is the complex part, and it's encapsulated. once
> it's in place your code looks like the presenter and you are not
> bothered with the details of the ICommandExecutor implementations.
>
> reviewing your DAO object. the MakePersistent is the worst way you can
> use NH.
> 1. you will end up writing most of the code multiple times. open
> session, begin transaction, commit, close.
> 2. you are not disposing of the transaction or session. and they are
> not within a try/finally block. if an exception is thrown you will
> have orphaned sessions.
> 3. you loose all the benefits on what ISession provides, identity map,
> unit of work, etc.
> 4. there is no need for an explicit call to save or update. the
> session will handle this for you, once the entity is associated to the
> session.
>
> the main difference I see between our approaches is where we define
> the UOW boundaries. you approach makes every call to the db it's own
> UOW. I define my UOW in the context of what the presenter requires.
>
> I would also recommend implementing an IoC, even for small/simple
> applications they make infrastructure so much easier. Of course, if
> you have not used one before, they can appear daunting.
>
> On Nov 10, 5:01 pm, mt <[email protected]> wrote:
>
> > Jason thanks for the detailed answer.  Your answer led me to discover
> > fluent nhibernate which we now adopted.  However, your code seems
> > overly complex for my situation.  I dont have an IoC container that I
> > am using at all.  I am trying to extract from your sample code what I
> > would need to do:
>
> > Essentially in the callback function i would:
> > 1.  open new session
> > 2.  begin transaction
> > 3.  perform database operation (save or retrieve)
> > 4.  end trnsaction
> > 5.  close session
> > 6.  I am thinking the only way to update the main thread is to send
> > the saved or retrieved entity to a singleton object.
>
> > Does this make sense or am I still missing something?
>
> > below is some psudo-code.
>
> >    //business layer
> >         widgetDao.Save(widget)
> >         .
> >         .
> >         .
> >         //called by business layer
> >         public T Save (T entity)
> >         {
> >                 WaitCallback workItem = new WaitCallback
> > (MakePersistent,entity);
> >                 ThreadPool.QueueUserWorkItem(workItem);
> >         }
> >    //called by thread from threadpool
> >    protected Object MakePersistent(Object entity){
> >          session = NHibernateHelper.OpenSession();
> >          session.beginTransaction()
> >          T entity = session.SaveOrUpdate(entity);
> >          session.endTransaction();
> >          session.CloseSession() ;
> >          //give back to main thread through a singleton object
> >         Singleton.getInstance.entityList.Add(entity);
> >     }
>
> > On Nov 6, 1:28 pm, Fabio Maulo <[email protected]> wrote:
>
> > > Thanks... another man with good karma!
>
> > > 2009/11/6 Jason Meckley <[email protected]>
>
> > > > the wiki how-to is posted. I refactored a bit, but the intent is still
> > > > the same.
>
> > > >http://nhforge.org/wikis/howtonh/currentsessioncontext-for-desktop-de...
>
> > > > On Nov 6, 11:57 am, Fabio Maulo <[email protected]> wrote:
> > > > > Hi Jason.
> > > > > Would be nice a blog-post or an how-to wiki.
> > > >http://nhforge.org/wikis/howtonh/default.aspx
>
> > > > > 2009/11/6 Jason Meckley <[email protected]>
>
> > > > > > I have used something similar with Service Buses and Web 
> > > > > > applications.
> > > > > > really the only difference between the web and thread is how to hook
> > > > > > in. with web is begin/end request, which has easy hooks to tie into.
> > > > > > with a thread is the start/close of a new thread. the hooks do not
> > > > > > exist, so you need to provide them yourself.
>
> > > > > > I use the GetCurrentSession with all my NH projects. if you have a
> > > > > > single NH factory then ThreadStaticCurrentSessionContext is the way 
> > > > > > to
> > > > > > go.  Then you start work on the new thread open a session and bind 
> > > > > > to
> > > > > > the current session context. you can then get the current session 
> > > > > > from
> > > > > > the current factory. when the thread is closed (or about to close)
> > > > > > dispose of the session (commit transaction).
>
> > > > > > I use this approach in conjunction with TransactionScope to manage 
> > > > > > the
> > > > > > transaction.
> > > > > > ok, so what does this look like? web is explained numerous times on
> > > > > > the net. it doesn't sound like you are using a service bus to handle
> > > > > > the threadings/async. if you are the bus should have hooks for 
> > > > > > recieve/
> > > > > > complete consuming a message. this is the place to start/end the
> > > > > > session and transaction.
>
> > > > > > if you are using straight threading I would use this approach.
> > > > > > 1. have a single object which manages the threading and context
> > > > > > synchronization.
> > > > > > 2. use this object as the place to manage your Nh scope.
>
> > > > > > building off of Jeremy Miller's latest MSDN article of everyday
> > > > > > functional programming in .net we will use an Executor object to
> > > > > > manage the threading. as we off load the work to the the thread we 
> > > > > > can
> > > > > > open/close our session.
>
> > > > > > public interface ICommandExecutor
> > > > > > {
> > > > > >    void Execute(Action action);
> > > > > >    void Execute(Func<Action> action);
> > > > > > }
>
> > > > > > public class AsynchronousExecutor : ICommandExecutor
> > > > > > {
> > > > > >    private readonly SynchronizationContext synchronizationContext;
> > > > > >    private readonly ISessionFactory factory;
>
> > > > > >    public AsynchronousExecutor(ISessionFactory factory,
> > > > > > SynchronizationContext synchronizationContext)
> > > > > >    {
> > > > > >        this.synchronizationContext = synchronizationContext;
> > > > > >        this.factory = factory;
> > > > > >    }
>
> > > > > >    //useful for operations that will not have an impact on the GUI.
> > > > > > one-way operations
> > > > > >    public void Execute(Action action)
> > > > > >    {
> > > > > >        ThreadPool.QueueUserWorkItem(item =>
> > > > > >                {
> > > > > >                         using(var transaction = new TransactionScope
> > > > > > ())
> > > > > >                         using(var session = factory.OpenSession())
> > > > > >                         {
> > > > > >                                CallSessionContext.Bind(factory,
> > > > > > session);
> > > > > >                                action();
> > > > > >                                transaction.Complete();
> > > > > >                                CallSessionContext.Unbind(factory);
> > > > > >                         }
> > > > > >                });
> > > > > >    }
>
> > > > > >    //do the bulk of the work in the background, then marshal the
> > > > > > result back to the UI thread.
> > > > > >    public void Execute(Func<Action> action)
> > > > > >    {
> > > > > >        ThreadPool.QueueUserWorkItem(item =>
> > > > > >                                         {
> > > > > >                                             Action continuation =
> > > > > > null;
> > > > > >                                             using(var transaction =
> > > > > > new TransactionScope())
> > > > > >                                             using(var session =
> > > > > > factory.OpenSession())
> > > > > >                                             {
>
> > > > > > CallSessionContext.Bind(factory, session);
> > > > > >                                                    continuation =
> > > > > > action();
>
> > > > > > transaction.Complete();
>
> > > > > > CallSessionContext.Unbind(factory);
> > > > > >                                             }
>
> > > > > > synchronizationContext.Send(callBack => continuation(), null);
> > > > > >                                         });
> > > > > >    }
> > > > > > }
>
> > > > > > where action is a delegate of the work you want done. In a presenter
> > > > > > it may look like this:
> > > > > > public class Presenter
> > > > > > {
> > > > > >    private readonly IView view;
> > > > > >    private readonly IService service;
> > > > > >    private readonly ICommandExecutor executor;
>
> > > > > >    public Presenter(IView view, IService service, ICommandExecutor
> > > > > > executor)
> > > > > >    {
> > > > > >        this.view = view;
> > > > > >        this.service = service;
> > > > > >        this.executor = executor;
> > > > > >    }
>
> > > > > >    public virtual void UpdateView()
> > > > > >    {
> > > > > >        executor.Execute(() =>
> > > > > >                             {
> > > > > >                                 var data = service.GetData(); // 
> > > > > > work
> > > > > > done on background thread
> > > > > >                                 return () => view.Display(data); //
> > > > > > work done in synchronization context
> > > > > >                
>
> ...
>
> read more »
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"nhusers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/nhusers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to