/// <summary> /// Update the specified container /// </summary> /// <param name="data">The data to be updated</param> /// <param name="principal">The principal for authorization</param> /// <param name="mode">The mode of operation</param> /// <returns>The updated model</returns> public TModel Update(TModel data, TransactionMode mode, IPrincipal principal) { if (data == null) { throw new ArgumentNullException(nameof(data)); } else if (data.Key == Guid.Empty) { throw new InvalidOperationException("Data missing key"); } DataPersistingEventArgs <TModel> preArgs = new DataPersistingEventArgs <TModel>(data, mode, principal); this.Updating?.Invoke(this, preArgs); if (preArgs.Cancel) { this.m_tracer.TraceWarning("Pre-Event handler indicates abort update for {0}", data); return(data); } // Persist object using (var connection = AdoAuditPersistenceService.GetConfiguration().Provider.GetWriteConnection()) { connection.Open(); using (IDbTransaction tx = connection.BeginTransaction()) try { //connection.Connection.Open(); connection.EstablishProvenance(principal, (data as NonVersionedEntityData)?.UpdatedByKey ?? (data as BaseEntityData)?.CreatedByKey); this.m_tracer.TraceVerbose("UPDATE {0}", data); data = this.UpdateInternal(connection, data, principal); connection.AddCacheCommit(data); data.LoadState = LoadState.FullLoad; // We just persisted this so it is fully loaded if (mode == TransactionMode.Commit) { tx.Commit(); foreach (var itm in connection.CacheOnCommit) { ApplicationServiceContext.Current.GetService <IDataCachingService>()?.Add(itm); } } else { tx.Rollback(); } var args = new DataPersistedEventArgs <TModel>(data, mode, principal); this.Updated?.Invoke(this, args); return(data); } catch (DbException e) { #if DEBUG this.m_tracer.TraceError("Error : {0} -- {1}", e, this.ObjectToString(data)); #else this.m_tracer.TraceError("Error : {0}", e.Message); #endif tx?.Rollback(); this.TranslateDbException(e); throw; } catch (Exception e) { #if DEBUG this.m_tracer.TraceError("Error : {0} -- {1}", e, this.ObjectToString(data)); #else this.m_tracer.TraceError("Error : {0}", e.Message); #endif tx?.Rollback(); // if the exception is key not found, we want the caller to know // so that a potential insert can take place if (e is KeyNotFoundException) { throw new KeyNotFoundException(e.Message, e); } // if the exception is anything else, we want to throw a data persistence exception throw new DataPersistenceException(e.Message, e); } finally { } } }
/// <summary> /// Performs query logic /// </summary> /// <param name="query">The query</param> /// <param name="queryId">The query identifier</param> /// <param name="offset">The offset of the query</param> /// <param name="count">The total count to return</param> /// <param name="authContext">The authentication context</param> /// <param name="totalCount">The total results matching</param> /// <param name="fastQuery">True if fast querying should be performed</param> /// <returns>The matching results</returns> protected virtual IEnumerable <TModel> QueryInvoke(Expression <Func <TModel, bool> > query, Guid queryId, int offset, int?count, IPrincipal authContext, out int totalCount, bool fastQuery, ModelSort <TModel>[] orderBy) { if (query == null) { throw new ArgumentNullException(nameof(query)); } #if DEBUG Stopwatch sw = new Stopwatch(); sw.Start(); #endif QueryRequestEventArgs <TModel> preArgs = new QueryRequestEventArgs <TModel>(query, offset, count, queryId, authContext, orderBy); this.Querying?.Invoke(this, preArgs); if (preArgs.Cancel) { this.m_tracer.TraceWarning("Pre-Event handler indicates abort query {0}", query); totalCount = preArgs.TotalResults; return(preArgs.Results); } // Query object using (var connection = AdoAuditPersistenceService.GetConfiguration().Provider.GetReadonlyConnection()) try { connection.Open(); this.m_tracer.TraceVerbose("QUERY {0}", query); // Is there an obsoletion item already specified? if (fastQuery) { connection.AddData("loadFast", true); connection.LoadState = LoadState.PartialLoad; } else { connection.LoadState = LoadState.FullLoad; } var results = this.QueryInternal(connection, query, queryId, offset, count ?? 1000, out totalCount, authContext, true, orderBy); var postData = new QueryResultEventArgs <TModel>(query, results.AsQueryable(), offset, count, totalCount, queryId, authContext); this.Queried?.Invoke(this, postData); var retVal = postData.Results.ToList(); // Add to cache foreach (var i in retVal.AsParallel().Where(i => i != null)) { connection.AddCacheCommit(i); } ApplicationServiceContext.Current.GetService <IThreadPoolService>()?.QueueUserWorkItem(o => { foreach (var itm in (o as IEnumerable <IdentifiedData>)) { ApplicationServiceContext.Current.GetService <IDataCachingService>()?.Add(itm); } }, connection.CacheOnCommit.ToList()); this.m_tracer.TraceVerbose("Returning {0}..{1} or {2} results", offset, offset + (count ?? 1000), totalCount); return(retVal); } catch (NotSupportedException e) { throw new DataPersistenceException("Cannot perform LINQ query", e); } catch (Exception e) { this.m_tracer.TraceError("Error : {0}", e); throw; } finally { #if DEBUG sw.Stop(); this.m_tracer.TraceVerbose("Query {0} took {1} ms", query, sw.ElapsedMilliseconds); #endif } }
/// <summary> /// Insert the specified storage data /// </summary> /// <param name="data">The storage data to be inserted</param> /// <param name="principal">The authentication context</param> /// <param name="mode">The transaction control mode</param> /// <returns>The inserted data</returns> public TModel Insert(TModel data, TransactionMode mode, IPrincipal principal) { if (data == null) { throw new ArgumentNullException(nameof(data)); } DataPersistingEventArgs <TModel> preArgs = new DataPersistingEventArgs <TModel>(data, mode, principal); this.Inserting?.Invoke(this, preArgs); if (preArgs.Cancel) { this.m_tracer.TraceWarning("Pre-Event handler indicates abort insert for {0}", data); return(data); } // Persist object using (var connection = AdoAuditPersistenceService.GetConfiguration().Provider.GetWriteConnection()) { connection.Open(); using (IDbTransaction tx = connection.BeginTransaction()) try { connection.EstablishProvenance(principal, (data as NonVersionedEntityData)?.CreatedByKey ?? (data as BaseEntityData)?.CreatedByKey); // Disable inserting duplicate classified objects var existing = data.TryGetExisting(connection, principal, true); if (existing != null) { throw new DuplicateNameException(data.Key?.ToString()); } else { this.m_tracer.TraceVerbose("INSERT {0}", data); data = this.InsertInternal(connection, data, principal); connection.AddCacheCommit(data); } data.LoadState = LoadState.FullLoad; // We just persisted so it is fully loaded if (mode == TransactionMode.Commit) { tx.Commit(); foreach (var itm in connection.CacheOnCommit) { ApplicationServiceContext.Current.GetService <IDataCachingService>()?.Add(itm); } } else { tx.Rollback(); } var args = new DataPersistedEventArgs <TModel>(data, mode, principal); this.Inserted?.Invoke(this, args); return(data); } catch (DbException e) { #if DEBUG this.m_tracer.TraceError("Error : {0} -- {1}", e, this.ObjectToString(data)); #else this.m_tracer.TraceError("Error : {0}", e.Message); #endif tx?.Rollback(); this.TranslateDbException(e); throw; } catch (Exception e) { this.m_tracer.TraceError("Error : {0} -- {1}", e, this.ObjectToString(data)); tx?.Rollback(); throw new DataPersistenceException(e.Message, e); } } }
/// <summary> /// Obsolete the specified key data /// </summary> /// <param name="storageData">The storage data to be obsoleted</param> /// <param name="principal">The principal to use to obsolete</param> /// <param name="mode">The mode of obsoletion</param> /// <returns>The obsoleted record</returns> public TModel Obsolete(TModel data, TransactionMode mode, IPrincipal principal) { if (data == null) { throw new ArgumentNullException(nameof(data)); } else if (data.Key == Guid.Empty) { throw new InvalidOperationException("Data missing key"); } DataPersistingEventArgs <TModel> preArgs = new DataPersistingEventArgs <TModel>(data, mode, principal); this.Obsoleting?.Invoke(this, preArgs); if (preArgs.Cancel) { this.m_tracer.TraceWarning("Pre-Event handler indicates abort for {0}", data); return(data); } // Obsolete object using (var connection = AdoAuditPersistenceService.GetConfiguration().Provider.GetWriteConnection()) { connection.Open(); using (IDbTransaction tx = connection.BeginTransaction()) try { //connection.Connection.Open(); this.m_tracer.TraceVerbose("OBSOLETE {0}", data); connection.EstablishProvenance(principal, (data as NonVersionedEntityData)?.ObsoletedByKey ?? (data as BaseEntityData)?.ObsoletedByKey); data = this.ObsoleteInternal(connection, data, principal); connection.AddCacheCommit(data); if (mode == TransactionMode.Commit) { tx.Commit(); foreach (var itm in connection.CacheOnCommit) { ApplicationServiceContext.Current.GetService <IDataCachingService>()?.Remove(itm.Key.Value); } } else { tx.Rollback(); } var args = new DataPersistedEventArgs <TModel>(data, mode, principal); this.Obsoleted?.Invoke(this, args); return(data); } catch (Exception e) { this.m_tracer.TraceError("Error : {0}", e); tx?.Rollback(); throw new DataPersistenceException(e.Message, e); } finally { } } }
/// <summary> /// Get the specified resource /// </summary> /// <typeparam name="TIdentifier">The type of identifier</typeparam> /// <param name="containerId">The id of the identifier to get</param> /// <param name="principal">The security principal to execute as</param> /// <param name="loadFast">True if to skip loading of some properties</param> /// <returns>The loaded model</returns> public TModel Get(Guid containerId, Guid?containerVersion, bool loadFast, IPrincipal principal) { // Try the cache if available var guidIdentifier = containerId; var cacheItem = ApplicationServiceContext.Current.GetService <IDataCachingService>()?.GetCacheItem <TModel>(guidIdentifier) as TModel; if (loadFast && cacheItem != null) { return(cacheItem); } else { #if DEBUG Stopwatch sw = new Stopwatch(); sw.Start(); #endif DataRetrievingEventArgs <TModel> preArgs = new DataRetrievingEventArgs <TModel>(containerId, containerVersion, principal); this.Retrieving?.Invoke(this, preArgs); if (preArgs.Cancel) { this.m_tracer.TraceWarning("Pre-Event handler indicates abort retrieve {0}", containerId); return(null); } // Query object using (var connection = AdoAuditPersistenceService.GetConfiguration().Provider.GetReadonlyConnection()) try { connection.Open(); this.m_tracer.TraceVerbose("GET {0}", containerId); if (loadFast) { connection.AddData("loadFast", true); connection.LoadState = LoadState.PartialLoad; } else { connection.LoadState = LoadState.FullLoad; } var result = this.GetInternal(connection, guidIdentifier, principal); var postData = new DataRetrievedEventArgs <TModel>(result, principal); this.Retrieved?.Invoke(this, postData); foreach (var itm in connection.CacheOnCommit) { ApplicationServiceContext.Current.GetService <IDataCachingService>()?.Add(itm); } return(result); } catch (NotSupportedException e) { throw new DataPersistenceException("Cannot perform LINQ query", e); } catch (Exception e) { this.m_tracer.TraceError("Error : {0}", e); throw; } finally { #if DEBUG sw.Stop(); this.m_tracer.TraceVerbose("Retrieve took {0} ms", sw.ElapsedMilliseconds); #endif } } }