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 terminates you should be ok. Although I'm not sure
> > > > > this actually happens, this will ensure sessions are not hanging
> > about
> > > > > in the NH session factory, unused, as the thread that created them
> > has
> > > > > died.
>
> > > > > It will also mean that threads taken from the thread pool don't have
> > > > > old data hanging about when they get used (again, not sure this
> > > > > actually happens but we've have some wierd errors that cleared up
> > when
> > > > > we were more explict over our session management). Although it *does*
> > > > > seem to work, you can't share a session between threads as sooner or
> > > > > later something will break - as we found in the early days when there
> > > > > were some bugs in our session handling code.
>
> > > > > If you want to pass the saved entity back across threads (e.g. a
> > > > > static variable or something similar) then you can, but if you then
> > > > > want to use it in the parent thread for DB access you'll need to
> > > > > attach it to a parent thread session.
>
> > > > > Is this what you meant?
>
> > > --
> > > Fabio Maulo
>
> --
> Fabio Maulo
--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---