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
> > > > >                             });
> > > > >    }
> > > > > }
>
> > > > > the service could then look like this. note: I'm using AutoMapper to
> > > > > convert my entities to dtos. you could use another method. What i
> > > > > wouldn't do is return the domain entity, as these results will be
> > > > > crossing threads.
> > > > > public class Service : IService
> > > > > {
> > > > >    private readonly ISession session;
> > > > >    private readonly IMappingEngine mapper;
>
> > > > >    public Service(ISession session, IMappingEngine mapper)
> > > > >    {
> > > > >           this.session = session;
> > > > >           this.mapper = mapper;
> > > > >    }
>
> > > > >    public Dto[] GetData()
> > > > >    {
> > > > >        var entities = session.CreateCriteria<Entity>().List<Entity>
> > > > > ();
> > > > >        return mapper.Map<IList<Entity>, Dto[]>(entities);
> > > > >    }
> > > > > }
>
> > > > > your session factory needs to know about the current session context.
> > > > > I use FNH so the configuration looks like this
> > > > > var cfg = Fluently
> > > > >     .Configure()
> > > > >     .Database(...)
> > > > >     .Mappings(...)
> > > > >     .ExposeConfiguration(c=>c.SetProperty
> > > > > (Enviornment.CurrentSessionContext, typeof
> > > > > (ThreadStaticCurrentSessionContext).AssemblyQualifiedName));
>
> > > > > and then I need to inject the session into the service. I use Windsor
> > > > > for that, but any IoC should be able to.
> > > > > Kernel.AddComponentInstance(cfg);
> > > > > Kernel.AddComponentInstance(cfg.BuildSessionFactory());
> > > > > Kernel.Register(Component
> > > > >                             .For<ISession>()
> > > > >                             .Lifestyle.Is(LifestyleType.Transient)
> > > > >                             .UsingFactoryMethod<ISessionFactory>
> > > > > (f=>f.GetCurrentSession()));
>
> > > > > as a final disclaimer. this was all written from memory, so there may
> > > > > be syntax/naming errors.
>
> > > > > On Nov 6, 7:36 am, Graham Bunce <[email protected]> wrote:
> > > > > > Not sure about your explanation, but we do something similar I 
> > > > > > think.
> > > > > > If you create a session per new thread, then make sure its closed
> > > > > > before the thread
>
> ...
>
> 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