2
\$\begingroup\$

I am developing a single user .net desktop application that lists numerous (over 2000) contacts sorted by an algorithm.

I use NHibernate and did consider a session per use case (or conversation as it is sometimes called). However, closing the session means the next time I need the contacts NH will go to the database, which is too costly in time. To get around that, I could use the view model to find entities for my services, and then attach the detached entities to a new session. Or perhaps use a second level cache, but the ones for desktop apps seem very old and not maintained.

Rather than write that code, I instead use a session for the application's lifetime. This is not generally advised, for the following reasons:

  • Memory leak - session keeps objects alive; however, in my case, these objects are needed in memory for permanent list display (I could use a DTO just for the data needed for display, but in my case the memory footprint is not much different)

  • Session exception - all data lost; however, in my case, I flush to database on each use case, so no risk of serious data loss

I also wish to use pool threads (Task class) so the user can perform other actions whilst the algorithm is processing. As session is not thread safe, that means setting up a separate thread solely for session operations.

The following is my implementation of the repository:

public class NHibernateRepository<T>
    where T : class
{
    #region Static Fields

    private static readonly Thread nhibernateThread = new Thread(ProcessQueue) { IsBackground = false, Name = "NHibernateSession" };
    private static readonly BlockingCollection<Action> queue = new BlockingCollection<Action>();
    private static readonly EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
    private static bool closeQueue;

    #endregion

    #region Constructors and Destructors

    static NHibernateRepository()
    {
        nhibernateThread.Start();
    }

    public NHibernateRepository(SessionProvider sessionProvider)
    {
        SessionProvider = sessionProvider;

        this.QueueTask(
            () =>
            {
                Debug.Assert(sessionProvider != null);
                Session = sessionProvider.Session;
                Session.FlushMode = FlushMode.Never;
            });
    }

    #endregion

    #region Properties

    protected static ISession Session { get; set; }
    protected static SessionProvider SessionProvider { get; set; }

    #endregion

    #region Public Methods and Operators

    public void Add(T model)
    {
        this.QueueTask(
            () =>
            {
                using (ITransaction txn = Session.BeginTransaction())
                {
                    Session.Save(model);
                    txn.Commit();
                }

            });
    }

    public void CloseRepository()
    {
        closeQueue = true;
        waitHandle.Set();
    }

    public void Delete(T model)
    {
        this.QueueTask(
            () =>
            {
                using (ITransaction txn = Session.BeginTransaction())
                {
                    Session.Delete(model);
                    txn.Commit();
                }
            });

        this.QueueFlush();
    }

    public IEnumerable<T> GetAll()
    {
        IEnumerable<T> list = null;
        this.QueueTask(
            () =>
            {
                using (ITransaction txn = Session.BeginTransaction())
                {
                    if (Session.IsDirty())
                    {
                        Debug.Fail("Don't expect dirty session as flush queued after delete/update");
                    }

                    list = Session.Query<T>().ToList();
                    txn.Commit();
                }
            });

        do
        {
            if (list != null)
            {
                return list;
            }
        }
        while (true);
    }

    public void Update(T model)
    {
        this.QueueFlush();
    }

    #endregion

    #region Methods

    protected void QueueFlush()
    {
        this.QueueTask(
            () =>
            {
                using (ITransaction txn = Session.BeginTransaction())
                {
                    if (Session.IsDirty())
                    {
                        Session.Flush();
                    }

                    txn.Commit();
                }
            });
    }

    protected void QueueTask(Action action)
    {
        queue.Add(action);
        waitHandle.Set();
    }

   private static void ProcessQueue()
    {
        while (true)
        {
            if (!queue.Any())
            {
                if (closeQueue)
                {
                    Session.Dispose();
                    SessionProvider.DisposeSessionFactory();
                    return;
                }

                waitHandle.WaitOne();
                continue;
            }

            Action nextTask = queue.Take();
            nextTask();
        }

    #endregion
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.