async Task RemoveRepresentation(Type repType, ModelRepresentation representation) { if (lastChangedRepresentation == representation && linkedModels.Count > 0) { // Make sure there is at lease one representation with the latest data var otherRep = representations.Values.FirstOrDefault(r => r != representation); if (otherRep == null) { // Even though there are other models sharing the same data, none of their // representations is loaded. Load one of them now, so that we can copy to it the data of the // representation being deleted. var otherModel = linkedModels [0]; otherRep = GetRepresentationUnlinked(otherModel.RepresentationType); // Force the creation of the rep } try { await otherRep.WaitHandle.WaitAsync(); await Synchronize(otherRep, true); } finally { otherRep.WaitHandle.Release(); } lock (changeLock) { if (lastChangedRepresentation == representation || changeVersion < otherRep.CurrentVersion) { lastChangedRepresentation = otherRep; changeVersion = otherRep.CurrentVersion; } } } representations.Remove(repType); }
public async Task <ModelRepresentation> GetRepresentation(Type type) { ModelRepresentation existingRep = null; ModelRepresentation representation; try { await dataLock.WaitAsync(); if (!representations.TryGetValue(type, out representation)) { existingRep = representations.Values.FirstOrDefault(); representations [type] = representation = (ModelRepresentation)Activator.CreateInstance(type); representation.DocumentModelData = this; if (existingRep != null) { // The requested representation is new and there are other representations around which may contain // changes. Get existing data from them. await existingRep.WaitHandle.WaitAsync(); // Ensure internal data structures are initialized representation.CreateNew(); await representation.InternalCopyFrom(existingRep); } } } finally { if (existingRep != null) { existingRep.WaitHandle.Release(); } dataLock.Release(); } return(representation); }
public async Task Load() { CheckInitialized(); modelRepresentation = await GetRepresentationAsync(); await modelRepresentation.Load(); }
public void NotifyChanged(ModelRepresentation representation) { lock (changeLock) { lastChangedRepresentation = representation; representation.CurrentVersion = ++changeVersion; } RaiseChangedEvent(representation.GetType()); }
protected override async Task OnCopyFrom(ModelRepresentation other) { if (other is FileModelRepresentation file) { await SetContent(file.GetContent()); } else { throw new InvalidOperationException($"Can't copy data from model of type {other.GetType ()} into a model of type {GetType ()}"); } }
public void NotifyHasUnsavedChanges(ModelRepresentation representation) { var repType = representation.GetType(); foreach (var m in linkedModels) { if (m.RepresentationType == repType) { m.RaiseHasUnsavedChangesEvent(); } } }
internal async Task InternalCopyFrom(ModelRepresentation other) { try { FreezeChangeEvent(); // Capture the copied version now. If the copied object changes during the copy, the version // will increase, so a subsequent synchronize call will bring the additional changes. CurrentVersion = other.CurrentVersion; IsLoaded = true; await OnCopyFrom(other); HasUnsavedChanges = other.HasUnsavedChanges; } finally { ThawChangeEvent(false); } }
internal async Task RaiseRepresentationChangeEvent() { if (modelRepresentation == null) { return; } var newRep = await GetRepresentationAsync(); if (newRep != modelRepresentation) { modelRepresentation = newRep; try { OnRepresentationChanged(); } catch (Exception ex) { LoggingService.LogInternalError("OnRepresentationChanged failed", ex); } RaiseChangeEvent(); } }
async Task Synchronize(ModelRepresentation targetRep, bool dataLocked) { ModelRepresentation sourceModel = null; int currentChangeVersion; lock (changeLock) { sourceModel = lastChangedRepresentation; currentChangeVersion = changeVersion; } if (targetRep.CurrentVersion == currentChangeVersion || sourceModel == null || sourceModel == targetRep) { return; // Not changed } try { // Any operation that requires the lock of several representation must take // the model lock first, to avoid deadlocks if (!dataLocked) { await dataLock.WaitAsync(); await targetRep.WaitHandle.WaitAsync(); await sourceModel.WaitHandle.WaitAsync(); } await targetRep.InternalCopyFrom(sourceModel); } finally { if (!dataLocked) { dataLock.Release(); targetRep.WaitHandle.Release(); sourceModel.WaitHandle.Release(); } } RaiseChangedEvent(targetRep.GetType()); }
public Task Synchronize(ModelRepresentation targetRep) { return(Synchronize(targetRep, false)); }
internal async Task Relink(DocumentModel model, DocumentModelData newData) { bool newDataLocked = false, representationLocked = false; ModelRepresentation representation = null; var repType = model.RepresentationType; ModelRepresentation modelRepresentationToDispose = null; var modelsToNotify = new List <DocumentModel> (); await dataLock.WaitAsync(); try { representations.TryGetValue(repType, out representation); await representation.WaitHandle.WaitAsync(); representationLocked = true; linkedModels = linkedModels.Remove(model); var modelsForRep = linkedModels.Where(m => m.RepresentationType == repType).ToList(); if (modelsForRep.Count > 0) { // If there is more than one model using this representation then the representation // has to be cloned, since it can't be shared anymore var representationCopy = (ModelRepresentation)Activator.CreateInstance(representation.GetType()); await representationCopy.InternalCopyFrom(representation); representationCopy.DocumentModelData = this; representations [repType] = representationCopy; foreach (var m in modelsForRep) { modelsToNotify.Add(m); } } else { await RemoveRepresentation(repType, representation); } if (newData != null) { await newData.dataLock.WaitAsync(); newDataLocked = true; // If there are other models using the same representation, we'll have to notify them that // the representation has changed if (newData.representations.TryGetValue(repType, out modelRepresentationToDispose)) { modelsToNotify.AddRange(newData.linkedModels.Where(m => m.RepresentationType == repType)); } newData.linkedModels = newData.linkedModels.Add(model); model.Data = newData; newData.representations [repType] = representation; representation.DocumentModelData = newData; // Register that this new representation is the latest version. // If there are other representations, they will get the new data after a Synchronize call. newData.NotifyChanged(representation); } } finally { dataLock.Release(); if (representationLocked) { representation.WaitHandle.Release(); } if (newDataLocked) { newData.dataLock.Release(); } } foreach (var m in modelsToNotify) { m.RaiseRepresentationChangeEvent().Ignore(); } if (modelRepresentationToDispose != null) { modelRepresentationToDispose.OnDispose().Ignore(); } }
/// <summary> /// Saves the data to disk /// </summary> /// <returns>The save.</returns> public async Task Save() { CheckInitialized(); await ModelRepresentation.Save(); }
protected abstract Task OnCopyFrom(ModelRepresentation other);