SessionManager
using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Web; using NHibernate; using NHibernate.Cfg; using NHibernate.Context; namespace Northwind.Repositories { /// <summary> /// A static (singleton) class to manage NHibernate. /// Manage NHibernate sessions using <see cref="Session"/> /// </summary> public sealed class SessionManager { private const string SESSIONKEY = "NHIBERNATE.SESSION"; [ThreadStatic] private static ISession _Session; //this session is not used in web private readonly Configuration _configuration; private readonly ISessionFactory _sessionFactory; #region Constructor #region Singleton public static SessionManager Instance { get { //#if !INSTANCE return Singleton.Instance; //#else // return new SessionManager(); //#endif } } //#if !INSTANCE private class Singleton { static Singleton() { } internal static readonly SessionManager Instance = new SessionManager(); } //#endif #endregion /// <summary> /// Initializes a new instance of the <see cref="SessionManager"/> class. /// </summary> private SessionManager() { _configuration = RestoreConfiguration(); if (_configuration == null) { _configuration = new Configuration(); BuildConfiguration(); } //get the session factory _sessionFactory = Configuration.BuildSessionFactory(); } private void BuildConfiguration() { Configuration.Configure(); //configure from the app.config Configuration.AddAssembly(GetType().Assembly);//default- mapping is in this assembly //other examples: //Configuration.AddFile("file.hbm.xml"); //add files //Configuration.AddAssembly(assembly); //add assembly //Configuration.AddAssembly(assemblyName); //add assembly by name //foreach (string assemblyName in assemblyNames) //add enumerable of assemblies // Configuration.AddAssembly(assemblyName); #if !DEBUG SaveConfiguration(Configuration); #endif } #endregion #region Configuration Serialization //specify a full path if required private const string _configurationFilePath = "Configuration.save"; private static void SaveConfiguration(Configuration configuration) { IFormatter serializer = new BinaryFormatter(); using (Stream stream = File.OpenWrite(_configurationFilePath)) { try { serializer.Serialize(stream, configuration); } catch (UnauthorizedAccessException) { Console.WriteLine("No write access to " + _configurationFilePath); } } } /// <summary> /// Optimization- you could deploy the serialization file. /// </summary> private static Configuration RestoreConfiguration() { #if DEBUG return null; #endif if (!File.Exists(_configurationFilePath)) return null; IFormatter serializer = new BinaryFormatter(); using (Stream stream = File.OpenRead(_configurationFilePath)) { return serializer.Deserialize(stream) as Configuration; } } #endregion #region NHibernate Setup /// <summary> /// Gets the <see cref="NHibernate.Cfg.Configuration"/>. /// </summary> internal Configuration Configuration { get { return _configuration; } } /// <summary> /// Gets the <see cref="NHibernate.ISessionFactory"/> /// </summary> internal ISessionFactory SessionFactory { get { return _sessionFactory; } } /// <summary> /// Closes the session factory. /// </summary> public void Close() { SessionFactory.Close(); } internal static bool IsWeb { get { return (HttpContext.Current != null); } } #endregion #region NHibernate SessionContext (1.2+) /// <summary> /// Opens the conversation (if already existing, reuses it). Call this from Application_BeginPreRequestHandlerExecute /// </summary> /// <returns>A <see cref="NHibernate.ISession"/></returns> public ISession OpenConversation() { //you must set <property name="current_session_context_class">web</property> (or thread_static etc) ISession session = Session; //get the current session (or open one). We do this manually, not using SessionFactory.GetCurrentSession() //for session per conversation (otherwise remove) session.FlushMode = FlushMode.Never; //Only save on session.Flush() - because we need to commit on unbind in PauseConversation session.BeginTransaction(); //start a transaction CurrentSessionContext.Bind(session); //bind it return session; } /// <summary> /// Ends the conversation. If an exception occurs, rethrows it ensuring session is closed. Call this (or <see cref="PauseConversation"/> if session per conversation) from Application_PostRequestHandlerExecute /// </summary> public void EndConversation() { ISession session = CurrentSessionContext.Unbind(SessionFactory); if (session == null) return; try { session.Flush(); session.Transaction.Commit(); } catch (Exception) { session.Transaction.Rollback(); throw; } finally { session.Close(); } } /// <summary> /// Pauses the conversation. Call this (or <see cref="EndConversation"/>) from Application_EndRequest /// </summary> public void PauseConversation() { ISession session = CurrentSessionContext.Unbind(SessionFactory); if (session == null) return; try { session.Transaction.Commit(); //with flushMode=Never, this closes connections but doesn't flush } catch (Exception) { session.Transaction.Rollback(); throw; } //we don't close the session, and it's still in Asp SessionState } #endregion #region NHibernate Sessions /// <summary> /// Explicitly open a session. If you have an open session, close it first. /// </summary> /// <returns>The <see cref="NHibernate.ISession"/></returns> public ISession OpenSession() { ISession session = SessionFactory.OpenSession(); if (IsWeb) HttpContext.Current.Items.Add(SESSIONKEY, session); else _Session = session; return session; } /// <summary> /// Gets the current <see cref="NHibernate.ISession"/>. Although this is a singleton, this is specific to the thread/ asp session. If you want to handle multiple sessions, use <see cref="OpenSession"/> directly. If a session it not open, a new open session is created and returned. /// </summary> /// <value>The <see cref="NHibernate.ISession"/></value> public ISession Session { get { //use threadStatic or asp session. ISession session = IsWeb ? HttpContext.Current.Items[SESSIONKEY] as ISession : _Session; //if using CurrentSessionContext, SessionFactory.GetCurrentSession() can be used //if it's an open session, that's all if (session != null && session.IsOpen) return session; //if not open, open a new session return OpenSession(); } } #endregion } }
NHibernateModule:
using System; using System.Web; using NHibernate; using NHibernate.Context; using Northwind; /// <summary> /// A simple HttpModule which loads NHibernate session /// </summary> public class NHibernateModule : IHttpModule { #region Implementation of IHttpModule /// <summary> /// Initializes a module and prepares it to handle requests. /// </summary> /// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application /// </param> public void Init(HttpApplication context) { context.BeginRequest += context_BeginRequest; context.EndRequest += context_EndRequest; } private static void context_BeginRequest(object sender, EventArgs e) { //use my session manager ISession session = SessionManager.Instance.OpenSession(); CurrentSessionContext.Bind(session); } private static void context_EndRequest(object sender, EventArgs e) { ISessionFactory sessionFactory = SessionManager.Instance.SessionFactory; ISession session = CurrentSessionContext.Unbind(sessionFactory); if (session == null) return; if (session.Transaction != null) { if (session.Transaction.IsActive) { //if there is an active session, commit it session.Transaction.Commit(); } else { // session.Transaction.Rollback(); } } session.Close(); } /// <summary> /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>. /// </summary> public void Dispose() { } #endregion } Web.config It needs to include these lines... <configuration> <!-- IIS 6 --> <system.web> <httpModules> <add name="NHibernateModule" type="NHibernateModule"/> </httpModules> </system.web> <!-- IIS 7 and Cassini. --> <system.webServer> <modules> <add name="NHibernateModule" type="NHibernateModule"/> </modules> </system.webServer> </configuration> NHibernate configuration The config file needs to include this line. <property name="current_session_context_class"> web </property>
GenericRepository:
using System; using System.Collections.Generic; using NHibernate; namespace Northwind { /// <summary> /// A generic repository. Normally this would be a base class of customized entity repositories- not directly exposed. /// </summary> /// <typeparam name="T"></typeparam> /// <remarks> /// All operations are wrapped with <see cref="TransactionRequired"/>. If there is NO open transaction, they open a transaction and commit immediately. If there is an open transaction, nothing is commited, so you should commit at a higher level. /// </remarks> public class GenericRepository<T> where T : new() { protected ISession Session { get { return SessionManager.Instance.Session; } } #region Read /// <summary> /// Loads the specified id. Throws an exception if not in database. /// </summary> public T Load(object id) { using (TransactionRequired transaction = new TransactionRequired()) { return Session.Load<T>(id); } } /// <summary> /// Gets the Id. Returns null if there is no matching row /// </summary> public T GetById(object id) { using (TransactionRequired transaction = new TransactionRequired()) { return Session.Get<T>(id); } } /// <summary> /// Finds all records. Consider <see cref="FindPage"/> for large result sets. /// </summary> public ICollection<T> FindAll() { using (TransactionRequired transaction = new TransactionRequired()) { return Session.CreateCriteria(typeof(T)).List<T>(); } } /// <summary> /// Counts the number of records. /// </summary> public int Count() { ICriteria criteria = Session.CreateCriteria(typeof(T)); using (TransactionRequired transaction = new TransactionRequired()) { return criteria.SetProjection(NHibernate.Criterion.Projections.RowCount()).UniqueResult<int>(); } } /// <summary> /// Finds records by page. /// </summary> /// <param name="pageStartRow">The page start row.</param> /// <param name="pageSize">Size of the page.</param> public IList<T> FindPage(int pageStartRow, int pageSize) { ICriteria criteria = Session.CreateCriteria(typeof(T)); criteria.SetFirstResult(pageStartRow); criteria.SetMaxResults(pageSize); using (TransactionRequired transaction = new TransactionRequired()) { return criteria.List<T>(); } } /// <summary> /// Finds records by page, sorted. /// </summary> public IList<T> FindSortedPage(int pageStartRow, int pageSize, string sortBy, bool descending) { ICriteria criteria = Session.CreateCriteria(typeof(T)); if (descending) criteria.AddOrder(NHibernate.Criterion.Order.Desc(sortBy)); else criteria.AddOrder(NHibernate.Criterion.Order.Asc(sortBy)); criteria.SetFirstResult(pageStartRow); criteria.SetMaxResults(pageSize); using (TransactionRequired transaction = new TransactionRequired()) { return criteria.List<T>(); } } #endregion #region Update /// <summary> /// Saves the specified object within a transaction. /// </summary> public void Save(T entity) { using (TransactionRequired transaction = new TransactionRequired()) { Session.Save(entity); transaction.Commit(); //flush to database } } /// <summary> /// Saves the specified object within a transaction. /// </summary> public void SaveOrUpdate(T entity) { using (TransactionRequired transaction = new TransactionRequired()) { Session.SaveOrUpdate(entity); transaction.Commit(); //flush to database } } /// <summary> /// Deletes the specified object within a transaction. /// </summary> public void Delete(T entity) { using (TransactionRequired transaction = new TransactionRequired()) { Session.Delete(entity); transaction.Commit(); //flush to database } } #endregion } }
TransactionRequired:
using System; using NHibernate; namespace Northwind.Repositories { /// <summary> /// Ensure a code block is transactional. /// If the session transaction is not open, create a transaction; otherwise there is an existing transaction so don't do anything. /// </summary> /// <remarks> /// Equivalent to <see cref="System.Transactions.TransactionScope"/> with default <see cref="System.Transactions.TransactionScopeOption"/> value <c>Required</c> (enlist in enclosing transaction or create a new one if it doesn't exist). /// </remarks> public sealed class TransactionRequired : IDisposable { private const string TRANSACTIONKEY = "NHIBERNATE.TRANSACTION"; private ITransaction _transaction; private bool _shouldCommit; private bool _completed; #region Constructor public TransactionRequired(ISession session) { if (session == null) throw new ArgumentNullException("session"); _transaction = session.Transaction; //equal to Transaction.Current if (!IsOpenTransaction(_transaction)) { _transaction = session.BeginTransaction(); ShouldCommit = true; } } #endregion #region NHibernate Transactions /// <summary> /// Gets or sets a value indicating whether this transaction should commit. If there is an open transaction, by default calling Commit will not do anything- it will leave the transaction open. /// </summary> /// <value><c>true</c> if should commit; otherwise, <c>false</c>.</value> public bool ShouldCommit { get { return _shouldCommit; } set { _shouldCommit = value; } } public void Commit() { if (!ShouldCommit) return; if (_completed) throw new InvalidOperationException("The current transaction is already committed. You should dispose the transaction."); _completed = true; try { if (IsOpenTransaction(_transaction)) { _transaction.Commit(); _transaction = null; } } catch (HibernateException) { RollbackTransaction(); throw; } } public void RollbackTransaction() { if (!ShouldCommit) return; _completed = true; if (IsOpenTransaction(_transaction)) _transaction.Rollback(); _transaction = null; } private static bool IsOpenTransaction(ITransaction transaction) { return transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack; } #endregion #region IDisposable Members public void Dispose() { if (!ShouldCommit) return; RollbackTransaction(); } #endregion } }
原文出处:http://www.martinwilley.com/net/code/nhibernate/index.html