/// <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> /// 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)); } }