/// <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> /// Try get by classifier /// </summary> public static IIdentifiedEntity TryGetExisting(this IIdentifiedEntity me, DataContext context, IPrincipal principal, bool forceDatabase = false) { // Is there a classifier? var idpInstance = AdoPersistenceService.GetPersister(me.GetType()) as IAdoPersistenceService; var cacheService = new AdoPersistenceCache(context); IIdentifiedEntity existing = null; // Forcing from database load from if (forceDatabase && me.Key.HasValue) { // HACK: This should really hit the database instead of just clearing the cache ApplicationContext.Current.GetService <IDataCachingService>()?.Remove(me.Key.Value); } //var tableType = AdoPersistenceService.GetMapper().MapModelType(me.GetType()); //if (me.GetType() != tableType) //{ // var tableMap = TableMapping.Get(tableType); // var dbExisting = context.FirstOrDefault(tableType, context.CreateSqlStatement().SelectFrom(tableType).Where($"{tableMap.Columns.FirstOrDefault(o=>o.IsPrimaryKey).Name}=?", me.Key.Value)); // if (dbExisting != null) // existing = idpInstance.ToModelInstance(dbExisting, context, principal) as IIdentifiedEntity; //} if (me.Key != Guid.Empty && me.Key != null) { existing = idpInstance.Get(context, me.Key.Value, principal) as IIdentifiedEntity; } var classAtt = me.GetType().GetCustomAttribute <KeyLookupAttribute>(); if (classAtt != null && existing == null) { // Get the domain type var dataType = AdoPersistenceService.GetMapper().MapModelType(me.GetType()); var tableMap = TableMapping.Get(dataType); // Get the classifier attribute value var classProperty = me.GetType().GetProperty(classAtt.UniqueProperty); object classifierValue = classProperty.GetValue(me); // Get the classifier // Is the classifier a UUID'd item? if (classifierValue is IIdentifiedEntity) { classifierValue = (classifierValue as IIdentifiedEntity).Key.Value; classProperty = me.GetType().GetProperty(classProperty.GetCustomAttribute <SerializationReferenceAttribute>()?.RedirectProperty ?? classProperty.Name); } // Column var column = tableMap.GetColumn(AdoPersistenceService.GetMapper().MapModelProperty(me.GetType(), dataType, classProperty)); // Now we want to query SqlStatement stmt = context.CreateSqlStatement().SelectFrom(dataType) .Where($"{column.Name} = ?", classifierValue); Guid objIdCache = Guid.Empty; IDbIdentified dataObject = null; // We've seen this before String classKey = $"{dataType}.{classifierValue}"; if (m_classIdCache.TryGetValue(classKey, out objIdCache)) { existing = cacheService?.GetCacheItem(objIdCache) as IdentifiedData; } if (existing == null) { dataObject = context.FirstOrDefault(dataType, stmt) as IDbIdentified; if (dataObject != null) { lock (m_classIdCache) if (!m_classIdCache.ContainsKey(classKey)) { m_classIdCache.Add(classKey, dataObject.Key); } var existCache = cacheService?.GetCacheItem((dataObject as IDbIdentified).Key); if (existCache != null) { existing = existCache as IdentifiedData; } else { existing = idpInstance.ToModelInstance(dataObject, context, principal) as IIdentifiedEntity; } } } } return(existing); }