示例#1
0
 private EfiTransaction(EFArtifact artifact, string originatorId, bool createdXmlTxn, EfiTransactionContext context)
 {
     _artifact              = artifact;
     _changes               = new EfiChangeGroup(this);
     _createdXmlTxn         = createdXmlTxn;
     _originatorId          = originatorId;
     _efiTransactionContext = context != null ? context : new EfiTransactionContext();
 }
示例#2
0
        protected override void OnAfterHandleXmlModelTransactionCompleted(
            object sender, XmlTransactionEventArgs xmlTransactionEventArgs, EfiChangeGroup changeGroup)
        {
            base.OnAfterHandleXmlModelTransactionCompleted(sender, xmlTransactionEventArgs, changeGroup);

            Debug.Assert(_layerManager != null, "LayerManager must not be null");
            if (_layerManager != null)
            {
                var changes = from ixc in xmlTransactionEventArgs.Transaction.Changes()
                              select new Tuple<XObject, XObjectChange>(ixc.Node, ixc.Action);
                _layerManager.OnAfterTransactionCommitted(changes);
            }
        }
示例#3
0
 internal EfiChangedEventArgs(EfiChangeGroup changes)
 {
     _changes = changes;
 }
示例#4
0
 internal void RecordChangeGroup(EfiChangeGroup changeGroup)
 {
     if (changeGroup != null
         &&
         changeGroup.Count > 0)
     {
         _changeGroups.Enqueue(changeGroup);
     }
 }
 internal EDMXModelChangeEventArgs(EfiChangeGroup changeGroup)
 {
     _efiChangeGroup = changeGroup;
 }
 internal EfiChangedEventArgs(EfiChangeGroup changes)
 {
     _changes = changes;
 }
示例#7
0
        private void ProcessExternalAddOrUpdateChange(
            EfiChangeGroup changeGroup, List<EFContainer> containersToRenormalize, ExternalXMLModelChange undoModelChange)
        {
            var changedEFObject = undoModelChange.ChangedEFObject;
            var parentEFObject = undoModelChange.Parent;

            if (parentEFObject != null)
            {
                var parentEFContainer = parentEFObject as EFContainer;
                Debug.Assert(parentEFContainer != null, "parentEFObject was not an EFContainer!");

                // if this is an "add" for an element, then the parent will parse the new child.
                // if this is an "add" for an attribute, then we have to directly set the XObject
                // if this is an "update" then we will just record it in our model
                if (parentEFContainer != null
                    &&
                    undoModelChange.Action == XObjectChange.Add)
                {
                    var newXElement = undoModelChange.XNode as XElement;

                    if (newXElement != null
                        &&
                        parentEFContainer.ReparseSingleElement(new List<XName>(), newXElement))
                    {
                        var newEFObject = ModelItemAnnotation.GetModelItem(undoModelChange.XNode);
                        Debug.Assert(newEFObject != null, "Couldn't find the ModelItemAnnotation for the newly created XLinq node");
                        if (newEFObject != null)
                        {
                            var newEFElement = newEFObject as EFElement;
                            Debug.Assert(newEFElement != null, "If the XObject was an XElement, then we should have an EFElement as well");
                            if (newEFElement != null)
                            {
                                XmlModelHelper.NormalizeAndResolve(newEFElement);
                            }

                            // we need to rediscover the XLinq annotation to pick up the new EFObject that just got created
                            changedEFObject = ModelItemAnnotation.GetModelItem(undoModelChange.XNode);
                        }
                    }
                    else if (undoModelChange.XNode is XAttribute)
                    {
                        var staleItemBinding = changedEFObject as ItemBinding;
                        var staleDefaultableValue = changedEFObject as DefaultableValue;

                        // item bindings might have gotten added after their parents got added in which case the MIA will be stale.
                        // we have to step through the new parent, set the xobject manually, rebind, and reset the annotation on the xlinq node.
                        if (staleItemBinding != null)
                        {
                            foreach (var child in parentEFContainer.Children)
                            {
                                var updatedItemBinding = child as ItemBinding;
                                if (updatedItemBinding != null
                                    && updatedItemBinding.EFTypeName == staleItemBinding.EFTypeName)
                                {
                                    updatedItemBinding.SetXObject(undoModelChange.XNode);
                                    updatedItemBinding.Rebind();
                                    changedEFObject = updatedItemBinding;
                                }
                            }
                        }

                            // for defaultable values that got added after parents were added, we have to discover the actual EFObject in the Escher tree,
                            // set the xobject, and reset the annotation on the existing xlinq node
                        else if (staleDefaultableValue != null)
                        {
                            foreach (var child in parentEFContainer.Children)
                            {
                                var updatedDefaultableValue = child as DefaultableValue;
                                if (updatedDefaultableValue != null
                                    && updatedDefaultableValue.EFTypeName == staleDefaultableValue.EFTypeName)
                                {
                                    updatedDefaultableValue.SetXObject(undoModelChange.XNode);
                                    changedEFObject = updatedDefaultableValue;
                                }
                            }
                        }

                        ModelItemAnnotation.SetModelItem(undoModelChange.XNode, changedEFObject);
                    }
                }
                else if (undoModelChange.Action == XObjectChange.Value)
                {
                    Debug.Assert(undoModelChange.XNode is XAttribute, "The only 'value' change Escher supports is to XAttributes");
                    if (undoModelChange.XNode is XAttribute)
                    {
                        var existingItemBinding = changedEFObject as ItemBinding;
                        if (existingItemBinding != null)
                        {
                            existingItemBinding.Rebind();
                        }

                        // we have to normalize and resolve the parents of DefaultableValues
                        // because this change could affect the parent's RefName, affecting SingleItemBindings
                        var defaultableValue = changedEFObject as DefaultableValue;
                        if (defaultableValue != null)
                        {
                            XmlModelHelper.NormalizeAndResolve(parentEFContainer);
#if DEBUG
                            var xattr = undoModelChange.XNode as XAttribute;
                            var defaultableValueValue = defaultableValue.ObjectValue as string;
                            if (defaultableValueValue != null)
                            {
                                // verify that the defaultableValue's value & the XAttribute's value are the same
                                Debug.Assert(xattr.Value == defaultableValueValue);
                            }
#endif
                        }
                    }
                }
                else
                {
                    Debug.Assert(false, "Encountered a non-valid type of change to the XML: " + undoModelChange.Action.ToString());
                    throw new ChangeProcessingFailedException();
                }

                // if an object's state is unresolved, then queue it for re-normalization. This occurs
                // for example, if itembinding changes occur before the 'add' changes for their targeted objects
                var itemBinding = changedEFObject as ItemBinding;
                if ((itemBinding != null && false == itemBinding.Resolved)
                    || parentEFContainer.State != EFElementState.Resolved)
                {
                    containersToRenormalize.Add(parentEFContainer);
                }
                CheckObjectToRenormalize(changedEFObject, ref containersToRenormalize);

                // now tell the views that a new item has been created; this will happen for both an 'add' and a 'change'
                string oldValue = null;
                string newValue = null;
                string property = null;
                GetOldAndNewValues(undoModelChange.XmlChange, out property, out oldValue, out newValue);

                changeGroup.RecordModelChange(GetChangeType(undoModelChange.XmlChange), changedEFObject, property, oldValue, newValue);
            }
            else
            {
                // we got a change event on a root-node in the document.  
                // This will cause an NRE when looking for the model-item annotation,
                // so throw this exception to cause the caller to reload the doc
                throw new ChangeProcessingFailedException();
            }
        }
示例#8
0
        private static void ProcessExternalRemoveChange(
            EfiChangeGroup changeGroup, IList<ItemBinding> bindingsForRebind, ExternalXMLModelChange modelChange)
        {
            var changedEFObject = modelChange.ChangedEFObject;
            var parentEFObject = modelChange.Parent;
            if (changedEFObject != null)
            {
                var staleItemBinding = changedEFObject as ItemBinding;
                var staleDefaultableValue = changedEFObject as DefaultableValue;

                var parentEFContainer = parentEFObject as EFContainer;
                Debug.Assert(parentEFContainer != null, "parentEfObject was not an EFContainer!");
                if (staleItemBinding != null)
                {
                    // if this is an itembinding, then we have to directly null out the xobject and rebind since the refname is a "symlink" to the xattribute
                    foreach (var child in parentEFContainer.Children)
                    {
                        var updatedItemBinding = child as ItemBinding;
                        if (updatedItemBinding != null
                            && updatedItemBinding.EFTypeName == staleItemBinding.EFTypeName)
                        {
                            updatedItemBinding.SetXObject(null);
                            updatedItemBinding.Rebind();
                            changedEFObject = updatedItemBinding;
                            break;
                        }
                    }
                }
                else if (staleDefaultableValue != null)
                {
                    foreach (var child in parentEFContainer.Children)
                    {
                        var updatedDefaultableValue = child as DefaultableValue;
                        if (updatedDefaultableValue != null
                            && updatedDefaultableValue.EFTypeName == staleDefaultableValue.EFTypeName)
                        {
                            updatedDefaultableValue.SetXObject(null);
                            changedEFObject = updatedDefaultableValue;
                            break;
                        }
                    }
                }
                else
                {
                    // Find all the dependent binding.
                    var visitor = new AntiDependencyCollectorVisitor();
                    visitor.Traverse(changedEFObject);
                    foreach (var binding in visitor.AntiDependencyBindings)
                    {
                        if (!bindingsForRebind.Contains(binding))
                        {
                            bindingsForRebind.Add(binding);
                        }
                    }
                    // Delete(false) because it has already been removed from XLinq tree
                    changedEFObject.Delete(false);
                }

                // record the change so views get updated
                changeGroup.RecordModelChange(EfiChange.EfiChangeType.Delete, changedEFObject, String.Empty, String.Empty, String.Empty);
            }
            else
            {
                throw new ChangeProcessingFailedException();
            }
        }
示例#9
0
 protected virtual void OnAfterHandleXmlModelTransactionCompleted(
     object sender, XmlTransactionEventArgs xmlTransactionEventArgs, EfiChangeGroup changeGroup)
 {
 }
示例#10
0
        protected virtual void OnHandleXmlModelTransactionCompleted(
            object sender, XmlTransactionEventArgs xmlTransactionEventArgs, bool isUndoOrRedo, out EfiChangeGroup changeGroup)
        {
            // If this is an undo/redo XML transaction there is no EfiTransaction, thus the artifact will not
            // be made dirty as necessary. We will have to do it manually here.
            var efiTransaction = xmlTransactionEventArgs.Transaction.UserState as EfiTransaction;
            if (efiTransaction == null && isUndoOrRedo)
            {
                Artifact.IsDirty = true;
            }

            // When an XML transaction completes it could either be a normal, undo, or redo transaction.
            // In all cases we will need to clear the "validity" of the artifact so that any successive
            // validations will not short-circuit.
            // Ideally we can skip this in the event of any major error that causes the reloading
            // of the artifact but we'll be safe.
            SetValidityDirtyForErrorClass(ErrorClass.All, true);

            // the change group to send back to the caller
            changeGroup = null;

            // if the transaction is aborting, drop and reload
            if (xmlTransactionEventArgs.Transaction.Status == XmlTransactionStatus.Aborted)
            {
                ReloadArtifact();
                return;
            }

            if (efiTransaction != null)
            {
                changeGroup = ProcessDesignerChange(xmlTransactionEventArgs, efiTransaction);
                if (changeGroup != null)
                {
                    ModelManager.RecordChangeGroup(changeGroup);
                }
            }
            else
            {
                // TODO: when we want SxS again, we should handle these operations in addition to undo/redo
                if (isUndoOrRedo)
                {
                    try
                    {
                        changeGroup = ProcessUndoRedoChanges(xmlTransactionEventArgs);
                        if (changeGroup != null)
                        {
                            ModelManager.RecordChangeGroup(changeGroup);

                            // we have to manually route the change groups here because we can't rely on ProcessUndoRedoChange to do it since nothing
                            // gets updated in the Xml Model
                            ModelManager.RouteChangeGroups();
                        }
                    }
                    catch (ChangeProcessingFailedException)
                    {
                        ReloadArtifact();
                    }
                    catch (Exception e)
                    {
                        Debug.Fail("Unexpected exception caught while processing undo/redo", e.Message);
                        ReloadArtifact();
                    }
                }
            }
        }
        private void ProcessModelChanges(EfiChangeGroup changeGroup)
        {
            EntityDesignerDiagram diagram = null;
            diagram = GetDiagram();
            var disableFixUpDiagramSelection = false;

            // If diagram is null it might have been deleted, in this case just return.
            if (diagram == null)
            {
                return;
            }

            // Store the value of the disableFixUpDiagramSelection.
            disableFixUpDiagramSelection = diagram.DisableFixUpDiagramSelection;
            try
            {
                //
                // This turns off layout during "serialization" and fixes bad perf during update-model scenarios.
                // We set this to true here, and then set it false in the finally block because there were some problems with 
                // the DSL fix not drawing self-associations correctly when this is set to true all the time. 
                //
                if (changeGroup.Transaction != null
                    && (changeGroup.Transaction.OriginatorId == EfiTransactionOriginator.UpdateModelFromDatabaseId ||
                        changeGroup.Transaction.OriginatorId == EfiTransactionOriginator.UndoRedoOriginatorId))
                {
                    Store.PropertyBag["WorkaroundFixSerializationTransaction"] = true;
                }
                    // If coming from property window, we don't want the selection to change.
                else if (changeGroup.Transaction.OriginatorId == EfiTransactionOriginator.PropertyWindowOriginatorId
                         && diagram != null)
                {
                    diagram.DisableFixUpDiagramSelection = true;
                }

                // sort changes and discover the artifact that has changed
                var extraElementsToProcess = new HashSet<EFObject>();
                var changes = changeGroup.SortChangesForProcessing(new ChangeComparer());

                var artifacts = CurrentArtifactsInView;

                ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // FIRST STEP: updating DSL Model Elements.
                ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // tx name is not shown anywhere since we don't use the DSL undo manager
                using (var t = Store.TransactionManager.BeginTransaction(
                        InternalTransactionIdPrefix + changeGroup.Transaction.OriginatorId, true))
                {
                    // start auto-arranger for arranging new elements
                    // don't do this for undo/redo because we will translate the new diagram EFObject information into the DSL ShapeElements
                    // don't do this for update model because we want to set up an entirely new transaction for auto-layout with the new shapes
                    if (changeGroup.Transaction.OriginatorId != EfiTransactionOriginator.UndoRedoOriginatorId
                        && changeGroup.Transaction.OriginatorId != EfiTransactionOriginator.UpdateModelFromDatabaseId)
                    {
                        if (diagram != null)
                        {
                            diagram.Arranger.Start(PointD.Empty);
                        }
                    }

                    var hasChangesForCurrentArtifactInView = false;

                    foreach (var change in changes)
                    {
                        if (artifacts.Contains(change.Changed.Artifact))
                        {
                            // only process changes if the artifact is designer-safe.  
                            if (change.Changed.Artifact.IsDesignerSafe)
                            {
                                hasChangesForCurrentArtifactInView = true;

                                if (change.Changed is ModelDiagram.BaseDiagramObject)
                                {
                                    ProcessSingleDiagramModelChange(change, extraElementsToProcess);
                                }
                                else if (ProcessSingleModelChange(change))
                                {
                                    break;
                                }
                            }
                        }
                    }

                    foreach (var efobject in extraElementsToProcess)
                    {
                        var et = efobject as ConceptualEntityType;
                        if (et != null)
                        {
                            foreach (var p in et.Properties())
                            {
                                OnEFObjectCreatedOrUpdated(p, ModelXRef);
                            }
                            foreach (var np in et.NavigationProperties())
                            {
                                OnEFObjectCreatedOrUpdated(np, ModelXRef);
                            }

                            if (et.BaseType.Target != null)
                            {
                                OnEFObjectCreatedOrUpdated(et.BaseType, ModelXRef);
                            }
                        }
                        else
                        {
                            Debug.Fail("Unexpected type of model node in extraElementsToProcess");
                        }
                    }

                    if (hasChangesForCurrentArtifactInView & t.HasPendingChanges)
                    {
                        t.Commit();
                    }

                    if (changeGroup.Transaction.OriginatorId != EfiTransactionOriginator.UndoRedoOriginatorId
                        && changeGroup.Transaction.OriginatorId != EfiTransactionOriginator.UpdateModelFromDatabaseId)
                    {
                        Debug.Assert(diagram != null, "Should have discovered the diagram if autoArrange == true");
                        if (diagram != null)
                        {
                            diagram.Arranger.End();
                        }
                    }

                    if (ModelHasElements() == false)
                    {
                        var edd = GetDiagram();
                        if (edd != null)
                        {
                            edd.ResetWatermark(edd.ActiveDiagramView);
                        }
                    }
                }

                // we need to keep track of the entity shapes that needed to be auto layout.
                var shapesToAutoLayout = new List<ShapeElement>();

                ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // SECOND STEP: updating DSL Presentation Elements.
                ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                // Given the changegroup and any transaction that originates from the model, see if there were any diagram EFObjects were changed. 
                // Go through each one, find the corresponding ShapeElement (DSL presentation element) that was created, and add it to the view model xref.
                if (changeGroup.Transaction != null)
                {
                    using (var t = Store.TransactionManager.BeginTransaction(
                            InternalTransactionIdPrefix + changeGroup.Transaction.OriginatorId, true))
                    {
                        var hasDiagramObjectChanges = false;
                        var moveDiagram2Shapes =
                            (changeGroup.Transaction.OriginatorId == EfiTransactionOriginator.UndoRedoOriginatorId ||
                             changeGroup.Transaction.OriginatorId == EfiTransactionOriginator.UpdateModelFromDatabaseId);

                        foreach (var change in changes)
                        {
                            if (artifacts.Contains(change.Changed.Artifact))
                            {
                                if (change.Changed.Artifact.IsDesignerSafe)
                                {
                                    if (change.Changed is DefaultableValue
                                        && change.Changed.Parent is ModelDesigner.Diagram
                                        && ModelXRef.ContainsKey(change.Changed.Parent))
                                    {
                                        hasDiagramObjectChanges = true;
                                        ModelTranslatorContextItem.GetEntityModelTranslator(EditingContext)
                                            .SynchronizeSingleDslModelElement(this, change.Changed.Parent);
                                    }
                                    else
                                    {
                                        // this will set the ShapeElements to their original position as well as add them to the XRef
                                        ModelDiagram.BaseDiagramObject modelDiagramObject = null;
                                        var currentNode = change.Changed;
                                        while (currentNode != null)
                                        {
                                            var baseDiagram = currentNode as ModelDiagram.BaseDiagramObject;
                                            if (baseDiagram != null)
                                            {
                                                modelDiagramObject = baseDiagram;
                                                break;
                                            }
                                            currentNode = currentNode.Parent;
                                        }

                                        // Call TranslateDiagramObject to sync Escher Designer Model element and DSL PEL (Presentation Element).
                                        // We need to call this even when the change type is 'delete' because it might affect DSL PEL.
                                        // (For example: when you change entity type shape's FillColor to a default color, the FillColor attribute will be deleted since it is not needed,
                                        // in that case, we still need to update DSL PEL).
                                        if (modelDiagramObject != null
                                            && modelDiagramObject.Diagram != null
                                            && modelDiagramObject.Diagram.Id == DiagramId)
                                        {
                                            hasDiagramObjectChanges = true;
                                            EntityModelToDslModelTranslatorStrategy.TranslateDiagramObject(
                                                this, modelDiagramObject, moveDiagram2Shapes, shapesToAutoLayout);
                                        }
                                    }
                                }
                            }
                        }

                        if (hasDiagramObjectChanges && t.HasPendingChanges)
                        {
                            t.Commit();
                        }
                    }
                }

                if (changeGroup.Transaction != null
                    && shapesToAutoLayout.Count > 0)
                {
                    diagram.AutoLayoutDiagram(shapesToAutoLayout);
                }
            }
            finally
            {
                Store.PropertyBag["WorkaroundFixSerializationTransaction"] = false;
                if (diagram != null)
                {
                    diagram.DisableFixUpDiagramSelection = disableFixUpDiagramSelection;
                }
            }

            if (_shouldClearAndReloadDiagram)
            {
                try
                {
                    ClearAndReloadDiagram();
                }
                finally
                {
                    _shouldClearAndReloadDiagram = false;
                }
            }
        }