/// <summary> /// Perform the query /// </summary> protected virtual IEnumerable <Object> DoQueryInternal(DataContext context, Expression <Func <TModel, bool> > primaryQuery, Guid queryId, int offset, int?count, out int totalResults, ModelSort <TModel>[] orderBy, bool includeCount = true, bool overrideFuzzyTotalSetting = false) { #if DEBUG Stopwatch sw = new Stopwatch(); sw.Start(); #endif OrmResultSet <TQueryReturn> retVal = null; Expression <Func <TModel, bool> >[] queries = new Expression <Func <TModel, bool> >[] { primaryQuery }; // Are we intersecting? if (context.Data.TryGetValue("UNION", out object others) && others is Expression <Func <TModel, bool> >[]) { context.Data.Remove("UNION"); queries = queries.Concat((Expression <Func <TModel, bool> >[])others).ToArray(); } try { // Fetch queries foreach (var q in queries) { var query = q; SqlStatement domainQuery = null; // Query has been registered? if (queryId != Guid.Empty && this.m_queryPersistence?.IsRegistered(queryId) == true) { return(this.GetStoredQueryResults(queryId, offset, count, out totalResults)); } // Is obsoletion time already specified? If so and the entity is an iversioned entity we don't want obsolete data coming back var queryString = query.ToString(); if (!queryString.Contains("ObsoletionTime") && typeof(IVersionedEntity).IsAssignableFrom(typeof(TModel)) && !queryString.Contains("VersionKey")) { var obsoletionReference = Expression.MakeBinary(ExpressionType.Equal, Expression.MakeMemberAccess(query.Parameters[0], typeof(TModel).GetProperty(nameof(BaseEntityData.ObsoletionTime))), Expression.Constant(null)); //var obsoletionReference = Expression.MakeUnary(ExpressionType.Not, Expression.MakeMemberAccess(Expression.MakeMemberAccess(query.Parameters[0], typeof(TModel).GetProperty(nameof(BaseEntityData.ObsoletionTime))), typeof(Nullable<DateTimeOffset>).GetProperty("HasValue")), typeof(bool)); query = Expression.Lambda <Func <TModel, bool> >(Expression.MakeBinary(ExpressionType.AndAlso, obsoletionReference, query.Body), query.Parameters); } else if (!queryString.Contains("ObsoleteVersionSequenceId") && typeof(IVersionedAssociation).IsAssignableFrom(typeof(TModel))) { var obsoletionReference = Expression.MakeBinary(ExpressionType.Equal, Expression.MakeMemberAccess(query.Parameters[0], typeof(TModel).GetProperty(nameof(IVersionedAssociation.ObsoleteVersionSequenceId))), Expression.Constant(null)); //var obsoletionReference = Expression.MakeUnary(ExpressionType.Not, Expression.MakeMemberAccess(Expression.MakeMemberAccess(query.Parameters[0], typeof(TModel).GetProperty(nameof(IVersionedAssociation.ObsoleteVersionSequenceId))), typeof(Nullable<decimal>).GetProperty("HasValue")), typeof(bool)); query = Expression.Lambda <Func <TModel, bool> >(Expression.MakeBinary(ExpressionType.AndAlso, obsoletionReference, query.Body), query.Parameters); } // Domain query Type[] selectTypes = { typeof(TQueryReturn) }; if (selectTypes[0].IsConstructedGenericType) { selectTypes = selectTypes[0].GenericTypeArguments; } domainQuery = context.CreateSqlStatement <TDomain>().SelectFrom(selectTypes); var expression = m_mapper.MapModelExpression <TModel, TDomain, bool>(query, false); if (expression != null) { Type lastJoined = typeof(TDomain); if (typeof(CompositeResult).IsAssignableFrom(typeof(TQueryReturn))) { foreach (var p in typeof(TQueryReturn).GenericTypeArguments.Select(o => this.m_persistenceService.GetMapper().MapModelType(o))) { if (p != typeof(TDomain)) { // Find the FK to join domainQuery.InnerJoin(lastJoined, p); lastJoined = p; } } } domainQuery.Where <TDomain>(expression); } else { m_tracer.TraceEvent(EventLevel.Verbose, "Will use slow query construction due to complex mapped fields"); domainQuery = this.m_persistenceService.GetQueryBuilder().CreateQuery(query, orderBy); } if (retVal == null) { retVal = this.DomainQueryInternal <TQueryReturn>(context, domainQuery); } else { retVal = retVal.Union(this.DomainQueryInternal <TQueryReturn>(context, domainQuery)); } } this.AppendOrderBy(retVal.Statement, orderBy); // Count = 0 means we're not actually fetching anything so just hit the db if (count != 0) { // Stateful query identifier = We need to add query results if (queryId != Guid.Empty && ApplicationContext.Current.GetService <IQueryPersistenceService>() != null) { // Create on a separate thread the query results var keys = retVal.Keys <Guid>().ToArray(); totalResults = keys.Length; this.m_queryPersistence?.RegisterQuerySet(queryId, keys, queries, totalResults); } else if (count.HasValue && includeCount && !m_configuration.UseFuzzyTotals) // Get an exact total { totalResults = retVal.Count(); } else { totalResults = 0; } // Fuzzy totals - This will only fetch COUNT + 1 as the total results if (count.HasValue) { if ((overrideFuzzyTotalSetting || m_configuration.UseFuzzyTotals) && totalResults == 0) { var fuzzResults = retVal.Skip(offset).Take(count.Value + 1).OfType <Object>().ToList(); totalResults = fuzzResults.Count(); return(fuzzResults.Take(count.Value)); } else { return(retVal.Skip(offset).Take(count.Value).OfType <Object>()); } } else { return(retVal.Skip(offset).OfType <Object>()); } } else { totalResults = retVal.Count(); return(new List <Object>()); } } catch (Exception ex) { this.m_tracer.TraceError("Error performing underlying query: {0}", ex); if (retVal != null) { this.m_tracer.TraceEvent(EventLevel.Error, context.GetQueryLiteral(retVal.ToSqlStatement())); } context.Dispose(); // No longer important throw; } #if DEBUG finally { sw.Stop(); } #endif }
/// <summary> /// Perform the query /// </summary> protected virtual IEnumerable <Object> QueryInternal(DataContext context, Expression <Func <TModel, bool> > query, Guid queryId, int offset, int?count, out int totalResults, bool incudeCount = true) { #if DEBUG Stopwatch sw = new Stopwatch(); sw.Start(); #endif SqlStatement domainQuery = null; try { // Query has been registered? if (queryId != Guid.Empty && this.m_queryPersistence?.IsRegistered(queryId.ToString()) == true) { totalResults = (int)this.m_queryPersistence.QueryResultTotalQuantity(queryId.ToString()); var resultKeys = this.m_queryPersistence.GetQueryResults <Guid>(queryId.ToString(), offset, count.Value); return(resultKeys.Select(p => p.Id).OfType <Object>()); } // Is obsoletion time already specified? if (!query.ToString().Contains("ObsoletionTime") && typeof(BaseEntityData).IsAssignableFrom(typeof(TModel))) { var obsoletionReference = Expression.MakeBinary(ExpressionType.Equal, Expression.MakeMemberAccess(query.Parameters[0], typeof(TModel).GetProperty(nameof(BaseEntityData.ObsoletionTime))), Expression.Constant(null)); query = Expression.Lambda <Func <TModel, bool> >(Expression.MakeBinary(ExpressionType.AndAlso, obsoletionReference, query.Body), query.Parameters); } // Domain query domainQuery = context.CreateSqlStatement <TDomain>().SelectFrom(); var expression = m_mapper.MapModelExpression <TModel, TDomain>(query, false); if (expression != null) { Type lastJoined = typeof(TDomain); if (typeof(CompositeResult).IsAssignableFrom(typeof(TQueryReturn))) { foreach (var p in typeof(TQueryReturn).GenericTypeArguments.Select(o => AdoPersistenceService.GetMapper().MapModelType(o))) { if (p != typeof(TDomain)) { // Find the FK to join domainQuery.InnerJoin(lastJoined, p); lastJoined = p; } } } domainQuery.Where <TDomain>(expression); } else { m_tracer.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "Will use slow query construction due to complex mapped fields"); domainQuery = AdoPersistenceService.GetQueryBuilder().CreateQuery(query); } // Count = 0 means we're not actually fetching anything so just hit the db if (count != 0) { domainQuery = this.AppendOrderBy(domainQuery); // Query id just get the UUIDs in the db if (queryId != Guid.Empty && count != 0) { ColumnMapping pkColumn = null; if (typeof(CompositeResult).IsAssignableFrom(typeof(TQueryReturn))) { foreach (var p in typeof(TQueryReturn).GenericTypeArguments.Select(o => AdoPersistenceService.GetMapper().MapModelType(o))) { if (!typeof(DbSubTable).IsAssignableFrom(p) && !typeof(IDbVersionedData).IsAssignableFrom(p)) { pkColumn = TableMapping.Get(p).Columns.SingleOrDefault(o => o.IsPrimaryKey); break; } } } else { pkColumn = TableMapping.Get(typeof(TQueryReturn)).Columns.SingleOrDefault(o => o.IsPrimaryKey); } var keyQuery = AdoPersistenceService.GetQueryBuilder().CreateQuery(query, pkColumn).Build(); var resultKeys = context.Query <Guid>(keyQuery.Build()); //ApplicationContext.Current.GetService<IThreadPoolService>().QueueNonPooledWorkItem(a => this.m_queryPersistence?.RegisterQuerySet(queryId.ToString(), resultKeys.Select(o => new Identifier<Guid>(o)).ToArray(), query), null); // Another check this.m_queryPersistence?.RegisterQuerySet(queryId.ToString(), resultKeys.Count(), resultKeys.Select(o => new Identifier <Guid>(o)).Take(1000).ToArray(), query); ApplicationContext.Current.GetService <IThreadPoolService>().QueueNonPooledWorkItem(o => { int ofs = 1000; var rkeys = o as Guid[]; while (ofs < rkeys.Length) { this.m_queryPersistence?.AddResults(queryId.ToString(), rkeys.Skip(ofs).Take(1000).Select(k => new Identifier <Guid>(k)).ToArray()); ofs += 1000; } }, resultKeys.ToArray()); if (incudeCount) { totalResults = (int)resultKeys.Count(); } else { totalResults = 0; } var retVal = resultKeys.Skip(offset); if (count.HasValue) { retVal = retVal.Take(count.Value); } return(retVal.OfType <Object>()); } else if (incudeCount) { totalResults = context.Count(domainQuery); if (totalResults == 0) { return(new List <Object>()); } } else { totalResults = 0; } if (offset > 0) { domainQuery.Offset(offset); } if (count.HasValue) { domainQuery.Limit(count.Value); } return(this.DomainQueryInternal <TQueryReturn>(context, domainQuery, ref totalResults).OfType <Object>()); } else { totalResults = context.Count(domainQuery); return(new List <Object>()); } } catch (Exception ex) { if (domainQuery != null) { this.m_tracer.TraceEvent(TraceEventType.Error, ex.HResult, context.GetQueryLiteral(domainQuery.Build())); } context.Dispose(); // No longer important throw; } #if DEBUG finally { sw.Stop(); } #endif }
/// <summary> /// Queries for the specified model. /// </summary> /// <param name="context">The context.</param> /// <param name="query">The query.</param> /// <param name="offset">The offset.</param> /// <param name="count">The count.</param> /// <param name="totalResults">The total results.</param> /// <param name="countResults">if set to <c>true</c> [count results].</param> /// <param name="overrideAuthContext">The principal to use instead of the default.</param> /// <returns>Returns a list of the specified model instance which match the given query expression.</returns> public override IEnumerable <TModel> QueryInternal(DataContext context, Expression <Func <TModel, bool> > query, int offset, int?count, out int totalResults, bool countResults) { try { // Domain query SqlStatement domainQuery = context.CreateSqlStatement <TDomain>().SelectFrom(); var expression = ModelMapper.MapModelExpression <TModel, TDomain, bool>(query, false); if (expression != null) { Type lastJoined = typeof(TDomain); if (typeof(CompositeResult).IsAssignableFrom(typeof(TQueryReturn))) { foreach (var p in typeof(TQueryReturn).GenericTypeArguments.Select(o => ReportingPersistenceService.ModelMapper.MapModelType(o))) { if (p != typeof(TDomain)) { // Find the FK to join domainQuery.InnerJoin(lastJoined, p); lastJoined = p; } } } domainQuery.Where(expression); } else { this.traceSource.TraceEvent(EventLevel.Verbose, "Will use slow query construction due to complex mapped fields"); domainQuery = ReportingPersistenceService.QueryBuilder.CreateQuery(query); } // Build and see if the query already exists on the stack??? domainQuery = domainQuery.Build(); if (Configuration.TraceSql) { traceSource.TraceEvent(EventLevel.Verbose, "Trace SQL flag is set to true, printing SQL statement"); traceSource.TraceEvent(EventLevel.Verbose, $"GENERATED SQL STATEMENT: {domainQuery.SQL}"); } if (offset > 0) { domainQuery.Offset(offset); } if (count.HasValue) { domainQuery.Limit(count.Value); } var results = context.Query <TQueryReturn>(domainQuery).OfType <object>(); totalResults = results.Count(); return(results.Select(r => ToModelInstance(r, context))); } catch (Exception e) { traceSource.TraceEvent(EventLevel.Error, $"Unable to query: {e}"); throw; } }
/// <summary> /// Perform the query /// </summary> protected virtual IEnumerable <Object> QueryInternalEx(DataContext context, Expression <Func <TModel, bool> > query, Guid queryId, int offset, int?count, out int totalResults, bool incudeCount, ModelSort <TModel>[] orderBy) { #if DEBUG Stopwatch sw = new Stopwatch(); sw.Start(); #endif SqlStatement domainQuery = null; try { // Query has been registered? if (queryId != Guid.Empty && this.m_queryPersistence?.IsRegistered(queryId) == true) { totalResults = (int)this.m_queryPersistence.QueryResultTotalQuantity(queryId); var resultKeys = this.m_queryPersistence.GetQueryResults(queryId, offset, count.Value); return(resultKeys.OfType <Object>()); } // Is obsoletion time already specified? if (!query.ToString().Contains("ObsoletionTime") && typeof(BaseEntityData).IsAssignableFrom(typeof(TModel))) { var obsoletionReference = Expression.MakeBinary(ExpressionType.Equal, Expression.MakeMemberAccess(query.Parameters[0], typeof(TModel).GetProperty(nameof(BaseEntityData.ObsoletionTime))), Expression.Constant(null)); query = Expression.Lambda <Func <TModel, bool> >(Expression.MakeBinary(ExpressionType.AndAlso, obsoletionReference, query.Body), query.Parameters); } // Domain query domainQuery = context.CreateSqlStatement <TDomain>().SelectFrom(); var expression = m_mapper.MapModelExpression <TModel, TDomain, bool>(query, false); if (expression != null) { Type lastJoined = typeof(TDomain); if (typeof(CompositeResult).IsAssignableFrom(typeof(TQueryReturn))) { foreach (var p in typeof(TQueryReturn).GenericTypeArguments.Select(o => AdoAuditPersistenceService.GetMapper().MapModelType(o))) { if (p != typeof(TDomain)) { // Find the FK to join domainQuery.InnerJoin(lastJoined, p); lastJoined = p; } } } domainQuery.Where <TDomain>(expression); } else { m_tracer.TraceVerbose("Will use slow query construction due to complex mapped fields"); domainQuery = AdoAuditPersistenceService.GetQueryBuilder().CreateQuery(query, orderBy); } var retVal = this.DomainQueryInternal <TQueryReturn>(context, domainQuery); this.AppendOrderBy(retVal.Statement, orderBy); // Count = 0 means we're not actually fetching anything so just hit the db if (count != 0) { // Stateful query identifier = We need to add query results if (queryId != Guid.Empty && ApplicationServiceContext.Current.GetService <IQueryPersistenceService>() != null) { // Create on a separate thread the query results var keys = retVal.Keys <Guid>().ToArray(); totalResults = keys.Length; this.m_queryPersistence?.RegisterQuerySet(queryId, keys, query, totalResults); } else if (count.HasValue && !AdoAuditPersistenceService.GetConfiguration().UseFuzzyTotals) // Get an exact total { totalResults = retVal.Count(); } else { totalResults = 0; } // Fuzzy totals - This will only fetch COUNT + 1 as the total results if (count.HasValue) { if ((AdoAuditPersistenceService.GetConfiguration().UseFuzzyTotals) && totalResults == 0) { var fuzzResults = retVal.Skip(offset).Take(count.Value + 1).OfType <Object>().ToList(); totalResults = offset + fuzzResults.Count(); return(fuzzResults.Take(count.Value)); } else { return(retVal.Skip(offset).Take(count.Value).OfType <Object>()); } } else { return(retVal.Skip(offset).OfType <Object>()); } } else { totalResults = retVal.Count(); return(new List <Object>()); } } catch (Exception ex) { if (domainQuery != null) { this.m_tracer.TraceError(context.GetQueryLiteral(domainQuery.Build())); } context.Dispose(); // No longer important throw; } #if DEBUG finally { sw.Stop(); } #endif }