/// <summary> /// Insert or update contents of the bundle /// </summary> /// <returns></returns> public override AuditBundle InsertInternal(DataContext context, AuditBundle data, IPrincipal principal) { if (data.Item == null) { return(data); } this.m_tracer.TraceInfo("Audit Bundle has {0} objects...", data.Item.Count); for (int i = 0; i < data.Item.Count; i++) { var itm = data.Item[i]; var svc = AdoAuditPersistenceService.GetPersister(itm.GetType()); this.ProgressChanged?.Invoke(this, new ProgressChangedEventArgs((float)(i + 1) / data.Item.Count, itm)); try { if (svc == null) { throw new InvalidOperationException($"Cannot find persister for {itm.GetType()}"); } if (itm.TryGetExisting(context, principal, true) != null) { this.m_tracer.TraceInfo("Will update {0} object from bundle...", itm); data.Item[i] = svc.Update(context, itm) as IdentifiedData; } else { this.m_tracer.TraceInfo("Will insert {0} object from bundle...", itm); data.Item[i] = svc.Insert(context, itm) as IdentifiedData; } } catch (TargetInvocationException e) { this.m_tracer.TraceError("Error inserting bundle: {0}", e); throw e.InnerException; } catch (Exception e) { throw new Exception($"Could not insert bundle due to sub-object persistence (bundle item {i})", e); } } // Cache items foreach (var itm in data.Item) { itm.LoadState = LoadState.FullLoad; context.AddCacheCommit(itm); } return(data); }
/// <summary> /// Ensures a model has been persisted /// </summary> public static IIdentifiedEntity EnsureExists(this IIdentifiedEntity me, DataContext context, IPrincipal principal) { if (me == null) { return(null); } // Me var vMe = me as IVersionedEntity; String dkey = String.Format("{0}.{1}", me.GetType().FullName, me.Key); IIdentifiedEntity existing = me.TryGetExisting(context, principal); var idpInstance = AdoAuditPersistenceService.GetPersister(me.GetType()); // Existing exists? if (existing != null && me.Key.HasValue) { // Exists but is an old version if ((existing as IVersionedEntity)?.VersionKey != vMe?.VersionKey && vMe?.VersionKey != null && vMe?.VersionKey != Guid.Empty) { // Update method IVersionedEntity updated = idpInstance.Update(context, me) as IVersionedEntity; me.Key = updated.Key; if (vMe != null) { vMe.VersionKey = (updated as IVersionedEntity).VersionKey; } return(updated); } return(existing); } else if (existing == null) // Insert { IIdentifiedEntity inserted = idpInstance.Insert(context, me) as IIdentifiedEntity; me.Key = inserted.Key; if (vMe != null) { vMe.VersionKey = (inserted as IVersionedEntity).VersionKey; } return(inserted); } return(existing); }
/// <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 = AdoAuditPersistenceService.GetPersister(me.GetType()) as IAdoPersistenceService; var cacheService = ApplicationServiceContext.Current.GetService <IDataCachingService>(); 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 ApplicationServiceContext.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) as IIdentifiedEntity; } var classAtt = me.GetType().GetCustomAttribute <KeyLookupAttribute>(); if (classAtt != null && existing == null) { // Get the domain type var dataType = AdoAuditPersistenceService.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(AdoAuditPersistenceService.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 ?? context.GetCacheCommit(objIdCache); } 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) as IIdentifiedEntity; } } } } return(existing); }
/// <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 }