PageRenderTime 57ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/Source/Libraries/Umbraco.Framework.Persistence.NHibernate/DataManagement/DataContext.cs

https://bitbucket.org/cchitsiang/umbraco
C# | 246 lines | 182 code | 38 blank | 26 comment | 25 complexity | c33ceb8db6bb6510be3922845b43ac60 MD5 | raw file
Possible License(s): Apache-2.0, MPL-2.0-no-copyleft-exception, BSD-3-Clause, CC-BY-SA-3.0, LGPL-2.1
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Data;
  5. using System.Diagnostics;
  6. using System.Linq;
  7. using NHibernate;
  8. using NHibernate.Linq;
  9. using Umbraco.Foundation;
  10. using Umbraco.Foundation.Diagnostics;
  11. using Umbraco.Framework.Context;
  12. using Umbraco.Framework.DataManagement;
  13. using Umbraco.Framework.DataManagement.Linq;
  14. using Umbraco.Framework.DataManagement.Linq.QueryModel;
  15. using Umbraco.Framework.DataManagement.Linq.ResultBinding;
  16. using Umbraco.Framework.Persistence.Abstractions;
  17. using Umbraco.Framework.Persistence.DataManagement;
  18. using Umbraco.Framework.Persistence.Model.Attribution.MetaData;
  19. using Umbraco.Framework.Persistence.NHibernate.DataManagement.Linq;
  20. using Umbraco.Framework.Persistence.ProviderSupport;
  21. using Umbraco.Framework.Persistence.RdbmsModel;
  22. using Umbraco.Framework.Persistence.RdbmsModel.Mapping;
  23. using Attribute = Umbraco.Framework.Persistence.RdbmsModel.Attribute;
  24. using AttributeType = Umbraco.Framework.Persistence.Model.Attribution.MetaData.AttributeType;
  25. using ITransaction = Umbraco.Framework.DataManagement.ITransaction;
  26. namespace Umbraco.Framework.Persistence.NHibernate.DataManagement
  27. {
  28. [DebuggerDisplay("DataContext id: {_contextId}")]
  29. public class DataContext : AbstractDataContext, IQueryableDataSource
  30. {
  31. private readonly ISession _nhibernateSession;
  32. private Transaction _transaction;
  33. private readonly bool _leaveSessionOpenOnDispose;
  34. private readonly Func<object, SourceFieldBinder> _fieldBinderFactory;
  35. private readonly Guid _contextId = Guid.NewGuid();
  36. /// <summary>
  37. /// Initializes a new instance of the <see cref="DataContext"/> class with default behaviour, i.e. to close the NHibernate session when this object is disposed.
  38. /// </summary>
  39. /// <param name="hiveProvider"></param>
  40. /// <param name="nhibernateSession">The nhibernate session.</param>
  41. /// <param name="fieldBinderFactory"></param>
  42. public DataContext(IHiveProvider hiveProvider, ISession nhibernateSession, Func<object, SourceFieldBinder> fieldBinderFactory)
  43. : this(hiveProvider, nhibernateSession, false, fieldBinderFactory)
  44. { }
  45. /// <summary>
  46. /// Initializes a new instance of the <see cref="DataContext"/> class, maintaining responsibility for the disposal of the NHibernate session or not, depending on the value
  47. /// of the parameter <paramref name="leaveSessionOpenOnDispose"/>. This overload is useful for unit testing scenarios where a single session is desired for the entire duration
  48. /// of multiple tests.
  49. /// </summary>
  50. /// <param name="hiveProvider"></param>
  51. /// <param name="nhibernateSession">The nhibernate session.</param>
  52. /// <param name="leaveSessionOpenOnDispose">if set to <c>true</c> [leave session open on dispose].</param>
  53. /// <param name="fieldBinderFactory"></param>
  54. public DataContext(IHiveProvider hiveProvider, ISession nhibernateSession, bool leaveSessionOpenOnDispose, Func<object, SourceFieldBinder> fieldBinderFactory)
  55. : base(hiveProvider)
  56. {
  57. Mandate.ParameterNotNull(nhibernateSession, "nhibernateSession");
  58. Mandate.ParameterNotNull(hiveProvider, "providerContext");
  59. Mandate.That(nhibernateSession.IsOpen, x => new InvalidOperationException("Cannot create a DataContext from a closed NHibernate session"));
  60. FrameworkContext = hiveProvider.FrameworkContext;
  61. _fieldBinderFactory = fieldBinderFactory;
  62. _leaveSessionOpenOnDispose = leaveSessionOpenOnDispose;
  63. LogHelper.TraceIfEnabled<DataContext>("Creating DataContext from NHibernate session to {0}", () => nhibernateSession.Connection.Database);
  64. _nhibernateSession = nhibernateSession;
  65. }
  66. public ISession NhibernateSession
  67. {
  68. get
  69. {
  70. this.CheckThrowObjectDisposed(base.IsDisposed, "NHibernate...DataContext:NhibernateSession");
  71. return _nhibernateSession;
  72. }
  73. }
  74. public override ITransaction BeginTransaction()
  75. {
  76. this.CheckThrowObjectDisposed(base.IsDisposed, "NHibernate...DataContext:BeginTransaction");
  77. if (_transaction != null && !_transaction.IsDisposed && !_transaction.WasCommitted && !_transaction.WasRolledBack && _transaction.IsActive)
  78. {
  79. LogHelper.Warn<DataContext>("Re-using existing transaction as it wasn't disposed, committed, rolled back, and is active");
  80. return _transaction;
  81. }
  82. LogHelper.Warn<DataContext>("Calling and returning Nh.BeginTransaction()");
  83. return _transaction = new Transaction(_nhibernateSession.BeginTransaction());
  84. //TODO: Tidy this up, if we're only supporting one transaction per DataContext instance then merge CurrentTransaction with BeginTransaction
  85. if (_transaction != null)
  86. {
  87. if (_transaction.IsDisposed)
  88. {
  89. LogHelper.Warn<DataContext>("Transaction was already open but calling _nhibernateSession.BeginTransaction() anyway");
  90. _transaction = new Transaction(_nhibernateSession.BeginTransaction());
  91. }
  92. return _transaction;
  93. }
  94. var nhTransaction = _nhibernateSession.Transaction;
  95. var alreadyActive = false;
  96. if (nhTransaction != null && nhTransaction.IsActive)
  97. {
  98. LogHelper.Warn<DataContext>("Transaction already in progress on this Session");
  99. alreadyActive = true;
  100. }
  101. else
  102. nhTransaction = _nhibernateSession.BeginTransaction();
  103. _transaction = new Transaction(nhTransaction);
  104. return _transaction;
  105. }
  106. public override ITransaction CurrentTransaction
  107. {
  108. get { return _transaction; }
  109. }
  110. /// <summary>
  111. /// Gets the framework context.
  112. /// </summary>
  113. /// <remarks></remarks>
  114. public IFrameworkContext FrameworkContext { get; private set; }
  115. public override void Close()
  116. {
  117. if (_leaveSessionOpenOnDispose) return;
  118. this.CheckThrowObjectDisposed(base.IsDisposed, "NHibernate...DataContext:Close");
  119. LogHelper.TraceIfEnabled<DataContext>("Closing NHibernate session");
  120. if (NhibernateSession.IsOpen) NhibernateSession.Close();
  121. }
  122. public override void Flush()
  123. {
  124. this.CheckThrowObjectDisposed(base.IsDisposed, "NHibernate...DataContext:Flush");
  125. LogHelper.TraceIfEnabled<DataContext>("Flushing NHibernate session");
  126. NhibernateSession.Flush();
  127. }
  128. /// <summary>
  129. /// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObject"/> which handles common required locking logic.
  130. /// </summary>
  131. protected override void DisposeResources()
  132. {
  133. if (_leaveSessionOpenOnDispose) return;
  134. Close();
  135. if (NhibernateSession != null)
  136. NhibernateSession.Dispose();
  137. }
  138. public T ExecuteScalar<T>(QueryDescription query, ObjectBinder objectBinder)
  139. {
  140. var disconnected = new NHQueryOverVisitor().Visit(query.Criteria);
  141. switch (query.ResultFilter.ResultFilterType)
  142. {
  143. case ResultFilterType.Count:
  144. var queryOver = NhibernateSession.QueryOver<NodeVersion>().WithSubquery.WhereProperty(x => x.Id).In(disconnected);
  145. return queryOver.ToRowCountQuery().SingleOrDefault<T>();
  146. }
  147. var many = ExecuteMany<T>(query, objectBinder);
  148. return many.Single();
  149. }
  150. public T ExecuteSingle<T>(QueryDescription query, ObjectBinder objectBinder)
  151. {
  152. var disconnected = new NHQueryOverVisitor().Visit(query.Criteria);
  153. switch (query.ResultFilter.ResultFilterType)
  154. {
  155. case ResultFilterType.Single:
  156. case ResultFilterType.SingleOrDefault:
  157. var queryOver = NhibernateSession.QueryOver<NodeVersion>().WithSubquery.WhereProperty(x => x.Id).In(disconnected);
  158. try
  159. {
  160. var item = queryOver.SingleOrDefault<NodeVersion>();
  161. if (ReferenceEquals(item, null) && query.ResultFilter.ResultFilterType == ResultFilterType.Single)
  162. throw new InvalidOperationException(
  163. "Sequence contains 0 elements but query specified exactly 1 must be present");
  164. return FrameworkContext.TypeMappers.Map<T>(item);
  165. }
  166. catch (NonUniqueResultException ex)
  167. {
  168. const string nastyNhExceptionMessage = "query did not return a unique result: ";
  169. var getNumberFromNastyNHMessage = ex.Message.Replace(nastyNhExceptionMessage, "");
  170. throw new InvalidOperationException("Sequence contains {0} elements but query specified exactly 1 must be present.".InvariantFormat(getNumberFromNastyNHMessage), ex);
  171. }
  172. }
  173. return ExecuteMany<T>(query, objectBinder).FirstOrDefault();
  174. }
  175. public IEnumerable<T> ExecuteMany<T>(QueryDescription query, ObjectBinder objectBinder)
  176. {
  177. if (query.ResultFilter.ResultType.Equals(typeof(AttributeType)))
  178. {
  179. return ExecuteManyImpl<T, Attribute>(query, objectBinder);
  180. }
  181. return ExecuteManyImpl<T, Node>(query, objectBinder);
  182. }
  183. private IEnumerable<T> ExecuteManyImpl<T, TSource>(QueryDescription query, ObjectBinder objectBinder)
  184. {
  185. var direction = query.From.HierarchyScope;
  186. Type sourceType = typeof(NodeVersion);
  187. //var expression = new NHCriteriaVisitor().Visit(query.Criteria);
  188. var disconnected = new NHQueryOverVisitor().Visit(query.Criteria);
  189. IEnumerable<NodeVersion> resultBuilder =
  190. NhibernateSession.QueryOver<NodeVersion>().WithSubquery.WhereProperty(x => x.Id).In(disconnected).List();
  191. switch (query.ResultFilter.ResultFilterType)
  192. {
  193. case ResultFilterType.Count:
  194. //resultBuilder = resultBuilder.Count();
  195. break;
  196. case ResultFilterType.Take:
  197. resultBuilder = resultBuilder.Take(query.ResultFilter.SelectorArgument);
  198. break;
  199. }
  200. if (typeof(T).IsAssignableFrom(query.ResultFilter.ResultType))
  201. {
  202. return resultBuilder.Distinct().Select(node => FrameworkContext.TypeMappers.Map<T>(node));
  203. //return resultBuilder.Select(node => objectBinder.Execute(_fieldBinderFactory.Invoke(node))).Cast<T>();
  204. }
  205. return Enumerable.Empty<T>();
  206. }
  207. }
  208. }