/// <summary> /// Get the register /// </summary> internal static IQueryComponentPersister GetQueryPersister(Type forType) { IComponentPersister pPersister = null; if (m_persisters.TryGetValue(forType, out pPersister)) { return(pPersister as IQueryComponentPersister); } #if DEBUG Trace.TraceError("Can't find register for '{0}'", forType); #endif return(null); }
/// <summary> /// Get the persister for the specified type /// </summary> internal static IComponentPersister GetPersister(Type forType) { IComponentPersister pPersister = null; if (m_persisters.TryGetValue(forType, out pPersister)) { return(pPersister); } #if DEBUG Trace.TraceWarning("Can't find a persister for '{0}'", forType); #endif return(null); }
/// <summary> /// Persist the components of the specified container /// </summary> public static void PersistComponents(IDbConnection conn, IDbTransaction tx, bool isUpdate, IComponentPersister persister, IContainer cont) { // Now time for sub-components foreach (IComponent cmp in cont.Components) { IComponentPersister cmpp = DatabasePersistenceService.GetPersister(cmp.GetType()); if (cmpp != null) { var vid = cmpp.Persist(conn, tx, cmp, isUpdate); // Add this component to the registered components? if (vid != null) { RegisterComponent(conn, tx, Decimal.Parse(vid.Identifier), cmp, (cmp.Site.Container as HealthServiceRecordContainer).Id, cmp.Site.Container); } } else { throw new InvalidOperationException(String.Format("Cannot find persister for '{0}'", cmp.GetType().FullName)); } } }
/// <summary> /// Static constructor for this persistence service /// </summary> static DatabasePersistenceService() { m_configuration = ConfigurationManager.GetSection("marc.hi.ehrs.cr.persistence.data") as ConfigurationSectionHandler; m_persisters = new Dictionary <Type, IComponentPersister>(); // Verify that the database can be used foreach (var cm in new ConnectionManager[] { m_configuration.ConnectionManager, m_configuration.ReadonlyConnectionManager }) { var conn = cm.GetConnection(); try { var dbVer = DbUtil.GetSchemaVersion(conn); if (dbVer.CompareTo(typeof(DatabasePersistenceService).Assembly.GetName().Version) < 0) { throw new DataException(String.Format("The schema version '{0}' is less than the expected version of '{1}'", dbVer, typeof(DatabasePersistenceService).Assembly.GetName().Version)); } else { Trace.TraceInformation("Using Client Registry Schem Version '{0}'", dbVer); } } finally { conn.Close(); } } // Scan this assembly for helpers Type[] persistenceTypes = Array.FindAll <Type>(typeof(DatabasePersistenceService).Assembly.GetTypes(), o => o.GetInterface(typeof(IComponentPersister).FullName) != null); // Persistence helpers foreach (var t in persistenceTypes) { ConstructorInfo ci = t.GetConstructor(Type.EmptyTypes); IComponentPersister instance = ci.Invoke(null) as IComponentPersister; m_persisters.Add(instance.HandlesComponent, instance); } }
/// <summary> /// De-Persist components /// </summary> public static void DePersistComponents(IDbConnection conn, IContainer cont, IComponentPersister parent, bool loadFast) { List <ComponentData> tComponents = GetComponents(conn, cont, false); // Include Inverse roles? if (cont is RegistrationEvent) { tComponents.AddRange(GetComponents(conn, new HealthServiceRecordComponentRef() { Id = (cont as RegistrationEvent).Id }, true)); } // Reconstruct components foreach (var cmp in tComponents) { #if PERFMON Trace.TraceInformation("{0} : {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), cmp.m_roleType.ToString()); #endif IComponentPersister cmpp = DatabasePersistenceService.GetPersister(cmp.m_componentType); if (cmpp != null) { // Determine if we've already de-persisted the object HealthServiceRecordComponent dcComp = m_alreadyDepersisted.Find(o => o != null && o.GetType().Equals(cmp.m_componentType) && o is IIdentifiable && (o as IIdentifiable).Identifier == cmp.m_componentId && (cmp.m_componentVersionId == default(Decimal) || (o as IIdentifiable).VersionIdentifier == cmp.m_componentVersionId)); //Trace.TraceInformation("DePersist {0}:{1}", cmp.m_componentType, cmp.m_componentId); if (dcComp != null) { dcComp = dcComp.Clone() as HealthServiceRecordComponent; } else { if (cmpp is IVersionComponentPersister) { dcComp = (cmpp as IVersionComponentPersister).DePersist(conn, cmp.m_componentId, cmp.m_componentVersionId, cont, cmp.m_roleType, loadFast) as HealthServiceRecordComponent; } else { dcComp = cmpp.DePersist(conn, cmp.m_componentId, cont, cmp.m_roleType, loadFast) as HealthServiceRecordComponent; } if (dcComp != null) { m_alreadyDepersisted.Add(dcComp); } } // Add to the graph if (dcComp != null && dcComp.Site != null) { (dcComp.Site as HealthServiceRecordSite).SiteRoleType = cmp.m_roleType; } else if (dcComp != null) { (cont as HealthServiceRecordContainer).Add(dcComp, Guid.NewGuid().ToString(), cmp.m_roleType, null); } } else { throw new InvalidOperationException(String.Format("Cannot find persister for '{0}'", cmp.m_componentType.FullName)); } #if PERFMON Trace.TraceInformation("EO {0} : {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), cmp.m_roleType.ToString()); #endif } }
/// <summary> /// Update container /// </summary> public VersionedDomainIdentifier UpdateContainer(System.ComponentModel.IContainer storageData, DataPersistenceMode mode) { if (m_disposed) { throw new ObjectDisposedException("DatabasePersistenceService"); } if (storageData == null) { throw new ArgumentNullException("storageData"); } // Get the persister IComponentPersister persister = GetPersister(storageData.GetType()); ISystemConfigurationService configService = ApplicationContext.ConfigurationService; if (persister != null) { IDbTransaction tx = null; IDbConnection conn = null; try { conn = DatabasePersistenceService.ConnectionManager.GetConnection(); RegistrationEvent hsrEvent = storageData as RegistrationEvent; // Is the record something that we have access to? if (hsrEvent.AlternateIdentifier != null && !hsrEvent.AlternateIdentifier.Domain.Equals(configService.OidRegistrar.GetOid(ClientRegistryOids.REGISTRATION_EVENT).Oid)) { throw new ArgumentException(String.Format("The record OID '{0}' cannot be retrieved by this repository, expecting OID '{1}'", hsrEvent.AlternateIdentifier.Domain, configService.OidRegistrar.GetOid(ClientRegistryOids.REGISTRATION_EVENT).Oid)); } decimal tryDec = default(decimal); bool isDirectUpdate = false; // Is there no event identifier ? if (hsrEvent.AlternateIdentifier != null && !Decimal.TryParse(hsrEvent.AlternateIdentifier.Identifier, out tryDec)) { throw new ArgumentException(String.Format("The identifier '{0}' is not a valid identifier for this repository", hsrEvent.AlternateIdentifier.Identifier)); } else if (hsrEvent.AlternateIdentifier == null) // The alternate identifier is null ... so we need to look up the registration event to version ... interesting.... { this.EnrichRegistrationEvent(conn, tx, hsrEvent); } else { // Get the person name isDirectUpdate = true; // Explicit update } // Validate and duplicate the components that are to be loaded as part of the new version var oldHsrEvent = GetContainer(hsrEvent.AlternateIdentifier, true) as RegistrationEvent; // Get the old container if (oldHsrEvent == null) { throw new MissingPrimaryKeyException(String.Format("Record {1}^^^&{0}&ISO does not exist", hsrEvent.AlternateIdentifier.Domain, hsrEvent.AlternateIdentifier.Identifier)); } PersonPersister cp = new PersonPersister(); // Validate the old record target Person oldRecordTarget = oldHsrEvent.FindComponent(HealthServiceRecordSiteRoleType.SubjectOf) as Person, newRecordTarget = hsrEvent.FindComponent(HealthServiceRecordSiteRoleType.SubjectOf) as Person; Person verifyRecordTarget = null; if (!isDirectUpdate) { Trace.TraceInformation("Not Direct update, enriching the Patient Data"); int idCheck = 0; while (verifyRecordTarget == null || idCheck > newRecordTarget.AlternateIdentifiers.Count) { verifyRecordTarget = cp.GetPerson(conn, null, newRecordTarget.AlternateIdentifiers[idCheck++], true); } if (verifyRecordTarget == null || oldRecordTarget.Id != verifyRecordTarget.Id) { throw new ConstraintException("The update request specifies a different subject than the request currently stored"); } } else { verifyRecordTarget = oldRecordTarget; } //newRecordTarget.VersionId = verifyRecordTarget.VersionId; newRecordTarget.Id = verifyRecordTarget.Id; // VAlidate classific if (oldHsrEvent.EventClassifier != hsrEvent.EventClassifier && (hsrEvent.EventClassifier & RegistrationEventType.Register) == RegistrationEventType.None) { throw new ConstraintException("Record type mis-match between update data and the data already in the persistence store"); } if (oldHsrEvent.LanguageCode != hsrEvent.LanguageCode) { throw new ConstraintException("Record language mis-match between update data and data already in persistence store. To change the language use a \"replaces\" relationship rather than an update"); } // Are we performing a component update? If so the only two components we want freshened are the CommentOn (for adding comments) // and the ReasonFor | OlderVersionOf comment for change summaries if ((hsrEvent.EventClassifier & RegistrationEventType.ComponentEvent) != 0) { for (int i = hsrEvent.XmlComponents.Count - 1; i >= 0; i--) { if (((hsrEvent.XmlComponents[i].Site as HealthServiceRecordSite).SiteRoleType & (HealthServiceRecordSiteRoleType.CommentOn | HealthServiceRecordSiteRoleType.ReasonFor | HealthServiceRecordSiteRoleType.OlderVersionOf | HealthServiceRecordSiteRoleType.TargetOf)) == 0) { hsrEvent.Remove(hsrEvent.XmlComponents[i]); } } } // Copy over any components that aren't already specified or updated in the new record // Merge the old and new. Sets the update mode appropriately cp.MergePersons(newRecordTarget, oldRecordTarget); // Next we copy this as a replacement of hsrEvent.RemoveAllFromRole(HealthServiceRecordSiteRoleType.SubjectOf); hsrEvent.Add(newRecordTarget, "SUBJ", HealthServiceRecordSiteRoleType.SubjectOf, null); // Begin and update tx = conn.BeginTransaction(); var retVal = persister.Persist(conn, tx, storageData as IComponent, true); if (m_configuration.OverridePersistenceMode.HasValue) { mode = m_configuration.OverridePersistenceMode.Value; } if (mode == DataPersistenceMode.Production) { tx.Commit(); tx = null; // Mark conflicts if any are outstanding pointing at the old versionj if (this.m_clientRegistryMerge != null) { var existingConflicts = this.m_clientRegistryMerge.GetConflicts(oldHsrEvent.AlternateIdentifier); if (existingConflicts.Count() > 0) { Trace.TraceInformation("Obsoleting existing conlflicts resolved"); this.m_clientRegistryMerge.ObsoleteConflicts(oldHsrEvent.AlternateIdentifier); } this.m_threadPool.QueueUserWorkItem(this.MarkConflictsAsync, retVal); } // Notify register if (this.m_notificationService != null) { if (hsrEvent.Mode == RegistrationEventType.Replace) { this.m_notificationService.NotifyDuplicatesResolved(hsrEvent); } else { this.m_notificationService.NotifyUpdate(hsrEvent); } } } else { tx.Rollback(); } return(retVal); } catch (Exception e) { if (tx != null) { tx.Rollback(); } throw; } finally { if (tx != null) { tx.Dispose(); } if (conn != null) { DatabasePersistenceService.ConnectionManager.ReleaseConnection(conn); conn = null; } } } else { Trace.TraceError("Can't find a persistence handler for type '{0}'", storageData.GetType().Name); throw new DataException(String.Format("Can't persist type '{0}'", storageData.GetType().Name)); } }
/// <summary> /// Store a container /// </summary> public VersionedDomainIdentifier StoreContainer(System.ComponentModel.IContainer storageData, DataPersistenceMode mode) { if (m_disposed) { throw new ObjectDisposedException("DatabasePersistenceService"); } // Merge IEnumerable <VersionedDomainIdentifier> pid = null; var regEvent = storageData as RegistrationEvent; if (this.m_clientRegistryMerge != null && regEvent != null) { bool fuzzy = false; pid = this.m_clientRegistryMerge.FindIdConflicts(regEvent); // Do we have a match? if (pid.Count() == 0) // if we didn't find any id conflicts go to fuzzy mode { if (this.m_clientRegistryConfiguration.Configuration.Registration.AutoMerge) // we have to do this now :( { fuzzy = true; pid = this.m_clientRegistryMerge.FindFuzzyConflicts(regEvent); if (pid.Count() == 1) { regEvent.AlternateIdentifier = pid.First(); Trace.TraceInformation("Matched with {0} records (fuzzy={1}, autoOn={2}, updateEx={3})", pid.Count(), fuzzy, this.m_clientRegistryConfiguration.Configuration.Registration.AutoMerge, this.m_clientRegistryConfiguration.Configuration.Registration.UpdateIfExists); return(this.UpdateContainer(regEvent, mode)); } } } else if (this.m_clientRegistryConfiguration.Configuration.Registration.UpdateIfExists) { // Update if (pid.Count() == 1) { Trace.TraceInformation("Updating record {0} because it matched by identifier", regEvent.Id); regEvent.AlternateIdentifier = pid.First(); return(this.UpdateContainer(regEvent, mode)); } } } else { pid = new List <VersionedDomainIdentifier>(); } //// do a sanity check, have we already persisted this record? if (!ValidationSettings.AllowDuplicateRecords) { var duplicateQuery = new QueryEvent(); duplicateQuery.Add((storageData as ICloneable).Clone() as HealthServiceRecordContainer, "SUBJ", HealthServiceRecordSiteRoleType.SubjectOf, null); duplicateQuery.Add(new QueryParameters() { MatchStrength = Core.ComponentModel.MatchStrength.Exact, MatchingAlgorithm = MatchAlgorithm.Exact }, "FLT", HealthServiceRecordSiteRoleType.FilterOf, null); var storedRecords = QueryRecord(duplicateQuery as IComponent); if (storedRecords.Length != 0) { throw new DuplicateNameException(ApplicationContext.LocaleService.GetString("DTPE004")); } } // Get the persister IComponentPersister persister = GetPersister(storageData.GetType()); if (persister != null) { IDbTransaction tx = null; IDbConnection conn = null; try { conn = DatabasePersistenceService.ConnectionManager.GetConnection(); tx = conn.BeginTransaction(); var retVal = persister.Persist(conn, tx, storageData as IComponent, false); // Set the mode if (m_configuration.OverridePersistenceMode.HasValue) { mode = m_configuration.OverridePersistenceMode.Value; } // Commit or rollback if (mode == DataPersistenceMode.Production) { tx.Commit(); tx = null; // Notify that reconciliation is required and mark merge candidates if (pid.Count() > 0) { this.m_clientRegistryMerge.MarkConflicts(retVal, pid); if (this.m_notificationService != null && pid.Count() != 0) { var list = new List <VersionedDomainIdentifier>(pid) { retVal }; this.m_notificationService.NotifyReconciliationRequired(list); } } else if (this.m_clientRegistryMerge != null) { this.m_threadPool.QueueUserWorkItem(this.MarkConflictsAsync, retVal); } // Notify register if (this.m_notificationService != null && storageData is RegistrationEvent) { this.m_notificationService.NotifyRegister(storageData as RegistrationEvent); } } else { tx.Rollback(); } return(retVal); } catch (Exception e) { if (tx != null) { tx.Rollback(); } throw; } finally { if (tx != null) { tx.Dispose(); } if (conn != null) { DatabasePersistenceService.ConnectionManager.ReleaseConnection(conn); conn = null; } } } else { Trace.TraceError("Can't find a persistence handler for type '{0}'", storageData.GetType().Name); throw new DataException(String.Format("Can't persist type '{0}'", storageData.GetType().Name)); } }