/// <summary> /// This is used to create a snapshot that is equivalent to the current state of a particular CmObject, /// but not linked to it. Currently this is just used in testing, to simulate state obtained from /// another client. /// </summary> internal static CmObjectSurrogate CreateSnapshot(ICmObject obj) { var result = new CmObjectSurrogate(obj); result.m_object = null; result.Xml = ((ICmObjectInternal)obj).ToXmlString(); return(result); }
/// <summary> /// Create one from an existing object; set its XML to the current state of the object. /// </summary> public ICmObjectSurrogate Create(ICmObject obj) { return(CmObjectSurrogate.CreateSnapshot(obj)); }
/*-------------------------------------------------------------------*/ /// <summary> /// Update the backend store. /// </summary> /// <param name="newbies">The newly created objects</param> /// <param name="dirtballs">The recently modified objects</param> /// <param name="goners">The recently deleted objects</param> /// <returns>true if all is well (successful save or nothing to save)</returns> public override bool Commit( HashSet<ICmObjectOrSurrogate> newbies, HashSet<ICmObjectOrSurrogate> dirtballs, HashSet<ICmObjectId> goners) { if (m_dbStore == null) { if (!ResumeDb4oConnectionAskingUser()) { throw new NonRecoverableConnectionLostException(); } } if (!GetCommitLock(newbies.Count != 0 || dirtballs.Count != 0 || goners.Count != 0)) return true; try { List<ICmObjectSurrogate> foreignNewbies; List<ICmObjectSurrogate> foreignDirtballs; List<ICmObjectId> foreignGoners; if (GetUnseenForeignChanges(out foreignNewbies, out foreignDirtballs, out foreignGoners)) { IUnitOfWorkService uowService = ((IServiceLocatorInternal) m_cache.ServiceLocator).UnitOfWorkService; IReconcileChanges reconciler = uowService.CreateReconciler(foreignNewbies, foreignDirtballs, foreignGoners); if (reconciler.OkToReconcileChanges()) { reconciler.ReconcileForeignChanges(); // And continue looping, in case there are by now MORE foreign changes! } else { uowService.ConflictingChanges(reconciler); return true; } } IEnumerable<CustomFieldInfo> cfiList; bool anyModifiedCustomFields; if (!HaveAnythingToCommit(newbies, dirtballs, goners, out anyModifiedCustomFields, out cfiList)) return true; var commitData = new CommitData { Source = m_mySourceTag }; int objectAddedIndex = 0; commitData.ObjectsDeleted = (from item in goners select item.Guid).ToArray(); //m_dbStore.Ext().Purge(); var generation = commitData.WriteGeneration = NextWriteGeneration; // we've seen our own change, and we use a semaphore to make sure there haven't been others since we checked. m_lastWriteGenerationSeen = generation; if (anyModifiedCustomFields) { var validKeys = new HashSet<string>(); foreach (var customFieldInfo in cfiList) { validKeys.Add(customFieldInfo.Key); CustomFieldInfo oldInfo; if (m_myKnownCustomFields.TryGetValue(customFieldInfo.Key, out oldInfo)) { if (oldInfo.Equals(customFieldInfo)) continue; // unchanged m_dbStore.Delete(oldInfo); } m_dbStore.Store(customFieldInfo); m_myKnownCustomFields[customFieldInfo.Key] = customFieldInfo; } // Get rid of deleted ones. In Db4o, it is not enough just not to re-save them! foreach (CustomFieldInfo customField in m_dbStore.Query<CustomFieldInfo>()) { if (!validKeys.Contains(customField.Key)) m_dbStore.Delete(customField); } } // The ToArray() is so we can safely modify the collection. (Note: even before we added the // code to convert dirtballs to newbies, there was some mysterious case where .Net thinks we have modified the // collection. Maybe sometimes the dirtball is present as a surrogate and modifying it by Refreshing // somehow changes the hashset? Anyway, be cautious about removing the ToArray(), even if you find // a better way to handle the spurious dirtballs). foreach (var dirtball in dirtballs.ToArray()) { long id; if (!m_idMap.TryGetValue(dirtball.Id, out id)) { // pathologically, SaveAndForceNewestXmlForCmObjectWithoutUnitOfWork may pass newbies as dirtballs. newbies.Add(dirtball); dirtballs.Remove(dirtball); continue; } var realDbObj = (CmObjectSurrogate)m_dbStore.Ext().GetByID(id); m_dbStore.Ext().Refresh(realDbObj, Int32.MaxValue); realDbObj.Update(dirtball.Classname, dirtball.XMLBytes); m_dbStore.Store(realDbObj); } commitData.ObjectsAdded = new long[newbies.Count]; // after possibly adjusting newbies above commitData.ObjectsUpdated = (from item in dirtballs select item.Id.Guid).ToArray(); // Enhance JohnT: possibly this could be sped up by taking advantage of the case where // newby is already a CmObjectSurrogate. This is probably only the case where we are // doing data migration or switching backends, however. foreach (var newby in newbies) { var newSurrogate = new CmObjectSurrogate(m_cache, newby.Id, newby.Classname, newby.XMLBytes); m_dbStore.Store(newSurrogate); commitData.ObjectsAdded[objectAddedIndex++] = m_dbStore.Ext().GetID(newSurrogate); MapId(newSurrogate); } foreach (var goner in goners) { var id = m_idMap[goner]; var realDbObj = (CmObjectSurrogate)m_dbStore.Ext().GetByID(id); m_dbStore.Ext().Refresh(realDbObj, Int32.MaxValue); m_dbStore.Delete(realDbObj); m_idMap.Remove(goner); } if (m_modelVersionOverride != m_modelVersionNumber.m_modelVersionNumber) { m_modelVersionNumber.m_modelVersionNumber = m_modelVersionOverride; m_dbStore.Store(m_modelVersionNumber); } m_dbStore.Store(commitData); m_dbStore.Commit(); } catch (Db4objects.Db4o.Ext.DatabaseClosedException) { if (!ResumeDb4oConnectionAskingUser()) { throw new NonRecoverableConnectionLostException(); } ReleaseCommitLock(); // reattempt the commit. return Commit(newbies, dirtballs, goners); } catch (Exception err) { m_dbStore.Rollback(); throw; } finally { ReleaseCommitLock(); } return base.Commit(newbies, dirtballs, goners); }
/// <summary> /// This is used to create a snapshot that is equivalent to the current state of a particular CmObject, /// but not linked to it. Currently this is just used in testing, to simulate state obtained from /// another client. /// </summary> internal static CmObjectSurrogate CreateSnapshot(ICmObject obj) { var result = new CmObjectSurrogate(obj); result.m_object = null; result.Xml = ((ICmObjectInternal)obj).ToXmlString(); return result; }
/*==========================================================================================================*/ /// <summary> /// Map surrogate IDs to db4o internal ID /// </summary> private void MapId(CmObjectSurrogate surrogate) { var item1 = m_dbStore.Ext().GetID(surrogate); m_idMap[surrogate.Id] = item1; }