/// <summary>
		/// Integrate changes in dummy cache to real cache and DB.
		/// </summary>
		public void ConnectToRealCache()
		{
			// (FLEx) Review use of ISilDataAccess and other C++ cache related classes
			CheckDisposed();
			// If an Undo or Redo is in progress, we CAN'T save the changes. Ideally it wouldn't be necessary because making
			// any savable change in the slice would discard any pending Redo, and Undo would undo any changes in the slice
			// before undoing anything else. Currently Undo within the slice is not this well integrated. However, doing some editing
			// in the slice and then Undoing or Redoing a previous command DOES save the changes in the slice; I think OnLeave() must
			// be called somewhere in the process of invoking Undo before it is too late. This is not ideal behavior, but it
			// beats crashing.
			if (m_fdoCache.ActionHandlerAccessor.IsUndoOrRedoInProgress)
				return;
			Form frm = FindForm();
			WaitCursor wc = null;
			try
			{
				// frm will be null, if the record has been switched
				if (frm != null)
					wc = new WaitCursor(frm);
				// We're saving any changes to the real cache, so can no longer Undo/Redo local edits.
				CommitLocalEdits();
				// [NB: m_silCache is the same cache as m_vwCache, but is is a different cache than
				// m_fdoCache.  m_fdoCache has access to the database, and updates it, but
				// m_silCache does not.]
				if (DesignMode || m_rootb == null
					// It may not be valid by now, since it may have been deleted.
					|| !m_rootObj.IsValidObject)
				{
					if (frm != null)
						frm.Cursor = Cursors.Default;
					return;
				}
				string fieldname =
					(m_rootFlid == MoAffixAllomorphTags.kflidPhoneEnv) ? "PhoneEnv" : "Position";
				m_fdoCache.DomainDataByFlid.BeginUndoTask(
					String.Format(DetailControlsStrings.ksUndoSet, fieldname),
					String.Format(DetailControlsStrings.ksRedoSet, fieldname));
				IPhEnvironmentFactory environmentFactory = m_fdoCache.ServiceLocator.GetInstance<IPhEnvironmentFactory>();
				IFdoOwningSequence<IPhEnvironment> allAvailablePhoneEnvironmentsInProject =
					m_fdoCache.LanguageProject.PhonologicalDataOA.EnvironmentsOS;

				var envsBeingRequestedForThisEntry = EnvsBeingRequestedForThisEntry();

				// Environments just typed into slice that are not already used for
				// this entry or known about in the project.
				var newEnvsJustTyped =
					envsBeingRequestedForThisEntry.Where(localDummyHvoOfAnEnvInEntry =>
						!allAvailablePhoneEnvironmentsInProject
							.Select(projectEnv => RemoveSpaces(projectEnv.StringRepresentation.Text))
							.Contains(RemoveSpaces(GetStringOfEnvironment(localDummyHvoOfAnEnvInEntry))));
				// Add the unknown/new environments to project
				foreach (var localDummyHvoOfAnEnvironmentInEntry in
					newEnvsJustTyped)
				{
					ITsString envTssRep = GetTsStringOfEnvironment(
						localDummyHvoOfAnEnvironmentInEntry);
					IPhEnvironment newEnv = environmentFactory.Create();
					allAvailablePhoneEnvironmentsInProject.Add(newEnv);
					newEnv.StringRepresentation = envTssRep;
				}

				var countOfExistingEnvironmentsInDatabaseForEntry =
					m_fdoCache.DomainDataByFlid.get_VecSize(m_rootObj.Hvo, m_rootFlid);
				// Contains environments already in entry or recently selected in
				// dialog, but not ones just typed
				int[] existingListOfEnvironmentHvosInDatabaseForEntry;
				int chvoMax = m_fdoCache.DomainDataByFlid.get_VecSize(
					m_rootObj.Hvo, m_rootFlid);
				using (ArrayPtr arrayPtr = MarshalEx.ArrayToNative<int>(chvoMax))
				{
					m_fdoCache.DomainDataByFlid.VecProp(m_rootObj.Hvo, m_rootFlid, chvoMax, out chvoMax, arrayPtr);
					existingListOfEnvironmentHvosInDatabaseForEntry = MarshalEx.NativeToArray<int>(arrayPtr, chvoMax);
				}

				// Build up a list of real hvos used in database for the
				// environments in the entry
				var newListOfEnvironmentHvosForEntry = new List<int>();
				foreach (var localDummyHvoOfAnEnvironmentInEntry in
					envsBeingRequestedForThisEntry)
				{
					ITsString envTssRep = GetTsStringOfEnvironment(
						localDummyHvoOfAnEnvironmentInEntry);
					string envStringRep = envTssRep.Text;

					// Pick a sensible environment from the known environments in
					// the project, by string
					IPhEnvironment anEnvironmentInEntry = FindPhoneEnv(
						allAvailablePhoneEnvironmentsInProject, envStringRep,
						newListOfEnvironmentHvosForEntry.ToArray(),
						existingListOfEnvironmentHvosInDatabaseForEntry);

					// Maybe the ws has changed, so change the real env in database,
					// in case.
					anEnvironmentInEntry.StringRepresentation = envTssRep;

					ITsStrBldr bldr = envTssRep.GetBldr();
					ConstraintFailure failure;
					if (anEnvironmentInEntry.CheckConstraints(PhEnvironmentTags.kflidStringRepresentation, false, out failure, true))
						ClearSquigglyLine(localDummyHvoOfAnEnvironmentInEntry, ref envTssRep, ref bldr);
					else
						MakeSquigglyLine(localDummyHvoOfAnEnvironmentInEntry, failure.XmlDescription, ref envTssRep, ref bldr);

					newListOfEnvironmentHvosForEntry.Add(anEnvironmentInEntry.Hvo);

					// Refresh
					m_sda.SetString(localDummyHvoOfAnEnvironmentInEntry, kEnvStringRep, bldr.GetString());
					m_rootb.PropChanged(localDummyHvoOfAnEnvironmentInEntry, kEnvStringRep, 0,
						envTssRep.Length, envTssRep.Length);
				}

				// Only reset the main property, if it has changed.
				// Otherwise, the parser gets too excited about needing to reload.
				if ((countOfExistingEnvironmentsInDatabaseForEntry !=
					newListOfEnvironmentHvosForEntry.Count()) ||
					!equalArrays(existingListOfEnvironmentHvosInDatabaseForEntry,
						newListOfEnvironmentHvosForEntry.ToArray()))
				{
					m_fdoCache.DomainDataByFlid.Replace(m_rootObj.Hvo, m_rootFlid, 0,
						countOfExistingEnvironmentsInDatabaseForEntry,
						newListOfEnvironmentHvosForEntry.ToArray(),
						newListOfEnvironmentHvosForEntry.Count());
				}
				m_fdoCache.DomainDataByFlid.EndUndoTask();
			}
			finally
			{
				if (wc != null)
				{
					wc.Dispose();
					wc = null;
				}
			}
		}