private static void InjectAssociationConnectorCommand( CommandProcessorContext commandProcessorContext, HashSet <ShapeChangeInformation> shapeChangeInfoSet, Diagram diagram, Association association, XObjectChange changeAction) { // First check to see if there is already a change in the original transaction // for the AssociationConnector that matches the one that we're getting var shapeChangeInfoToQuery = new ShapeChangeInformation { ChangeType = changeAction, ModelEFObject = association, DiagramId = diagram.Id.Value }; var shapeChangeInfoExists = shapeChangeInfoSet.Contains(shapeChangeInfoToQuery); // We only want to create model diagram if the transaction is originated from this diagram. if (changeAction == XObjectChange.Add && shapeChangeInfoExists == false) { // The association connector is added if // - the participating entities are in the diagram. // or // - the participating entities will be added in the current transaction. foreach (var end in association.AssociationEnds()) { if (end.Type != null && end.Type.Target != null) { if (diagram.EntityTypeShapes.Where(ets => ets.EntityType.Target == end.Type.Target).Any() || shapeChangeInfoSet.Where( sc => sc.DiagramId == diagram.Id.Value && sc.ModelEFObject == end.Type.Target && sc.ChangeType == XObjectChange.Add).Any()) { continue; } } // If it reach this point, that means that we should not add association connector in the diagram. return; } var cmd = new CreateAssociationConnectorCommand(diagram, association); CommandProcessor.InvokeSingleCommand(commandProcessorContext, cmd); } else if (changeAction == XObjectChange.Remove && shapeChangeInfoExists == false) { // this is happening before the transaction is taking place so we are free to look up anti-dependencies on this delete foreach ( var associationConnector in association.GetAntiDependenciesOfType <AssociationConnector>() .Where(ac => ac.Diagram != null && ac.Diagram.Id == diagram.Id.Value)) { if (associationConnector != null) { var deleteAssociationConnectorCommand = associationConnector.GetDeleteCommand(); CommandProcessor.InvokeSingleCommand(commandProcessorContext, deleteAssociationConnectorCommand); } } } }
private static void InjectInheritanceConnectorCommand( CommandProcessorContext commandProcessorContext, HashSet <ShapeChangeInformation> shapeChangeInfoSet, Diagram diagram, EntityTypeBaseType baseType, XObjectChange changeAction) { // First check to see if there is already a change in the original transaction // for the InheritanceConnector that matches the one that we're getting var shapeChangeInfoToQuery = new ShapeChangeInformation { ChangeType = changeAction, ModelEFObject = baseType.OwnerEntityType, DiagramId = diagram.Id.Value }; var shapeChangeInfoExists = shapeChangeInfoSet.Contains(shapeChangeInfoToQuery); if (changeAction == XObjectChange.Add && shapeChangeInfoExists == false) { var participatingEntityTypes = new List <EntityType>(); var derivedEntityType = baseType.Parent as EntityType; Debug.Assert(derivedEntityType != null, "Where is the parent EntityType of this BaseType attribute?"); if (derivedEntityType != null) { // The inheritance connector is added if // - the participating entities exists in the diagram. // or // - the participating entities will be added in the current transaction. participatingEntityTypes.Add(derivedEntityType); participatingEntityTypes.Add(((ConceptualEntityType)derivedEntityType).BaseType.Target); foreach (var entityType in participatingEntityTypes) { if (diagram.EntityTypeShapes.Where(ets => ets.EntityType.Target == entityType).Any() || shapeChangeInfoSet.Where( sc => sc.DiagramId == diagram.Id.Value && sc.ModelEFObject == entityType && sc.ChangeType == XObjectChange.Add) .Any()) { continue; } // If it reach this point, that means that we should not add inheritance connector in the diagram. return; } var cmd = new CreateInheritanceConnectorCommand(diagram, derivedEntityType); CommandProcessor.InvokeSingleCommand(commandProcessorContext, cmd); } } else if (changeAction == XObjectChange.Remove && shapeChangeInfoExists == false) { // this is happening before the transaction is taking place so we are free to look up anti-dependencies // on this delete var owningEntityType = baseType.Parent as EntityType; if (owningEntityType != null) { foreach ( var inheritanceConnector in owningEntityType.GetAntiDependenciesOfType <InheritanceConnector>() .Where(ic => ic.Diagram != null && ic.Diagram.Id == diagram.Id.Value)) { if (inheritanceConnector != null) { var deleteInheritanceConnectorCommand = inheritanceConnector.GetDeleteCommand(); CommandProcessor.InvokeSingleCommand(commandProcessorContext, deleteInheritanceConnectorCommand); } } } } }
private static void InjectEntityTypeShapeCommand( CommandProcessorContext commandProcessorContext, HashSet <ShapeChangeInformation> shapeChangeInfoSet, Diagram diagram, ConceptualEntityType entityType, XObjectChange changeAction) { // First check to see if there is already a change in the original transaction // for the EntityTypeShape that matches the one that we're getting var shapeChangeInfoToQuery = new ShapeChangeInformation { ChangeType = changeAction, ModelEFObject = entityType, DiagramId = diagram.Id.Value }; var shapeChangeInfoExists = shapeChangeInfoSet.Contains(shapeChangeInfoToQuery); // We only want to create model diagram if the transaction is originated from this diagram. if (changeAction == XObjectChange.Add) { // We only want to inject the entity-type-shape if the transaction is originated from the passed in diagram. if (commandProcessorContext != null && commandProcessorContext.EfiTransaction != null) { var contextItem = commandProcessorContext.EfiTransaction.GetContextValue <DiagramContextItem>( EfiTransactionOriginator.TransactionOriginatorDiagramId); if (contextItem != null && contextItem.DiagramId == diagram.Id.Value) { // look in the dictionary for an 'add' to an EntityTypeShape that points to this modelobject. if (shapeChangeInfoExists == false) { var cmd = new CreateEntityTypeShapeCommand(diagram, entityType); CommandProcessor.InvokeSingleCommand(commandProcessorContext, cmd); } // We have the ability to create an EntityType and an Inheritance in one transaction, so we need // to check for it here. if (entityType.BaseType.Target != null) { InjectInheritanceConnectorCommand( commandProcessorContext, shapeChangeInfoSet, diagram, entityType.BaseType, changeAction); } } } } else if (changeAction == XObjectChange.Remove && shapeChangeInfoExists == false) { // this is happening before the transaction is taking place so we are free to look up anti-dependencies // on this delete foreach ( var entityTypeShape in entityType.GetAntiDependenciesOfType <EntityTypeShape>() .Where(ets => ets.Diagram != null && ets.Diagram.Id == diagram.Id.Value)) { if (entityTypeShape != null) { var deleteEntityTypeShapeCommand = entityTypeShape.GetDeleteCommand(); CommandProcessor.InvokeSingleCommand(commandProcessorContext, deleteEntityTypeShapeCommand); } } } }
private void OnBeforeModelChangesCommitted(object sender, EfiChangingEventArgs e) { if (e.CommandProcessorContext != null) { var artifact = e.CommandProcessorContext.Artifact; if (_diagrams != null) { // Copy the current Xml changes over since we are going to modifying the transaction var xmlChangesCopy = new List <IXmlChange>(); var shapeChangeInfoSet = new HashSet <ShapeChangeInformation>(); foreach (var xmlChange in e.CommandProcessorContext.EfiTransaction.XmlChanges) { // if the changed node is whitespace, ignore it as we don't want to pick up false // annotations for it if (xmlChange.Node.NodeType == XmlNodeType.Text) { var text = xmlChange.Node as XText; var trimmedValue = text.Value.Trim(); if (String.IsNullOrEmpty(trimmedValue)) { continue; } } // Ignore changes to annotations var externalModelChange = new EFArtifact.ExternalXMLModelChange(xmlChange, artifact.ExpectEFObjectForXObject); if (externalModelChange.IsAnnotationChange(artifact.GetNamespaces())) { continue; } // Construct a fast-queryable lookup for the shape change information that already // exists in this transaction. The transaction may have already fired some rules that // injected shape (DiagramEFObjects) information into the transaction. The purpose // of this lookup is to skip duplicate additions/removals. var changedEFObject = externalModelChange.ChangedEFObject; if (changedEFObject != null) { var diagramObject = changedEFObject as ModelDiagram.BaseDiagramObject; if (diagramObject != null) { var changedEntityTypeShape = changedEFObject as EntityTypeShape; var changedAssociationConnector = changedEFObject as AssociationConnector; var changedInheritanceConnector = changedEFObject as InheritanceConnector; var shapeChangeInformation = new ShapeChangeInformation(); if (changedEntityTypeShape != null) { shapeChangeInformation.ModelEFObject = changedEntityTypeShape.EntityType.Target; } else if (changedAssociationConnector != null) { shapeChangeInformation.ModelEFObject = changedAssociationConnector.Association.Target; } else if (changedInheritanceConnector != null) { shapeChangeInformation.ModelEFObject = changedInheritanceConnector.EntityType.Target; } shapeChangeInformation.ChangeType = xmlChange.Action; Debug.Assert( diagramObject.Diagram != null, changedEFObject.ToPrettyString() + " doesn't belong to any diagram."); if (diagramObject.Diagram != null) { shapeChangeInformation.DiagramId = diagramObject.Diagram.Id; } shapeChangeInfoSet.Add(shapeChangeInformation); } } xmlChangesCopy.Add(xmlChange); } // Call resolve on the diagram objects in the model here. When the EDMX is being incrementally updated by some // external factor (for example, a database project in a by-ref edmx), there may be situations where the existing // diagram objects are not resolved yet. This means that when we try to create entity type shapes or association connectors // the logic assumes that there aren't any anti-dependencies of the model elements, so it adds duplicate shapes/connectors // which can affect the loading of the model later (it will hit DSL diagram validation logic). XmlModelHelper.NormalizeAndResolve(this); // If we see that an Association, EntityType, or EntityTypeBaseType has been added in the model // changes, then we create the subsequent diagram EFObjects (AssociationConnector, EntityTypeShape, // and InheritanceConnector). By creating them here, this will automatically push new changes onto the // Xml transaction foreach (var xmlChange in xmlChangesCopy) { var changedEFObject = ModelItemAnnotation.GetModelItem(xmlChange.Node); if (changedEFObject != null && ModelHelper.GetBaseModelRoot(changedEFObject) is ConceptualEntityModel) { var entityType = changedEFObject as ConceptualEntityType; var association = changedEFObject as Association; var baseType = changedEFObject as EntityTypeBaseType; foreach (var diagram in _diagrams) { if (entityType != null) { InjectEntityTypeShapeCommand( e.CommandProcessorContext, shapeChangeInfoSet, diagram, entityType, xmlChange.Action); } else if (association != null) { InjectAssociationConnectorCommand( e.CommandProcessorContext, shapeChangeInfoSet, diagram, association, xmlChange.Action); } else if (baseType != null) { InjectInheritanceConnectorCommand( e.CommandProcessorContext, shapeChangeInfoSet, diagram, baseType, xmlChange.Action); } } } } } } }
private static void InjectInheritanceConnectorCommand( CommandProcessorContext commandProcessorContext, HashSet<ShapeChangeInformation> shapeChangeInfoSet, Diagram diagram, EntityTypeBaseType baseType, XObjectChange changeAction) { // First check to see if there is already a change in the original transaction // for the InheritanceConnector that matches the one that we're getting var shapeChangeInfoToQuery = new ShapeChangeInformation { ChangeType = changeAction, ModelEFObject = baseType.OwnerEntityType, DiagramId = diagram.Id.Value }; var shapeChangeInfoExists = shapeChangeInfoSet.Contains(shapeChangeInfoToQuery); if (changeAction == XObjectChange.Add && shapeChangeInfoExists == false) { var participatingEntityTypes = new List<EntityType>(); var derivedEntityType = baseType.Parent as EntityType; Debug.Assert(derivedEntityType != null, "Where is the parent EntityType of this BaseType attribute?"); if (derivedEntityType != null) { // The inheritance connector is added if // - the participating entities exists in the diagram. // or // - the participating entities will be added in the current transaction. participatingEntityTypes.Add(derivedEntityType); participatingEntityTypes.Add(((ConceptualEntityType)derivedEntityType).BaseType.Target); foreach (var entityType in participatingEntityTypes) { if (diagram.EntityTypeShapes.Where(ets => ets.EntityType.Target == entityType).Any() || shapeChangeInfoSet.Where( sc => sc.DiagramId == diagram.Id.Value && sc.ModelEFObject == entityType && sc.ChangeType == XObjectChange.Add) .Any()) { continue; } // If it reach this point, that means that we should not add inheritance connector in the diagram. return; } var cmd = new CreateInheritanceConnectorCommand(diagram, derivedEntityType); CommandProcessor.InvokeSingleCommand(commandProcessorContext, cmd); } } else if (changeAction == XObjectChange.Remove && shapeChangeInfoExists == false) { // this is happening before the transaction is taking place so we are free to look up anti-dependencies // on this delete var owningEntityType = baseType.Parent as EntityType; if (owningEntityType != null) { foreach ( var inheritanceConnector in owningEntityType.GetAntiDependenciesOfType<InheritanceConnector>() .Where(ic => ic.Diagram != null && ic.Diagram.Id == diagram.Id.Value)) { if (inheritanceConnector != null) { var deleteInheritanceConnectorCommand = inheritanceConnector.GetDeleteCommand(); CommandProcessor.InvokeSingleCommand(commandProcessorContext, deleteInheritanceConnectorCommand); } } } } }
private static void InjectAssociationConnectorCommand( CommandProcessorContext commandProcessorContext, HashSet<ShapeChangeInformation> shapeChangeInfoSet, Diagram diagram, Association association, XObjectChange changeAction) { // First check to see if there is already a change in the original transaction // for the AssociationConnector that matches the one that we're getting var shapeChangeInfoToQuery = new ShapeChangeInformation { ChangeType = changeAction, ModelEFObject = association, DiagramId = diagram.Id.Value }; var shapeChangeInfoExists = shapeChangeInfoSet.Contains(shapeChangeInfoToQuery); // We only want to create model diagram if the transaction is originated from this diagram. if (changeAction == XObjectChange.Add && shapeChangeInfoExists == false) { // The association connector is added if // - the participating entities are in the diagram. // or // - the participating entities will be added in the current transaction. foreach (var end in association.AssociationEnds()) { if (end.Type != null && end.Type.Target != null) { if (diagram.EntityTypeShapes.Where(ets => ets.EntityType.Target == end.Type.Target).Any() || shapeChangeInfoSet.Where( sc => sc.DiagramId == diagram.Id.Value && sc.ModelEFObject == end.Type.Target && sc.ChangeType == XObjectChange.Add).Any()) { continue; } } // If it reach this point, that means that we should not add association connector in the diagram. return; } var cmd = new CreateAssociationConnectorCommand(diagram, association); CommandProcessor.InvokeSingleCommand(commandProcessorContext, cmd); } else if (changeAction == XObjectChange.Remove && shapeChangeInfoExists == false) { // this is happening before the transaction is taking place so we are free to look up anti-dependencies on this delete foreach ( var associationConnector in association.GetAntiDependenciesOfType<AssociationConnector>() .Where(ac => ac.Diagram != null && ac.Diagram.Id == diagram.Id.Value)) { if (associationConnector != null) { var deleteAssociationConnectorCommand = associationConnector.GetDeleteCommand(); CommandProcessor.InvokeSingleCommand(commandProcessorContext, deleteAssociationConnectorCommand); } } } }
private static void InjectEntityTypeShapeCommand( CommandProcessorContext commandProcessorContext, HashSet<ShapeChangeInformation> shapeChangeInfoSet, Diagram diagram, ConceptualEntityType entityType, XObjectChange changeAction) { // First check to see if there is already a change in the original transaction // for the EntityTypeShape that matches the one that we're getting var shapeChangeInfoToQuery = new ShapeChangeInformation { ChangeType = changeAction, ModelEFObject = entityType, DiagramId = diagram.Id.Value }; var shapeChangeInfoExists = shapeChangeInfoSet.Contains(shapeChangeInfoToQuery); // We only want to create model diagram if the transaction is originated from this diagram. if (changeAction == XObjectChange.Add) { // We only want to inject the entity-type-shape if the transaction is originated from the passed in diagram. if (commandProcessorContext != null && commandProcessorContext.EfiTransaction != null) { var contextItem = commandProcessorContext.EfiTransaction.GetContextValue<DiagramContextItem>( EfiTransactionOriginator.TransactionOriginatorDiagramId); if (contextItem != null && contextItem.DiagramId == diagram.Id.Value) { // look in the dictionary for an 'add' to an EntityTypeShape that points to this modelobject. if (shapeChangeInfoExists == false) { var cmd = new CreateEntityTypeShapeCommand(diagram, entityType); CommandProcessor.InvokeSingleCommand(commandProcessorContext, cmd); } // We have the ability to create an EntityType and an Inheritance in one transaction, so we need // to check for it here. if (entityType.BaseType.Target != null) { InjectInheritanceConnectorCommand( commandProcessorContext, shapeChangeInfoSet, diagram, entityType.BaseType, changeAction); } } } } else if (changeAction == XObjectChange.Remove && shapeChangeInfoExists == false) { // this is happening before the transaction is taking place so we are free to look up anti-dependencies // on this delete foreach ( var entityTypeShape in entityType.GetAntiDependenciesOfType<EntityTypeShape>() .Where(ets => ets.Diagram != null && ets.Diagram.Id == diagram.Id.Value)) { if (entityTypeShape != null) { var deleteEntityTypeShapeCommand = entityTypeShape.GetDeleteCommand(); CommandProcessor.InvokeSingleCommand(commandProcessorContext, deleteEntityTypeShapeCommand); } } } }
private void OnBeforeModelChangesCommitted(object sender, EfiChangingEventArgs e) { if (e.CommandProcessorContext != null) { var artifact = e.CommandProcessorContext.Artifact; if (_diagrams != null) { // Copy the current Xml changes over since we are going to modifying the transaction var xmlChangesCopy = new List<IXmlChange>(); var shapeChangeInfoSet = new HashSet<ShapeChangeInformation>(); foreach (var xmlChange in e.CommandProcessorContext.EfiTransaction.XmlChanges) { // if the changed node is whitespace, ignore it as we don't want to pick up false // annotations for it if (xmlChange.Node.NodeType == XmlNodeType.Text) { var text = xmlChange.Node as XText; var trimmedValue = text.Value.Trim(); if (String.IsNullOrEmpty(trimmedValue)) { continue; } } // Ignore changes to annotations var externalModelChange = new EFArtifact.ExternalXMLModelChange(xmlChange, artifact.ExpectEFObjectForXObject); if (externalModelChange.IsAnnotationChange(artifact.GetNamespaces())) { continue; } // Construct a fast-queryable lookup for the shape change information that already // exists in this transaction. The transaction may have already fired some rules that // injected shape (DiagramEFObjects) information into the transaction. The purpose // of this lookup is to skip duplicate additions/removals. var changedEFObject = externalModelChange.ChangedEFObject; if (changedEFObject != null) { var diagramObject = changedEFObject as ModelDiagram.BaseDiagramObject; if (diagramObject != null) { var changedEntityTypeShape = changedEFObject as EntityTypeShape; var changedAssociationConnector = changedEFObject as AssociationConnector; var changedInheritanceConnector = changedEFObject as InheritanceConnector; var shapeChangeInformation = new ShapeChangeInformation(); if (changedEntityTypeShape != null) { shapeChangeInformation.ModelEFObject = changedEntityTypeShape.EntityType.Target; } else if (changedAssociationConnector != null) { shapeChangeInformation.ModelEFObject = changedAssociationConnector.Association.Target; } else if (changedInheritanceConnector != null) { shapeChangeInformation.ModelEFObject = changedInheritanceConnector.EntityType.Target; } shapeChangeInformation.ChangeType = xmlChange.Action; Debug.Assert( diagramObject.Diagram != null, changedEFObject.ToPrettyString() + " doesn't belong to any diagram."); if (diagramObject.Diagram != null) { shapeChangeInformation.DiagramId = diagramObject.Diagram.Id; } shapeChangeInfoSet.Add(shapeChangeInformation); } } xmlChangesCopy.Add(xmlChange); } // Call resolve on the diagram objects in the model here. When the EDMX is being incrementally updated by some // external factor (for example, a database project in a by-ref edmx), there may be situations where the existing // diagram objects are not resolved yet. This means that when we try to create entity type shapes or association connectors // the logic assumes that there aren't any anti-dependencies of the model elements, so it adds duplicate shapes/connectors // which can affect the loading of the model later (it will hit DSL diagram validation logic). XmlModelHelper.NormalizeAndResolve(this); // If we see that an Association, EntityType, or EntityTypeBaseType has been added in the model // changes, then we create the subsequent diagram EFObjects (AssociationConnector, EntityTypeShape, // and InheritanceConnector). By creating them here, this will automatically push new changes onto the // Xml transaction foreach (var xmlChange in xmlChangesCopy) { var changedEFObject = ModelItemAnnotation.GetModelItem(xmlChange.Node); if (changedEFObject != null && ModelHelper.GetBaseModelRoot(changedEFObject) is ConceptualEntityModel) { var entityType = changedEFObject as ConceptualEntityType; var association = changedEFObject as Association; var baseType = changedEFObject as EntityTypeBaseType; foreach (var diagram in _diagrams) { if (entityType != null) { InjectEntityTypeShapeCommand( e.CommandProcessorContext, shapeChangeInfoSet, diagram, entityType, xmlChange.Action); } else if (association != null) { InjectAssociationConnectorCommand( e.CommandProcessorContext, shapeChangeInfoSet, diagram, association, xmlChange.Action); } else if (baseType != null) { InjectInheritanceConnectorCommand( e.CommandProcessorContext, shapeChangeInfoSet, diagram, baseType, xmlChange.Action); } } } } } } }