/// <summary> /// Persist the person relationship /// </summary> public SVC.Core.DataTypes.VersionedDomainIdentifier Persist(System.Data.IDbConnection conn, System.Data.IDbTransaction tx, System.ComponentModel.IComponent data, bool isUpdate) { // Is this a replacement var pp = new PersonPersister(); PersonRegistrationRef refr = data as PersonRegistrationRef; Person psn = pp.GetPerson(conn, tx, refr.AlternateIdentifiers[0], true); Person cntrPsn = data.Site.Container as Person; if (psn == null || cntrPsn == null) { throw new ConstraintException(ApplicationContext.LocaleService.GetString("DBCF00B")); } else if (psn.Id == cntrPsn.Id) { throw new ConstraintException(ApplicationContext.LocaleService.GetString("DBCF00D")); } // Load the container person from DB so we get all data Person dbCntrPsn = pp.GetPerson(conn, tx, new SVC.Core.DataTypes.DomainIdentifier() { Domain = ApplicationContext.ConfigurationService.OidRegistrar.GetOid(ClientRegistryOids.CLIENT_CRID).Oid, Identifier = cntrPsn.Id.ToString() }, true); pp.MergePersons(dbCntrPsn, cntrPsn); // Load the components for the person #if DEBUG Trace.TraceInformation("Registration reference {0} > {1}", psn.Id, dbCntrPsn.Id); #endif DbUtil.DePersistComponents(conn, psn, this, true); if (psn == null || dbCntrPsn == null) { throw new ConstraintException(ApplicationContext.LocaleService.GetString("DBCF00B")); } dbCntrPsn.Site = (data.Site.Container as IComponent).Site; var role = (refr.Site as HealthServiceRecordSite).SiteRoleType; var symbolic = (refr.Site as HealthServiceRecordSite).IsSymbolic; // If true, the replacement does not cascade and is a symbolic replacement of only the identifiers listed // Replacement? if (role == HealthServiceRecordSiteRoleType.ReplacementOf) { // First, we obsolete all records with the existing person foreach (var id in psn.AlternateIdentifiers.FindAll(o => refr.AlternateIdentifiers.Exists(a => a.Domain == o.Domain))) { id.UpdateMode = SVC.Core.DataTypes.UpdateModeType.Remove; } //psn.AlternateIdentifiers.RemoveAll(o => o.UpdateMode != SVC.Core.DataTypes.UpdateModeType.Remove); // Not symbolic, means that we do a hard replace // Symbolic replace = Just replace the reference to that identifier // Hard replace = Merge the new and old record and then replace them if (!symbolic) { #if DEBUG Trace.TraceInformation("Performing an administrative migration of identifiers and information from {0} > {1}", psn.Id, dbCntrPsn.Id); #endif // Now to copy the components of the current version down //foreach (IComponent cmp in refr.Site.Container.Components) // if (cmp != refr) // dbCntrPsn.Add((cmp as HealthServiceRecordComponent).Clone() as IComponent); // Merge the two records in memory taking the newer data // This is a merge from old to new in order to capture any data elements // that have been updated in the old that might be newer (or more accurate) than the // the new if (psn.AlternateIdentifiers == null) { dbCntrPsn.AlternateIdentifiers = new List <SVC.Core.DataTypes.DomainIdentifier>(); } else if (psn.OtherIdentifiers == null) { dbCntrPsn.OtherIdentifiers = new List <KeyValuePair <SVC.Core.DataTypes.CodeValue, SVC.Core.DataTypes.DomainIdentifier> >(); } foreach (var id in psn.AlternateIdentifiers) { // Remove the identifier from the original id.UpdateMode = SVC.Core.DataTypes.UpdateModeType.Remove; // If this is a duplicate id then don't add if (dbCntrPsn.AlternateIdentifiers.Exists(i => i.Domain == id.Domain && i.Identifier == id.Identifier)) { continue; } bool isPrivate = false; var oidData = ApplicationContext.ConfigurationService.OidRegistrar.FindData(id.Domain); if (oidData != null) { isPrivate = !(oidData.Attributes.Exists(o => o.Key == "IsMergeSurvivor") && Convert.ToBoolean(oidData.Attributes.Find(o => o.Key == "IsMergeSurvivor").Value)); } // Add to alternate identifiers dbCntrPsn.AlternateIdentifiers.Add(new SVC.Core.DataTypes.DomainIdentifier() { AssigningAuthority = id.AssigningAuthority, UpdateMode = SVC.Core.DataTypes.UpdateModeType.AddOrUpdate, IsLicenseAuthority = false, IsPrivate = isPrivate, // TODO: Make this a configuration flag (cntrPsn.AlternateIdentifiers.Exists(i=>i.Domain == id.Domain)), Identifier = id.Identifier, Domain = id.Domain }); } foreach (var id in psn.OtherIdentifiers) { // Remove the identifier from the original id.Value.UpdateMode = SVC.Core.DataTypes.UpdateModeType.Remove; // If this is a duplicate id then don't add if (dbCntrPsn.OtherIdentifiers.Exists(i => i.Value.Domain == id.Value.Domain && i.Value.Identifier == id.Value.Identifier)) { continue; } // Add to other identifiers var oth = new KeyValuePair <SVC.Core.DataTypes.CodeValue, SVC.Core.DataTypes.DomainIdentifier>( id.Key, new SVC.Core.DataTypes.DomainIdentifier() { AssigningAuthority = id.Value.AssigningAuthority, UpdateMode = SVC.Core.DataTypes.UpdateModeType.Add, IsLicenseAuthority = false, IsPrivate = (dbCntrPsn.OtherIdentifiers.Exists(i => i.Value.Domain == id.Value.Domain)), Identifier = id.Value.Identifier, Domain = id.Value.Domain }); // Copy extensions var extns = psn.FindAllExtensions(o => o.PropertyPath == String.Format("OtherIdentifiers[{0}{1}]", oth.Value.Domain, oth.Value.Identifier)); if (extns != null) { foreach (var ex in extns) { if (dbCntrPsn.FindExtension(o => o.PropertyPath == ex.PropertyPath && o.Name == ex.Name) == null) { dbCntrPsn.Add(ex); } } } dbCntrPsn.OtherIdentifiers.Add(oth); } // Make sure we don't update what we don't need to dbCntrPsn.Addresses = psn.Addresses = null; dbCntrPsn.Citizenship = psn.Citizenship = null; dbCntrPsn.Employment = psn.Employment = null; dbCntrPsn.Language = psn.Language = null; dbCntrPsn.Names = psn.Names = null; dbCntrPsn.Race = psn.Race = null; dbCntrPsn.TelecomAddresses = psn.TelecomAddresses = null; dbCntrPsn.BirthTime = psn.BirthTime = null; dbCntrPsn.DeceasedTime = psn.DeceasedTime = null; // Remove the old person from the db psn.Status = SVC.Core.ComponentModel.Components.StatusType.Obsolete; // obsolete the old person } else // migrate identifiers { foreach (var id in refr.AlternateIdentifiers) { bool isPrivate = false; var oidData = ApplicationContext.ConfigurationService.OidRegistrar.FindData(id.Domain); if (oidData != null) { isPrivate = !(oidData.Attributes.Exists(o => o.Key == "IsMergeSurvivor") && Convert.ToBoolean(oidData.Attributes.Find(o => o.Key == "IsMergeSurvivor").Value)); } dbCntrPsn.AlternateIdentifiers.Add(new SVC.Core.DataTypes.DomainIdentifier() { AssigningAuthority = id.AssigningAuthority, UpdateMode = SVC.Core.DataTypes.UpdateModeType.AddOrUpdate, IsLicenseAuthority = false, IsPrivate = isPrivate, // TODO: Make this a configuration flag (cntrPsn.AlternateIdentifiers.Exists(i=>i.Domain == id.Domain)), Identifier = id.Identifier, Domain = id.Domain }); } // set to ignore if (psn.Names != null) { foreach (var rc in psn.Names) { rc.UpdateMode = SVC.Core.DataTypes.UpdateModeType.Ignore; } } // set to ignore if (psn.Addresses != null) { foreach (var rc in psn.Addresses) { rc.UpdateMode = SVC.Core.DataTypes.UpdateModeType.Ignore; } } // set to ignore if (psn.Race != null) { foreach (var rc in psn.Race) { rc.UpdateMode = SVC.Core.DataTypes.UpdateModeType.Ignore; } } } // Now update the person //psn.Site = refr.Site; //pp.Persist(conn, tx, psn, true); // update the person record #if DEBUG Trace.TraceInformation("Step 1 : Prepare update to person #{0}", psn.Id, cntrPsn.Id, !symbolic); #endif var regEvent = this.GetRegistrationEvent(conn, tx, psn); // get the registration event var changeSummary = DbUtil.GetRegistrationEvent(refr).FindComponent(HealthServiceRecordSiteRoleType.ReasonFor | HealthServiceRecordSiteRoleType.OlderVersionOf) as ChangeSummary; if (changeSummary != null) { regEvent.RemoveAllFromRole(HealthServiceRecordSiteRoleType.ReasonFor | HealthServiceRecordSiteRoleType.OlderVersionOf); regEvent.Add(new ChangeSummary() { ChangeType = changeSummary.ChangeType, EffectiveTime = changeSummary.EffectiveTime, LanguageCode = changeSummary.LanguageCode, Status = changeSummary.Status, Timestamp = changeSummary.Timestamp }, "CHG", HealthServiceRecordSiteRoleType.ReasonFor | HealthServiceRecordSiteRoleType.OlderVersionOf, null); } if (!symbolic) { regEvent.Status = StatusType.Obsolete; // obsolete } regEvent.RemoveAllFromRole(HealthServiceRecordSiteRoleType.SubjectOf); regEvent.Add(psn, "SUBJ", HealthServiceRecordSiteRoleType.SubjectOf, null); #if DEBUG Trace.TraceInformation("Step 2 : Perform update to person #{0}", psn.Id); #endif new RegistrationEventPersister().Persist(conn, tx, regEvent, true); refr.AlternateIdentifiers.Clear(); refr.AlternateIdentifiers.Add(new SVC.Core.DataTypes.DomainIdentifier() { Domain = ApplicationContext.ConfigurationService.OidRegistrar.GetOid("CR_CID").Oid, Identifier = psn.Id.ToString() }); //pp.CreatePersonVersion(conn, tx, psn); //DbUtil.PersistComponents(conn, tx, false, this, psn); // Now, we have to prepare an event so that this all makes sense // if we de-persist the most recent version (to reflect changes made) // Store the merged new record #if DEBUG Trace.TraceInformation("Step 3 : Perform update to person #{0}", dbCntrPsn.Id); #endif pp.CreatePersonVersion(conn, tx, dbCntrPsn); // Components DbUtil.PersistComponents(conn, tx, false, this, dbCntrPsn); // Now update the backreference to up the chain it gets updated cntrPsn.VersionId = dbCntrPsn.VersionId; } // Create the link #if DEBUG Trace.TraceInformation("Creating link between persons {0} > {1}", psn.Id, dbCntrPsn.Id); #endif using (var cmd = DbUtil.CreateCommandStoredProc(conn, tx)) { cmd.CommandText = "crt_psn_lnk"; cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "psn_id_in", DbType.Decimal, dbCntrPsn.Id)); cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "psn_vrsn_id_in", DbType.Decimal, dbCntrPsn.VersionId)); cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "lnk_psn_id_in", DbType.Decimal, psn.Id)); cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "lnk_cls_in", DbType.Decimal, (decimal)role)); cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "symbolic_in", DbType.Boolean, symbolic)); cmd.ExecuteNonQuery(); } // Send notification that duplicates were resolved //if (symbolic) //{ // // Send an duplicates resolved message // IClientNotificationService notificationService = ApplicationContext.CurrentContext.GetService(typeof(IClientNotificationService)) as IClientNotificationService; // if (notificationService != null) // notificationService.NotifyDuplicatesResolved(cntrPsn, refr.AlternateIdentifiers[0]); //} refr.Id = psn.Id; // Person identifier return(new SVC.Core.DataTypes.VersionedDomainIdentifier() { Identifier = psn.Id.ToString(), Version = psn.VersionId.ToString() }); }
/// <summary> /// De-persist the specified change summary /// </summary> public System.ComponentModel.IComponent DePersist(System.Data.IDbConnection conn, decimal identifier, System.ComponentModel.IContainer container, SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType?role, bool loadFast) { // TODO: Ensure that when a parent with context conduction exists, to grab contextual data (authors, etc...) from the parent ChangeSummary retVal = new ChangeSummary(); // Configuration service ISystemConfigurationService configService = ApplicationContext.ConfigurationService; //ApplicationContext.Current.GetService(typeof(ISystemConfigurationService)) as ISystemConfigurationService; // Get the health service event IDbCommand cmd = DbUtil.CreateCommandStoredProc(conn, null); try { cmd.CommandText = "get_hsr_crnt_vrsn"; cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "hsr_id_in", DbType.Decimal, identifier)); decimal tsId = default(decimal), cdId = default(decimal), rplcVersionId = default(decimal); // Read data IDataReader reader = cmd.ExecuteReader(); try { if (!reader.Read()) { return(null); } retVal.Id = Convert.ToDecimal(reader["hsr_id"]); retVal.VersionIdentifier = Convert.ToDecimal(reader["hsr_vrsn_id"]); retVal.AlternateIdentifier = new VersionedDomainIdentifier() { Domain = configService.OidRegistrar.GetOid(ClientRegistryOids.EVENT_OID).Oid, Identifier = retVal.Id.ToString(), Version = retVal.VersionIdentifier.ToString() }; retVal.LanguageCode = reader["lang_cs"].ToString(); retVal.Timestamp = DateTime.Parse(Convert.ToString(reader["aut_utc"])); retVal.Status = (StatusType)Convert.ToDecimal(reader["status_cs_id"]); tsId = reader["efft_ts_set_id"] == DBNull.Value ? default(decimal) : Convert.ToDecimal(reader["efft_ts_set_id"]); cdId = Convert.ToDecimal(reader["evt_typ_cd_id"]); rplcVersionId = reader["rplc_vrsn_id"] == DBNull.Value ? default(decimal) : Convert.ToDecimal(reader["rplc_vrsn_id"]); } finally { reader.Close(); } // Read codes and times retVal.ChangeType = DbUtil.GetCodedValue(conn, null, cdId); if (tsId != default(decimal)) { retVal.EffectiveTime = DbUtil.GetEffectiveTimestampSet(conn, null, tsId); } if (container != null) { container.Add(retVal); } if (role.HasValue && (role.Value & HealthServiceRecordSiteRoleType.ReplacementOf) == HealthServiceRecordSiteRoleType.ReplacementOf) { ; } else { DbUtil.DePersistComponents(conn, retVal, this, loadFast); } } finally { cmd.Dispose(); } return(retVal); }