Exemple #1
0
        /// <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
        }
Exemple #2
0
        /// <summary>
        /// Query internal for versioned data elements
        /// </summary>
        protected override IEnumerable <Object> DoQueryInternal(DataContext context, Expression <Func <TModel, bool> > primaryQuery, Guid queryId, int offset, int?count, out int totalResults, ModelSort <TModel>[] orderBy, bool countResults = true, bool overrideFuzzyTotalSetting = false)
        {
#if DEBUG
            Stopwatch sw = new Stopwatch();
            sw.Start();
#endif

            // Queries to be performed
            OrmResultSet <CompositeResult <TDomain, TDomainKey> > 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
            {
                // Execute queries
                foreach (var q in queries)
                {
                    var query = q;
                    // Is obsoletion time already specified? (this is important for versioned objects if we want to get the most current version of the object)
                    if (!query.ToString().Contains("ObsoletionTime") && !query.ToString().Contains("VersionKey"))
                    {
                        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);
                    }


                    // Query has been registered?
                    if (queryId != Guid.Empty && this.m_queryPersistence?.IsRegistered(queryId) == true)
                    {
                        return(this.GetStoredQueryResults(queryId, offset, count, out totalResults));
                    }

                    SqlStatement domainQuery = null;
                    var          expr        = m_mapper.MapModelExpression <TModel, TDomain, bool>(query, false);
                    if (expr != null)
                    {
                        domainQuery = context.CreateSqlStatement <TDomain>().SelectFrom(typeof(TDomain), typeof(TDomainKey))
                                      .InnerJoin <TDomain, TDomainKey>(o => o.Key, o => o.Key)
                                      .Where <TDomain>(expr).Build();
                    }
                    else
                    {
                        domainQuery = this.m_persistenceService.GetQueryBuilder().CreateQuery(query).Build();
                    }

                    // Create or extend queries
                    if (retVal == null)
                    {
                        retVal = this.DomainQueryInternal <CompositeResult <TDomain, TDomainKey> >(context, domainQuery);
                    }
                    else
                    {
                        retVal = retVal.Union(this.DomainQueryInternal <CompositeResult <TDomain, TDomainKey> >(context, domainQuery));
                    }
                }

                // HACK: More than one query which indicates union was used, we need to wrap in a select statement to be adherent to SQL standard on Firebird and PSQL
                if (queries.Count() > 1)
                {
                    var query = this.AppendOrderBy(context.CreateSqlStatement("SELECT * FROM (").Append(retVal.ToSqlStatement()).Append(") AS domain_query "), orderBy);
                    retVal = this.DomainQueryInternal <CompositeResult <TDomain, TDomainKey> >(context, query);
                }
                else
                {
                    this.AppendOrderBy(retVal.Statement, orderBy);
                }

                // Only perform count
                if (count == 0)
                {
                    totalResults = retVal.Count();
                    return(new List <CompositeResult <TDomain, TDomainKey> >());
                }
                else
                {
                    if (queryId != Guid.Empty && ApplicationContext.Current.GetService <IQueryPersistenceService>() != null)
                    {
                        var keys = retVal.Keys <Guid>(false).ToArray();
                        totalResults = keys.Count();
                        this.m_queryPersistence?.RegisterQuerySet(queryId, keys, queries, totalResults);
                    }
                    else if (count.HasValue && countResults && !m_configuration.UseFuzzyTotals)
                    {
                        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 // We already counted as part of the queryId so no need to take + 1
                        {
                            return(retVal.Skip(offset).Take(count.Value).OfType <Object>());
                        }
                    }
                    else
                    {
                        return(retVal.Skip(offset).OfType <Object>());
                    }
                }
            }
            catch (Exception ex)
            {
                if (retVal != null)
                {
                    this.m_tracer.TraceEvent(EventLevel.Error, context.GetQueryLiteral(retVal.ToSqlStatement()));
                }
                context.Dispose(); // No longer important

                throw new DataPersistenceException("Error executing query", ex);
            }
#if DEBUG
            finally
            {
                sw.Stop();
            }
#endif
        }