/Source/Libraries/Umbraco.Framework.Persistence.NHibernate/DataManagement/DataContext.cs
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
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Data;
- using System.Diagnostics;
- using System.Linq;
- using NHibernate;
- using NHibernate.Linq;
- using Umbraco.Foundation;
- using Umbraco.Foundation.Diagnostics;
- using Umbraco.Framework.Context;
- using Umbraco.Framework.DataManagement;
- using Umbraco.Framework.DataManagement.Linq;
- using Umbraco.Framework.DataManagement.Linq.QueryModel;
- using Umbraco.Framework.DataManagement.Linq.ResultBinding;
- using Umbraco.Framework.Persistence.Abstractions;
- using Umbraco.Framework.Persistence.DataManagement;
- using Umbraco.Framework.Persistence.Model.Attribution.MetaData;
- using Umbraco.Framework.Persistence.NHibernate.DataManagement.Linq;
- using Umbraco.Framework.Persistence.ProviderSupport;
- using Umbraco.Framework.Persistence.RdbmsModel;
- using Umbraco.Framework.Persistence.RdbmsModel.Mapping;
- using Attribute = Umbraco.Framework.Persistence.RdbmsModel.Attribute;
- using AttributeType = Umbraco.Framework.Persistence.Model.Attribution.MetaData.AttributeType;
- using ITransaction = Umbraco.Framework.DataManagement.ITransaction;
-
- namespace Umbraco.Framework.Persistence.NHibernate.DataManagement
- {
- [DebuggerDisplay("DataContext id: {_contextId}")]
- public class DataContext : AbstractDataContext, IQueryableDataSource
- {
- private readonly ISession _nhibernateSession;
- private Transaction _transaction;
- private readonly bool _leaveSessionOpenOnDispose;
- private readonly Func<object, SourceFieldBinder> _fieldBinderFactory;
- private readonly Guid _contextId = Guid.NewGuid();
-
- /// <summary>
- /// 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.
- /// </summary>
- /// <param name="hiveProvider"></param>
- /// <param name="nhibernateSession">The nhibernate session.</param>
- /// <param name="fieldBinderFactory"></param>
- public DataContext(IHiveProvider hiveProvider, ISession nhibernateSession, Func<object, SourceFieldBinder> fieldBinderFactory)
- : this(hiveProvider, nhibernateSession, false, fieldBinderFactory)
- { }
-
- /// <summary>
- /// 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
- /// of the parameter <paramref name="leaveSessionOpenOnDispose"/>. This overload is useful for unit testing scenarios where a single session is desired for the entire duration
- /// of multiple tests.
- /// </summary>
- /// <param name="hiveProvider"></param>
- /// <param name="nhibernateSession">The nhibernate session.</param>
- /// <param name="leaveSessionOpenOnDispose">if set to <c>true</c> [leave session open on dispose].</param>
- /// <param name="fieldBinderFactory"></param>
- public DataContext(IHiveProvider hiveProvider, ISession nhibernateSession, bool leaveSessionOpenOnDispose, Func<object, SourceFieldBinder> fieldBinderFactory)
- : base(hiveProvider)
- {
- Mandate.ParameterNotNull(nhibernateSession, "nhibernateSession");
- Mandate.ParameterNotNull(hiveProvider, "providerContext");
- Mandate.That(nhibernateSession.IsOpen, x => new InvalidOperationException("Cannot create a DataContext from a closed NHibernate session"));
-
- FrameworkContext = hiveProvider.FrameworkContext;
- _fieldBinderFactory = fieldBinderFactory;
- _leaveSessionOpenOnDispose = leaveSessionOpenOnDispose;
-
- LogHelper.TraceIfEnabled<DataContext>("Creating DataContext from NHibernate session to {0}", () => nhibernateSession.Connection.Database);
- _nhibernateSession = nhibernateSession;
- }
-
- public ISession NhibernateSession
- {
- get
- {
- this.CheckThrowObjectDisposed(base.IsDisposed, "NHibernate...DataContext:NhibernateSession");
- return _nhibernateSession;
- }
- }
-
- public override ITransaction BeginTransaction()
- {
- this.CheckThrowObjectDisposed(base.IsDisposed, "NHibernate...DataContext:BeginTransaction");
-
- if (_transaction != null && !_transaction.IsDisposed && !_transaction.WasCommitted && !_transaction.WasRolledBack && _transaction.IsActive)
- {
- LogHelper.Warn<DataContext>("Re-using existing transaction as it wasn't disposed, committed, rolled back, and is active");
- return _transaction;
- }
- LogHelper.Warn<DataContext>("Calling and returning Nh.BeginTransaction()");
- return _transaction = new Transaction(_nhibernateSession.BeginTransaction());
-
-
-
-
-
-
- //TODO: Tidy this up, if we're only supporting one transaction per DataContext instance then merge CurrentTransaction with BeginTransaction
- if (_transaction != null)
- {
- if (_transaction.IsDisposed)
- {
- LogHelper.Warn<DataContext>("Transaction was already open but calling _nhibernateSession.BeginTransaction() anyway");
- _transaction = new Transaction(_nhibernateSession.BeginTransaction());
- }
- return _transaction;
- }
- var nhTransaction = _nhibernateSession.Transaction;
- var alreadyActive = false;
- if (nhTransaction != null && nhTransaction.IsActive)
- {
- LogHelper.Warn<DataContext>("Transaction already in progress on this Session");
- alreadyActive = true;
- }
- else
- nhTransaction = _nhibernateSession.BeginTransaction();
- _transaction = new Transaction(nhTransaction);
- return _transaction;
- }
-
- public override ITransaction CurrentTransaction
- {
- get { return _transaction; }
- }
-
- /// <summary>
- /// Gets the framework context.
- /// </summary>
- /// <remarks></remarks>
- public IFrameworkContext FrameworkContext { get; private set; }
-
- public override void Close()
- {
- if (_leaveSessionOpenOnDispose) return;
- this.CheckThrowObjectDisposed(base.IsDisposed, "NHibernate...DataContext:Close");
- LogHelper.TraceIfEnabled<DataContext>("Closing NHibernate session");
- if (NhibernateSession.IsOpen) NhibernateSession.Close();
- }
-
- public override void Flush()
- {
- this.CheckThrowObjectDisposed(base.IsDisposed, "NHibernate...DataContext:Flush");
- LogHelper.TraceIfEnabled<DataContext>("Flushing NHibernate session");
- NhibernateSession.Flush();
- }
-
-
- /// <summary>
- /// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObject"/> which handles common required locking logic.
- /// </summary>
- protected override void DisposeResources()
- {
- if (_leaveSessionOpenOnDispose) return;
-
- Close();
-
- if (NhibernateSession != null)
- NhibernateSession.Dispose();
- }
-
- public T ExecuteScalar<T>(QueryDescription query, ObjectBinder objectBinder)
- {
- var disconnected = new NHQueryOverVisitor().Visit(query.Criteria);
-
- switch (query.ResultFilter.ResultFilterType)
- {
- case ResultFilterType.Count:
- var queryOver = NhibernateSession.QueryOver<NodeVersion>().WithSubquery.WhereProperty(x => x.Id).In(disconnected);
- return queryOver.ToRowCountQuery().SingleOrDefault<T>();
- }
-
- var many = ExecuteMany<T>(query, objectBinder);
-
- return many.Single();
- }
-
- public T ExecuteSingle<T>(QueryDescription query, ObjectBinder objectBinder)
- {
- var disconnected = new NHQueryOverVisitor().Visit(query.Criteria);
-
- switch (query.ResultFilter.ResultFilterType)
- {
- case ResultFilterType.Single:
- case ResultFilterType.SingleOrDefault:
- var queryOver = NhibernateSession.QueryOver<NodeVersion>().WithSubquery.WhereProperty(x => x.Id).In(disconnected);
- try
- {
- var item = queryOver.SingleOrDefault<NodeVersion>();
-
- if (ReferenceEquals(item, null) && query.ResultFilter.ResultFilterType == ResultFilterType.Single)
- throw new InvalidOperationException(
- "Sequence contains 0 elements but query specified exactly 1 must be present");
-
- return FrameworkContext.TypeMappers.Map<T>(item);
- }
- catch (NonUniqueResultException ex)
- {
- const string nastyNhExceptionMessage = "query did not return a unique result: ";
- var getNumberFromNastyNHMessage = ex.Message.Replace(nastyNhExceptionMessage, "");
- throw new InvalidOperationException("Sequence contains {0} elements but query specified exactly 1 must be present.".InvariantFormat(getNumberFromNastyNHMessage), ex);
- }
- }
-
- return ExecuteMany<T>(query, objectBinder).FirstOrDefault();
- }
-
- public IEnumerable<T> ExecuteMany<T>(QueryDescription query, ObjectBinder objectBinder)
- {
- if (query.ResultFilter.ResultType.Equals(typeof(AttributeType)))
- {
- return ExecuteManyImpl<T, Attribute>(query, objectBinder);
- }
-
- return ExecuteManyImpl<T, Node>(query, objectBinder);
- }
-
- private IEnumerable<T> ExecuteManyImpl<T, TSource>(QueryDescription query, ObjectBinder objectBinder)
- {
- var direction = query.From.HierarchyScope;
-
- Type sourceType = typeof(NodeVersion);
- //var expression = new NHCriteriaVisitor().Visit(query.Criteria);
- var disconnected = new NHQueryOverVisitor().Visit(query.Criteria);
- IEnumerable<NodeVersion> resultBuilder =
- NhibernateSession.QueryOver<NodeVersion>().WithSubquery.WhereProperty(x => x.Id).In(disconnected).List();
-
- switch (query.ResultFilter.ResultFilterType)
- {
- case ResultFilterType.Count:
- //resultBuilder = resultBuilder.Count();
- break;
- case ResultFilterType.Take:
- resultBuilder = resultBuilder.Take(query.ResultFilter.SelectorArgument);
- break;
- }
-
- if (typeof(T).IsAssignableFrom(query.ResultFilter.ResultType))
- {
- return resultBuilder.Distinct().Select(node => FrameworkContext.TypeMappers.Map<T>(node));
- //return resultBuilder.Select(node => objectBinder.Execute(_fieldBinderFactory.Invoke(node))).Cast<T>();
- }
-
- return Enumerable.Empty<T>();
- }
- }
- }