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