private void SetInitialMultiplicity(BidirectionalAssociation element) { // valid bidirectional associations: // EF6 - entity to entity // EFCore - entity to entity, entity to dependent, dependent to entity // EFCore5Plus - entity to entity, entity to dependent, dependent to entity ModelRoot modelRoot = element.Source.ModelRoot; EFVersion entityFrameworkVersion = modelRoot.EntityFrameworkVersion; if (entityFrameworkVersion == EFVersion.EF6) { if (element.Source.IsEntity() && element.Target.IsEntity()) { element.SourceMultiplicity = Multiplicity.One; element.TargetMultiplicity = Multiplicity.ZeroMany; } } else if (entityFrameworkVersion == EFVersion.EFCore && !modelRoot.IsEFCore5Plus) { if (element.Source.IsEntity() && element.Target.IsEntity()) { element.SourceMultiplicity = Multiplicity.One; element.TargetMultiplicity = Multiplicity.ZeroMany; } if (element.Source.IsEntity() && element.Target.IsDependent()) { element.SourceMultiplicity = Multiplicity.One; element.TargetMultiplicity = Multiplicity.One; } if (element.Source.IsDependent() && element.Target.IsEntity()) { element.SourceMultiplicity = Multiplicity.One; element.TargetMultiplicity = Multiplicity.One; } } else if (entityFrameworkVersion == EFVersion.EFCore && modelRoot.IsEFCore5Plus) { if (element.Source.IsEntity() && element.Target.IsEntity()) { element.SourceMultiplicity = Multiplicity.One; element.TargetMultiplicity = Multiplicity.ZeroMany; } if (element.Source.IsEntity() && element.Target.IsDependent()) { element.SourceMultiplicity = Multiplicity.One; element.TargetMultiplicity = Multiplicity.One; } if (element.Source.IsDependent() && element.Target.IsEntity()) { element.SourceMultiplicity = Multiplicity.One; element.TargetMultiplicity = Multiplicity.One; } } }
public override string GetToolTipText(DiagramItem item) { BidirectionalAssociation association = item.Shape.ModelElement as BidirectionalAssociation; return(association != null ? $"{association.Source.Name}.{association.TargetPropertyName} <--> {association.Target.Name}.{association.SourcePropertyName}" : string.Empty); }
private void OnMenuSplitAssociation(object sender, EventArgs e) { BidirectionalAssociation selected = CurrentSelection.OfType <BidirectionalConnector>() .Select(connector => connector.ModelElement) .Cast <BidirectionalAssociation>() .Single(); ((EFModelDocData)CurrentDocData).Split(selected); }
/// <summary> /// Called after the IsSourceImplementNotifyTracking property changes. /// </summary> /// <param name="element">The model element that has the property that changed. </param> /// <param name="oldValue">The previous value of the property. </param> /// <param name="newValue">The new value of the property. </param> protected override void OnValueChanged(BidirectionalAssociation element, bool oldValue, bool newValue) { base.OnValueChanged(element, oldValue, newValue); if (!element.Store.InUndoRedoOrRollback && newValue) { DomainPropertyInfo propInfo = element.Store.DomainDataDirectory.GetDomainProperty(SourceImplementNotifyDomainPropertyId); propInfo.NotifyValueChange(element); } }
private static ElementLink ConnectModelClassToModelClass(ModelClass sourceAccepted, ModelClass targetAccepted) { ElementLink result = new BidirectionalAssociation(sourceAccepted, targetAccepted); if (DomainClassInfo.HasNameProperty(result)) { DomainClassInfo.SetUniqueName(result); } return(result); }
private static string GetDisplayPropertyFromModelClassForSourcesCompartment(ModelElement element) { BidirectionalAssociation association = (BidirectionalAssociation)element; ModelClass source = association.Source; if (!string.IsNullOrEmpty(association.SourcePropertyName)) { return($"{association.SourcePropertyName} : {source.Name}"); } return(source.Name); }
private void ProcessBidirectionalAssociations(List<ModelBidirectionalAssociation> bidirectionalAssociations) { foreach (ModelBidirectionalAssociation data in bidirectionalAssociations) { if (Store.Get<BidirectionalAssociation>() .Any(x => x.Target.FullName == data.TargetClassFullName && x.Source.FullName == data.SourceClassFullName && x.TargetPropertyName == data.TargetPropertyName && x.SourcePropertyName == data.SourcePropertyName)) continue; if (Store.Get<BidirectionalAssociation>() .Any(x => x.Source.FullName == data.TargetClassFullName && x.Target.FullName == data.SourceClassFullName && x.SourcePropertyName == data.TargetPropertyName && x.TargetPropertyName == data.SourcePropertyName)) continue; ModelClass source = Store.Get<ModelClass>().FirstOrDefault(c => c.FullName == data.SourceClassFullName); if (source == null) continue; ModelClass target = Store.Get<ModelClass>().FirstOrDefault(c => c.Name == data.TargetClassName && c.Namespace == data.TargetClassNamespace); if (target == null) continue; // ReSharper disable once UnusedVariable BidirectionalAssociation element = new BidirectionalAssociation(Store, new[] { new RoleAssignment(BidirectionalAssociation.BidirectionalSourceDomainRoleId, source), new RoleAssignment(BidirectionalAssociation.BidirectionalTargetDomainRoleId, target) }, new[] { new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, ConvertMultiplicity(data.SourceMultiplicity)), new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, ConvertMultiplicity(data.TargetMultiplicity)), new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, data.TargetPropertyName), new PropertyAssignment(Association.TargetSummaryDomainPropertyId, data.TargetSummary), new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, data.TargetDescription), new PropertyAssignment(BidirectionalAssociation.SourcePropertyNameDomainPropertyId, data.SourcePropertyName), new PropertyAssignment(BidirectionalAssociation.SourceSummaryDomainPropertyId, data.SourceSummary), new PropertyAssignment(BidirectionalAssociation.SourceDescriptionDomainPropertyId, data.SourceDescription), }); } }
public override void ElementAdded(ElementAddedEventArgs e) { base.ElementAdded(e); BidirectionalAssociation element = (BidirectionalAssociation)e.ModelElement; Store store = element.Store; Transaction current = store.TransactionManager.CurrentTransaction; if (current.IsSerializing) { return; } PresentationHelper.UpdateAssociationDisplay(element); }
private static void CheckSourceForDisplayText(BidirectionalAssociation bidirectionalAssociation) { Match match = DisplayAttributeRegex.Match(bidirectionalAssociation.SourceCustomAttributes); // is there a custom attribute for [Display]? if (match != Match.Empty) { // if SourceDisplayText is empty, move the Name down to SourceDisplayText if (string.IsNullOrWhiteSpace(bidirectionalAssociation.SourceDisplayText)) bidirectionalAssociation.SourceDisplayText = match.Groups[3].Value; // if custom attribute's Name matches SourceDisplayText, remove that attribute, leaving other custom attributes if present if (match.Groups[3].Value == bidirectionalAssociation.SourceDisplayText) bidirectionalAssociation.SourceCustomAttributes = match.Groups[1].Value + match.Groups[4].Value; } }
/// <summary>Performs the reset operation for the IsSourceImplementNotifyTracking property for a model element.</summary> /// <param name="element">The model element that has the property to reset.</param> internal void ResetValue(BidirectionalAssociation element) { object calculatedValue = null; try { calculatedValue = element.Source?.ImplementNotify; } catch (NullReferenceException) { } catch (Exception e) { if (CriticalException.IsCriticalException(e)) { throw; } } if (calculatedValue != null && element.SourceImplementNotify == (bool)calculatedValue) { element.isSourceImplementNotifyTrackingPropertyStorage = true; } }
/// <summary> /// OnBeforePaint is called at the start of the ShapeElement's painting. /// It provides an opportunity for developers to update and override resources /// before they're used in painting. /// </summary> /// <remarks> /// You can override existing resources by calling StyleSet.OverrideXXX and /// changing the specific setting that you would like. /// </remarks> protected override void OnBeforePaint() { if (ModelElement is Association element) { BidirectionalAssociation bidirectionalElement = ModelElement as BidirectionalAssociation; bool hasAutoInclude = element.Source.ModelRoot.IsEFCore5Plus && (element.TargetAutoInclude || bidirectionalElement?.SourceAutoInclude == true); if (hasAutoInclude) { PenSettings settings = StyleSet.GetOverriddenPenSettings(DiagramPens.ConnectionLine) ?? new PenSettings(); settings.Width = 0.05f; StyleSet.OverridePen(DiagramPens.ConnectionLine, settings); } else { StyleSet.ClearPenOverride(DiagramPens.ConnectionLine); } } else { StyleSet.ClearPenOverride(DiagramPens.ConnectionLine); } }
private void SetSourcePropertyName(BidirectionalAssociation element) { if (string.IsNullOrEmpty(element.SourcePropertyName)) { string rootName = element.SourceMultiplicity == Multiplicity.ZeroMany && ModelRoot.PluralizationService?.IsSingular(element.Source.Name) == true ? ModelRoot.PluralizationService.Pluralize(element.Source.Name) : element.Source.Name; string identifierName = rootName; int index = 0; ModelClass modelClass = element.Target; while (modelClass.HasPropertyNamed(identifierName) || modelClass.AllSubclasses.Any(c => c.HasPropertyNamed(identifierName)) || modelClass.AllSuperclasses.Any(c => c.HasPropertyNamed(identifierName))) { identifierName = $"{rootName}_{++index}"; } element.SourcePropertyName = identifierName; } }
/// <summary> /// Returns the property descriptors for the described Association domain class, adding tracking property /// descriptor(s). /// </summary> private PropertyDescriptorCollection GetCustomProperties(Attribute[] attributes) { // Get the default property descriptors from the base class PropertyDescriptorCollection propertyDescriptors = base.GetProperties(attributes); //Add the descriptor for the tracking property. if (ModelElement is Association association) { ModelRoot modelRoot = association.Source.ModelRoot; storeDomainDataDirectory = association.Store.DomainDataDirectory; BidirectionalAssociation bidirectionalAssociation = association as BidirectionalAssociation; // show FKPropertyName only when possible and required if (!modelRoot.ExposeForeignKeys || (association.SourceRole != EndpointRole.Dependent && association.TargetRole != EndpointRole.Dependent)) { propertyDescriptors.Remove("FKPropertyName"); } // EF6 can't have declared foreign keys for 1..1 / 0-1..1 / 1..0-1 / 0-1..0-1 relationships if (modelRoot.EntityFrameworkVersion == EFVersion.EF6 && association.SourceMultiplicity != Multiplicity.ZeroMany && association.TargetMultiplicity != Multiplicity.ZeroMany) { propertyDescriptors.Remove("FKPropertyName"); } // no FKs for aggregates if (association.Source.IsDependentType || association.Target.IsDependentType) { propertyDescriptors.Remove("FKPropertyName"); } // only display roles for 1..1 and 0-1..0-1 associations if ((association.SourceMultiplicity != Multiplicity.One || association.TargetMultiplicity != Multiplicity.One) && (association.SourceMultiplicity != Multiplicity.ZeroOne || association.TargetMultiplicity != Multiplicity.ZeroOne)) { propertyDescriptors.Remove("SourceRole"); propertyDescriptors.Remove("TargetRole"); } // only display delete behavior on the principal end // except that owned types don't have deletiion behavior choices if (association.SourceRole != EndpointRole.Principal || association.Source.IsDependentType || association.Target.IsDependentType) { propertyDescriptors.Remove("SourceDeleteAction"); } if (association.TargetRole != EndpointRole.Principal || association.Source.IsDependentType || association.Target.IsDependentType) { propertyDescriptors.Remove("TargetDeleteAction"); } // only show JoinTableName if is *..* association if (association.SourceMultiplicity != Multiplicity.ZeroMany || association.TargetMultiplicity != Multiplicity.ZeroMany) { propertyDescriptors.Remove("JoinTableName"); } // implementNotify implicitly defines autoproperty as false, so we don't display it if (association.TargetImplementNotify) { propertyDescriptors.Remove("TargetAutoProperty"); } if (bidirectionalAssociation != null && bidirectionalAssociation.SourceImplementNotify) { propertyDescriptors.Remove("SourceAutoProperty"); } // we're only allowing ..1 and ..0-1 associations to have backing fields if (association.TargetMultiplicity == Multiplicity.ZeroMany) { propertyDescriptors.Remove("TargetAutoProperty"); } if (bidirectionalAssociation != null && bidirectionalAssociation.SourceMultiplicity == Multiplicity.ZeroMany) { propertyDescriptors.Remove("SourceAutoProperty"); } // EF6 doesn't support property access modes if (modelRoot.EntityFrameworkVersion == EFVersion.EF6) { propertyDescriptors.Remove("TargetPropertyAccessMode"); propertyDescriptors.Remove("SourcePropertyAccessMode"); } // only show backing field name and property access mode if not an autoproperty if (association.TargetAutoProperty) { propertyDescriptors.Remove("TargetBackingFieldName"); propertyDescriptors.Remove("TargetPropertyAccessMode"); } if (bidirectionalAssociation == null || bidirectionalAssociation.SourceAutoProperty) { propertyDescriptors.Remove("SourceBackingFieldName"); propertyDescriptors.Remove("SourcePropertyAccessMode"); } /********************************************************************************/ //Add the descriptors for the tracking properties propertyDescriptors.Add(new TrackingPropertyDescriptor(association , storeDomainDataDirectory.GetDomainProperty(Association.CollectionClassDomainPropertyId) , storeDomainDataDirectory.GetDomainProperty(Association.IsCollectionClassTrackingDomainPropertyId) , new Attribute[] { new DisplayNameAttribute("Collection Class") , new DescriptionAttribute("Type of collections generated. Overrides the default collection class for the model") , new CategoryAttribute("Code Generation") })); if (association.TargetMultiplicity == Multiplicity.One || association.TargetMultiplicity == Multiplicity.ZeroOne) { propertyDescriptors.Add(new TrackingPropertyDescriptor(association , storeDomainDataDirectory.GetDomainProperty(Association.TargetImplementNotifyDomainPropertyId) , storeDomainDataDirectory.GetDomainProperty(Association.IsTargetImplementNotifyTrackingDomainPropertyId) , new Attribute[] { new DisplayNameAttribute("Implement INotifyPropertyChanged") , new DescriptionAttribute("Should this end participate in INotifyPropertyChanged activities? " + "Only valid for non-collection targets.") , new CategoryAttribute("End 2") })); propertyDescriptors.Add(new TrackingPropertyDescriptor(association , storeDomainDataDirectory.GetDomainProperty(Association.TargetAutoPropertyDomainPropertyId) , storeDomainDataDirectory.GetDomainProperty(Association.IsTargetAutoPropertyTrackingDomainPropertyId) , new Attribute[] { new DisplayNameAttribute("End1 Is Auto Property") , new DescriptionAttribute("If false, generates a backing field and a partial method to hook getting and setting the property. " + "If true, generates a simple auto property. Only valid for non-collection properties.") , new CategoryAttribute("End 2") })); } if (bidirectionalAssociation?.SourceMultiplicity == Multiplicity.One || bidirectionalAssociation?.SourceMultiplicity == Multiplicity.ZeroOne) { propertyDescriptors.Add(new TrackingPropertyDescriptor(bidirectionalAssociation , storeDomainDataDirectory.GetDomainProperty(BidirectionalAssociation.SourceImplementNotifyDomainPropertyId) , storeDomainDataDirectory.GetDomainProperty(BidirectionalAssociation.IsSourceImplementNotifyTrackingDomainPropertyId) , new Attribute[] { new DisplayNameAttribute("Implement INotifyPropertyChanged") , new DescriptionAttribute("Should this end participate in INotifyPropertyChanged activities? " + "Only valid for non-collection targets.") , new CategoryAttribute("End 1") })); propertyDescriptors.Add(new TrackingPropertyDescriptor(association , storeDomainDataDirectory.GetDomainProperty(BidirectionalAssociation.SourceAutoPropertyDomainPropertyId) , storeDomainDataDirectory.GetDomainProperty(BidirectionalAssociation.IsSourceAutoPropertyTrackingDomainPropertyId) , new Attribute[] { new DisplayNameAttribute("End2 Is Auto Property") , new DescriptionAttribute("If false, generates a backing field and a partial method to hook getting and setting the property. " + "If true, generates a simple auto property. Only valid for non-collection properties.") , new CategoryAttribute("End 1") })); } } // Return the property descriptors for this element return(propertyDescriptors); }
public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e) { base.ElementPropertyChanged(e); Association element = (Association)e.ModelElement; if (element.IsDeleted) { return; } Store store = element.Store; Transaction current = store.TransactionManager.CurrentTransaction; if (current.IsSerializing) { return; } if (Equals(e.NewValue, e.OldValue)) { return; } List <string> errorMessages = EFCoreValidator.GetErrors(element).ToList(); BidirectionalAssociation bidirectionalAssociation = element as BidirectionalAssociation; switch (e.DomainProperty.Name) { case "SourceCustomAttributes": if (bidirectionalAssociation != null && !string.IsNullOrWhiteSpace(bidirectionalAssociation.SourceCustomAttributes)) { bidirectionalAssociation.SourceCustomAttributes = $"[{bidirectionalAssociation.SourceCustomAttributes.Trim('[', ']')}]"; CheckSourceForDisplayText(bidirectionalAssociation); } break; case "SourceDisplayText": if (bidirectionalAssociation != null) { CheckSourceForDisplayText(bidirectionalAssociation); } break; case "SourceMultiplicity": Multiplicity sourceMultiplicity = (Multiplicity)e.NewValue; // change unidirectional source cardinality // if target is dependent // source cardinality is 0..1 or 1 if (element.Target.IsDependentType && sourceMultiplicity == Multiplicity.ZeroMany) { errorMessages.Add($"Can't have a 0..* association from {element.Target.Name} to dependent type {element.Source.Name}"); break; } if ((sourceMultiplicity == Multiplicity.One && element.TargetMultiplicity == Multiplicity.One) || (sourceMultiplicity == Multiplicity.ZeroOne && element.TargetMultiplicity == Multiplicity.ZeroOne)) { if (element.SourceRole != EndpointRole.NotSet) { element.SourceRole = EndpointRole.NotSet; } if (element.TargetRole != EndpointRole.NotSet) { element.TargetRole = EndpointRole.NotSet; } } else { SetEndpointRoles(element); } // cascade delete behavior could now be illegal. Reset to default element.SourceDeleteAction = DeleteAction.Default; element.TargetDeleteAction = DeleteAction.Default; break; case "SourcePropertyName": string sourcePropertyNameErrorMessage = ValidateAssociationIdentifier(element, element.Target, (string)e.NewValue); if (EFModelDiagram.IsDropping && sourcePropertyNameErrorMessage != null) { element.Delete(); } else { errorMessages.Add(sourcePropertyNameErrorMessage); } break; case "SourceRole": if (element.Source.IsDependentType) { element.SourceRole = EndpointRole.Dependent; element.TargetRole = EndpointRole.Principal; } else if (!SetEndpointRoles(element)) { if (element.SourceRole == EndpointRole.Dependent && element.TargetRole != EndpointRole.Principal) { element.TargetRole = EndpointRole.Principal; } else if (element.SourceRole == EndpointRole.Principal && element.TargetRole != EndpointRole.Dependent) { element.TargetRole = EndpointRole.Dependent; } } break; case "TargetCustomAttributes": if (!string.IsNullOrWhiteSpace(element.TargetCustomAttributes)) { element.TargetCustomAttributes = $"[{element.TargetCustomAttributes.Trim('[', ']')}]"; CheckTargetForDisplayText(element); } break; case "TargetDisplayText": CheckTargetForDisplayText(element); break; case "TargetMultiplicity": Multiplicity newTargetMultiplicity = (Multiplicity)e.NewValue; // change unidirectional target cardinality // if target is dependent // target cardinality must be 0..1 or 1 if (element.Target.IsDependentType && newTargetMultiplicity == Multiplicity.ZeroMany) { errorMessages.Add($"Can't have a 0..* association from {element.Source.Name} to dependent type {element.Target.Name}"); break; } if ((element.SourceMultiplicity == Multiplicity.One && newTargetMultiplicity == Multiplicity.One) || (element.SourceMultiplicity == Multiplicity.ZeroOne && newTargetMultiplicity == Multiplicity.ZeroOne)) { if (element.SourceRole != EndpointRole.NotSet) { element.SourceRole = EndpointRole.NotSet; } if (element.TargetRole != EndpointRole.NotSet) { element.TargetRole = EndpointRole.NotSet; } } else { SetEndpointRoles(element); } // cascade delete behavior could now be illegal. Reset to default element.SourceDeleteAction = DeleteAction.Default; element.TargetDeleteAction = DeleteAction.Default; break; case "TargetPropertyName": // if we're creating an association via drag/drop, it's possible the existing property name // is the same as the default property name. The default doesn't get created until the transaction is // committed, so the drop's action will cause a name clash. Remove the clashing property, but // only if drag/drop. string targetPropertyNameErrorMessage = ValidateAssociationIdentifier(element, element.Source, (string)e.NewValue); if (EFModelDiagram.IsDropping && targetPropertyNameErrorMessage != null) { element.Delete(); } else { errorMessages.Add(targetPropertyNameErrorMessage); } break; case "TargetRole": if (element.Target.IsDependentType) { element.SourceRole = EndpointRole.Principal; element.TargetRole = EndpointRole.Dependent; } else if (!SetEndpointRoles(element)) { if (element.TargetRole == EndpointRole.Dependent && element.SourceRole != EndpointRole.Principal) { element.SourceRole = EndpointRole.Principal; } else if (element.TargetRole == EndpointRole.Principal && element.SourceRole != EndpointRole.Dependent) { element.SourceRole = EndpointRole.Dependent; } } break; } errorMessages = errorMessages.Where(m => m != null).ToList(); if (errorMessages.Any()) { current.Rollback(); ErrorDisplay.Show(string.Join("\n", errorMessages)); } }
/// <summary> /// Method to set IsSourceImplementNotifyTracking to false so that this instance of this tracking property is not /// storage-based. /// </summary> /// <param name="element"> /// The element on which to reset the property value. /// </param> internal void PreResetValue(BidirectionalAssociation element) { // Force the IsSourceImplementNotifyTracking property to false so that the value // of the SourceImplementNotify property is retrieved from storage. element.isSourceImplementNotifyTrackingPropertyStorage = false; }
private void ProcessBidirectionalAssociations(ParsingModels.ModelClass modelClass) { List<ModelBidirectionalAssociation> bidirectionalAssociations = modelClass.BidirectionalAssociations; foreach (ModelBidirectionalAssociation data in bidirectionalAssociations) { if (Store.ModelRoot().EntityFrameworkVersion == EFVersion.EF6 && data.SourceMultiplicity != ParsingModels.Multiplicity.ZeroMany && data.TargetMultiplicity != ParsingModels.Multiplicity.ZeroMany) data.ForeignKey = null; BidirectionalAssociation existing = Store.GetAll<BidirectionalAssociation>() .FirstOrDefault(x => x.Target.Name == data.TargetClassName && x.Source.Name == data.SourceClassName && x.Source.Name == modelClass.Name // just to be sure && x.TargetPropertyName == data.TargetPropertyName && x.SourcePropertyName == data.SourcePropertyName) ?? Store.GetAll<BidirectionalAssociation>() .FirstOrDefault(x => x.Source.Name == data.TargetClassName && x.Target.Name == data.SourceClassName && x.Target.Name == modelClass.Name // just to be sure && x.SourcePropertyName == data.TargetPropertyName && x.TargetPropertyName == data.SourcePropertyName); if (existing != null) { if (string.IsNullOrWhiteSpace(existing.FKPropertyName) && !string.IsNullOrWhiteSpace(data.ForeignKey)) { existing.FKPropertyName = string.Join(",", data.ForeignKey.Split(',').ToList().Select(p => p.Split('/').Last().Split(' ').Last())); existing.Source.ModelRoot.ExposeForeignKeys = true; } continue; } ModelClass source = Store.GetAll<ModelClass>().FirstOrDefault(c => c.Name == data.SourceClassName); ModelClass target = Store.GetAll<ModelClass>().FirstOrDefault(c => c.Name == data.TargetClassName); if (source == null || target == null || source.FullName != modelClass.FullName) continue; BidirectionalAssociation elementLink = (BidirectionalAssociation)BidirectionalAssociationBuilder.Connect(source, target); elementLink.SourceMultiplicity = ConvertMultiplicity(data.SourceMultiplicity); elementLink.TargetMultiplicity = ConvertMultiplicity(data.TargetMultiplicity); elementLink.TargetPropertyName = data.TargetPropertyName; elementLink.TargetSummary = data.TargetSummary; elementLink.TargetDescription = data.TargetDescription; elementLink.FKPropertyName = data.ForeignKey; elementLink.SourceRole = ConvertRole(data.SourceRole); elementLink.TargetRole = ConvertRole(data.TargetRole); elementLink.SourcePropertyName = data.SourcePropertyName; elementLink.SourceSummary = data.SourceSummary; elementLink.SourceDescription = data.SourceDescription; // ReSharper disable once UnusedVariable //BidirectionalAssociation element = new BidirectionalAssociation(Store, // new[] // { // new RoleAssignment(BidirectionalAssociation.BidirectionalSourceDomainRoleId, source), // new RoleAssignment(BidirectionalAssociation.BidirectionalTargetDomainRoleId, target) // }, // new[] // { // new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, ConvertMultiplicity(data.SourceMultiplicity)), // new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, ConvertMultiplicity(data.TargetMultiplicity)), // new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, data.TargetPropertyName), // new PropertyAssignment(Association.TargetSummaryDomainPropertyId, data.TargetSummary), // new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, data.TargetDescription), // new PropertyAssignment(Association.FKPropertyNameDomainPropertyId, data.ForeignKey), // new PropertyAssignment(Association.SourceRoleDomainPropertyId, ConvertRole(data.SourceRole)), // new PropertyAssignment(Association.TargetRoleDomainPropertyId, ConvertRole(data.TargetRole)), // new PropertyAssignment(BidirectionalAssociation.SourcePropertyNameDomainPropertyId, data.SourcePropertyName), // new PropertyAssignment(BidirectionalAssociation.SourceSummaryDomainPropertyId, data.SourceSummary), // new PropertyAssignment(BidirectionalAssociation.SourceDescriptionDomainPropertyId, data.SourceDescription), // }); AssociationChangedRules.SetEndpointRoles(elementLink); AssociationChangedRules.FixupForeignKeys(elementLink); // we could have a situation where there are no roles assigned (if 0/1-0/1 or 1-1). If we have exposed foreign keys, though, we can figure those out. if ((elementLink.SourceMultiplicity != Multiplicity.ZeroMany || elementLink.TargetMultiplicity != Multiplicity.ZeroMany) && (elementLink.SourceRole == EndpointRole.NotSet || elementLink.TargetRole == EndpointRole.NotSet) && !string.IsNullOrEmpty(elementLink.FKPropertyName)) { // which, if any, end has the foreign key properties in it? string firstFKPropertyName = elementLink.FKPropertyName.Split(',').First(); if (elementLink.Source.AllPropertyNames.Contains(firstFKPropertyName)) { elementLink.SourceRole = EndpointRole.Dependent; elementLink.TargetRole = EndpointRole.Principal; } else if (elementLink.Target.AllPropertyNames.Contains(firstFKPropertyName)) { elementLink.TargetRole = EndpointRole.Dependent; elementLink.SourceRole = EndpointRole.Principal; } } } }
private void ProcessAssociation([NotNull] ModelClass source, [NotNull] ModelClass target, [NotNull] PropertyDeclarationSyntax propertyDecl, bool toMany = false) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (target == null) { throw new ArgumentNullException(nameof(target)); } if (propertyDecl == null) { throw new ArgumentNullException(nameof(propertyDecl)); } Transaction tx = Store.TransactionManager.CurrentTransaction == null ? Store.TransactionManager.BeginTransaction() : null; try { string propertyName = propertyDecl.Identifier.ToString(); // since we don't have enough information from the code, we'll create unidirectional associations // cardinality 1 on the source end, 0..1 or 0..* on the target, depending on the parameter XMLDocumentation xmlDocumentation = new XMLDocumentation(propertyDecl); // if the association doesn't yet exist, create it if (!Store.ElementDirectory .AllElements .OfType <UnidirectionalAssociation>() .Any(a => a.Source == source && a.Target == target && a.TargetPropertyName == propertyName)) { // if there's a unidirectional going the other direction, we'll whack that one and make a bidirectional // otherwise, proceed as planned UnidirectionalAssociation compliment = Store.ElementDirectory .AllElements .OfType <UnidirectionalAssociation>() .FirstOrDefault(a => a.Source == target && a.Target == source); if (compliment == null) { UnidirectionalAssociation _ = new UnidirectionalAssociation(Store, new[] { new RoleAssignment(UnidirectionalAssociation.UnidirectionalSourceDomainRoleId, source), new RoleAssignment(UnidirectionalAssociation.UnidirectionalTargetDomainRoleId, target) }, new[] { new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, Multiplicity.One), new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, toMany ? Multiplicity.ZeroMany : Multiplicity.ZeroOne), new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, propertyName), new PropertyAssignment(Association.TargetSummaryDomainPropertyId, xmlDocumentation.Summary), new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, xmlDocumentation.Description) }); } else { compliment.Delete(); BidirectionalAssociation _ = new BidirectionalAssociation(Store, new[] { new RoleAssignment(BidirectionalAssociation.BidirectionalSourceDomainRoleId, source), new RoleAssignment(BidirectionalAssociation.BidirectionalTargetDomainRoleId, target) }, new[] { new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, compliment.TargetMultiplicity), new PropertyAssignment(BidirectionalAssociation.SourcePropertyNameDomainPropertyId, compliment.TargetPropertyName), new PropertyAssignment(BidirectionalAssociation.SourceSummaryDomainPropertyId, compliment.TargetSummary), new PropertyAssignment(BidirectionalAssociation.SourceDescriptionDomainPropertyId, compliment.TargetDescription), new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, toMany ? Multiplicity.ZeroMany : Multiplicity.ZeroOne), new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, propertyName), new PropertyAssignment(Association.TargetSummaryDomainPropertyId, xmlDocumentation.Summary), new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, xmlDocumentation.Description) }); } } } catch { tx.Rollback(); tx = null; throw; } finally { tx?.Commit(); } }
private void ProcessBidirectionalAssociations(ParsingModels.ModelClass modelClass) { List<ModelBidirectionalAssociation> bidirectionalAssociations = modelClass.BidirectionalAssociations; foreach (ModelBidirectionalAssociation data in bidirectionalAssociations) { if (Store.ModelRoot().EntityFrameworkVersion == EFVersion.EF6 && data.SourceMultiplicity != ParsingModels.Multiplicity.ZeroMany && data.TargetMultiplicity != ParsingModels.Multiplicity.ZeroMany) data.ForeignKey = null; BidirectionalAssociation existing = Store.GetAll<BidirectionalAssociation>() .FirstOrDefault(x => x.Target.Name == data.TargetClassName && x.Source.Name == data.SourceClassName && x.Source.Name == modelClass.Name // just to be sure && x.TargetPropertyName == data.TargetPropertyName && x.SourcePropertyName == data.SourcePropertyName) ?? Store.GetAll<BidirectionalAssociation>() .FirstOrDefault(x => x.Source.Name == data.TargetClassName && x.Target.Name == data.SourceClassName && x.Target.Name == modelClass.Name // just to be sure && x.SourcePropertyName == data.TargetPropertyName && x.TargetPropertyName == data.SourcePropertyName); if (existing != null) { if (string.IsNullOrWhiteSpace(existing.FKPropertyName) && !string.IsNullOrWhiteSpace(data.ForeignKey)) { existing.FKPropertyName = data.ForeignKey; existing.Source.ModelRoot.ExposeForeignKeys = true; } continue; } ModelClass source = Store.GetAll<ModelClass>().FirstOrDefault(c => c.Name == data.SourceClassName); ModelClass target = Store.GetAll<ModelClass>().FirstOrDefault(c => c.Name == data.TargetClassName); if (source == null || target == null || source.FullName != modelClass.FullName) continue; // ReSharper disable once UnusedVariable BidirectionalAssociation element = new BidirectionalAssociation(Store, new[] { new RoleAssignment(BidirectionalAssociation.BidirectionalSourceDomainRoleId, source), new RoleAssignment(BidirectionalAssociation.BidirectionalTargetDomainRoleId, target) }, new[] { new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, ConvertMultiplicity(data.SourceMultiplicity)), new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, ConvertMultiplicity(data.TargetMultiplicity)), new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, data.TargetPropertyName), new PropertyAssignment(Association.TargetSummaryDomainPropertyId, data.TargetSummary), new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, data.TargetDescription), new PropertyAssignment(Association.FKPropertyNameDomainPropertyId, data.ForeignKey), new PropertyAssignment(Association.SourceRoleDomainPropertyId, ConvertRole(data.SourceRole)), new PropertyAssignment(Association.TargetRoleDomainPropertyId, ConvertRole(data.TargetRole)), new PropertyAssignment(BidirectionalAssociation.SourcePropertyNameDomainPropertyId, data.SourcePropertyName), new PropertyAssignment(BidirectionalAssociation.SourceSummaryDomainPropertyId, data.SourceSummary), new PropertyAssignment(BidirectionalAssociation.SourceDescriptionDomainPropertyId, data.SourceDescription), }); AssociationChangedRules.SetEndpointRoles(element); } }
private void ConfigureNewAssociation(BidirectionalAssociation element) { SetInitialMultiplicity(element); SetTargetPropertyName(element); SetSourcePropertyName(element); }
public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e) { base.ElementPropertyChanged(e); Association element = (Association)e.ModelElement; if (element.IsDeleted) { return; } Store store = element.Store; Transaction current = store.TransactionManager.CurrentTransaction; if (current.IsSerializing || ModelRoot.BatchUpdating) { return; } if (Equals(e.NewValue, e.OldValue)) { return; } List <string> errorMessages = EFCoreValidator.GetErrors(element).ToList(); BidirectionalAssociation bidirectionalAssociation = element as BidirectionalAssociation; switch (e.DomainProperty.Name) { case "FKPropertyName": { string fkPropertyName = e.NewValue?.ToString(); bool fkPropertyError = false; // these can be multiples, separated by a comma string[] priorForeignKeyPropertyNames = e.OldValue?.ToString().Split(',').Select(n => n.Trim()).ToArray() ?? new string[0]; IEnumerable <ModelAttribute> priorForeignKeyModelAttributes = string.IsNullOrEmpty(e.OldValue?.ToString()) ? Array.Empty <ModelAttribute>() : priorForeignKeyPropertyNames.Select(oldValue => element.Dependent.Attributes.FirstOrDefault(a => a.Name == oldValue)) .ToArray(); if (!string.IsNullOrEmpty(fkPropertyName)) { string tag = $"({element.Source.Name}:{element.Target.Name})"; if (element.Dependent == null) { errorMessages.Add($"{tag} can't have foreign keys defined; no dependent role found"); break; } string[] foreignKeyPropertyNames = element.GetForeignKeyPropertyNames(); int propertyCount = foreignKeyPropertyNames.Length; int identityCount = element.Principal.AllIdentityAttributes.Count(); if (propertyCount != identityCount) { errorMessages.Add($"{tag} foreign key must have zero or {identityCount} {(identityCount == 1 ? "property" : "properties")} defined, since " + $"{element.Principal.Name} has {identityCount} identity properties; found {propertyCount} instead"); fkPropertyError = true; } // validate names foreach (string propertyName in foreignKeyPropertyNames) { if (!CodeGenerator.IsValidLanguageIndependentIdentifier(propertyName)) { errorMessages.Add($"{tag} FK property name '{propertyName}' isn't a valid .NET identifier"); fkPropertyError = true; } if (element.Dependent.AllAttributes.Except(element.Dependent.Attributes).Any(a => a.Name == propertyName)) { errorMessages.Add($"{tag} FK property name '{propertyName}' is used in a base class of {element.Dependent.Name}"); fkPropertyError = true; } } if (!fkPropertyError) { // remove any flags and locks on the attributes that were foreign keys foreach (ModelAttribute modelAttribute in priorForeignKeyModelAttributes) { modelAttribute.SetLocks(Locks.None); modelAttribute.Summary = null; modelAttribute.IsForeignKey = false; modelAttribute.RedrawItem(); } element.EnsureForeignKeyAttributes(); IEnumerable <ModelAttribute> currentForeignKeyModelAttributes = foreignKeyPropertyNames.Select(newValue => element.Dependent.Attributes.FirstOrDefault(a => a.Name == newValue)); // add delete flags and locks to the attributes that are now foreign keys foreach (ModelAttribute modelAttribute in currentForeignKeyModelAttributes) { modelAttribute.SetLocks(Locks.None); modelAttribute.Summary = $"Foreign key for {element.GetDisplayText()}"; modelAttribute.SetLocks(Locks.Delete); modelAttribute.IsForeignKey = true; modelAttribute.RedrawItem(); } } } else { // foreign key was removed // remove locks foreach (ModelAttribute modelAttribute in priorForeignKeyModelAttributes) { modelAttribute.SetLocks(Locks.None); modelAttribute.Summary = null; modelAttribute.IsForeignKey = false; } } } break; case "SourceCustomAttributes": if (bidirectionalAssociation != null && !string.IsNullOrWhiteSpace(bidirectionalAssociation.SourceCustomAttributes)) { bidirectionalAssociation.SourceCustomAttributes = $"[{bidirectionalAssociation.SourceCustomAttributes.Trim('[', ']')}]"; CheckSourceForDisplayText(bidirectionalAssociation); } break; case "SourceDisplayText": if (bidirectionalAssociation != null) { CheckSourceForDisplayText(bidirectionalAssociation); } break; case "SourceMultiplicity": Multiplicity sourceMultiplicity = (Multiplicity)e.NewValue; // change unidirectional source cardinality // if target is dependent // source cardinality is 0..1 or 1 if (element.Target.IsDependentType && sourceMultiplicity == Multiplicity.ZeroMany) { errorMessages.Add($"Can't have a 0..* association from {element.Target.Name} to dependent type {element.Source.Name}"); break; } if ((sourceMultiplicity == Multiplicity.One && element.TargetMultiplicity == Multiplicity.One) || (sourceMultiplicity == Multiplicity.ZeroOne && element.TargetMultiplicity == Multiplicity.ZeroOne)) { if (element.SourceRole != EndpointRole.NotSet) { element.SourceRole = EndpointRole.NotSet; } if (element.TargetRole != EndpointRole.NotSet) { element.TargetRole = EndpointRole.NotSet; } } else { SetEndpointRoles(element); } // cascade delete behavior could now be illegal. Reset to default element.SourceDeleteAction = DeleteAction.Default; element.TargetDeleteAction = DeleteAction.Default; break; case "SourcePropertyName": string sourcePropertyNameErrorMessage = ValidateAssociationIdentifier(element, element.Target, (string)e.NewValue); if (EFModelDiagram.IsDropping && sourcePropertyNameErrorMessage != null) { element.Delete(); } else { errorMessages.Add(sourcePropertyNameErrorMessage); } break; case "SourceRole": if (element.Source.IsDependentType) { element.SourceRole = EndpointRole.Dependent; element.TargetRole = EndpointRole.Principal; } else if (!SetEndpointRoles(element)) { if (element.SourceRole == EndpointRole.Dependent && element.TargetRole != EndpointRole.Principal) { element.TargetRole = EndpointRole.Principal; } else if (element.SourceRole == EndpointRole.Principal && element.TargetRole != EndpointRole.Dependent) { element.TargetRole = EndpointRole.Dependent; } } break; case "TargetCustomAttributes": if (!string.IsNullOrWhiteSpace(element.TargetCustomAttributes)) { element.TargetCustomAttributes = $"[{element.TargetCustomAttributes.Trim('[', ']')}]"; CheckTargetForDisplayText(element); } break; case "TargetDisplayText": CheckTargetForDisplayText(element); break; case "TargetMultiplicity": Multiplicity newTargetMultiplicity = (Multiplicity)e.NewValue; // change unidirectional target cardinality // if target is dependent // target cardinality must be 0..1 or 1 if (element.Target.IsDependentType && newTargetMultiplicity == Multiplicity.ZeroMany) { errorMessages.Add($"Can't have a 0..* association from {element.Source.Name} to dependent type {element.Target.Name}"); break; } if ((element.SourceMultiplicity == Multiplicity.One && newTargetMultiplicity == Multiplicity.One) || (element.SourceMultiplicity == Multiplicity.ZeroOne && newTargetMultiplicity == Multiplicity.ZeroOne)) { if (element.SourceRole != EndpointRole.NotSet) { element.SourceRole = EndpointRole.NotSet; } if (element.TargetRole != EndpointRole.NotSet) { element.TargetRole = EndpointRole.NotSet; } } else { SetEndpointRoles(element); } // cascade delete behavior could now be illegal. Reset to default element.SourceDeleteAction = DeleteAction.Default; element.TargetDeleteAction = DeleteAction.Default; break; case "TargetPropertyName": // if we're creating an association via drag/drop, it's possible the existing property name // is the same as the default property name. The default doesn't get created until the transaction is // committed, so the drop's action will cause a name clash. Remove the clashing property, but // only if drag/drop. string targetPropertyNameErrorMessage = ValidateAssociationIdentifier(element, element.Source, (string)e.NewValue); if (EFModelDiagram.IsDropping && targetPropertyNameErrorMessage != null) { element.Delete(); } else { errorMessages.Add(targetPropertyNameErrorMessage); } break; case "TargetRole": if (element.Target.IsDependentType) { element.SourceRole = EndpointRole.Principal; element.TargetRole = EndpointRole.Dependent; } else if (!SetEndpointRoles(element)) { if (element.TargetRole == EndpointRole.Dependent && element.SourceRole != EndpointRole.Principal) { element.SourceRole = EndpointRole.Principal; } else if (element.TargetRole == EndpointRole.Principal && element.SourceRole != EndpointRole.Dependent) { element.SourceRole = EndpointRole.Dependent; } } break; } errorMessages = errorMessages.Where(m => m != null).ToList(); if (errorMessages.Any()) { current.Rollback(); ErrorDisplay.Show(string.Join("\n", errorMessages)); } }
/// <inheritdoc /> public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e) { base.ElementPropertyChanged(e); Association element = (Association)e.ModelElement; if (element.IsDeleted) { return; } Store store = element.Store; ModelRoot modelRoot = store.ModelRoot(); Transaction current = store.TransactionManager.CurrentTransaction; if (current.IsSerializing || ModelRoot.BatchUpdating) { return; } if (Equals(e.NewValue, e.OldValue)) { return; } List <string> errorMessages = EFCoreValidator.GetErrors(element).ToList(); BidirectionalAssociation bidirectionalAssociation = element as BidirectionalAssociation; using (Transaction inner = store.TransactionManager.BeginTransaction("Association ElementPropertyChanged")) { bool doForeignKeyFixup = false; switch (e.DomainProperty.Name) { case "FKPropertyName": { if (store.ModelRoot().EntityFrameworkVersion == EFVersion.EF6 && element.SourceMultiplicity != Multiplicity.ZeroMany && element.TargetMultiplicity != Multiplicity.ZeroMany) { element.FKPropertyName = null; doForeignKeyFixup = true; } else { ValidateForeignKeyNames(element, errorMessages); } if (!errorMessages.Any()) { doForeignKeyFixup = true; } break; } case "SourceCustomAttributes": { if (bidirectionalAssociation != null && !string.IsNullOrWhiteSpace(bidirectionalAssociation.SourceCustomAttributes)) { bidirectionalAssociation.SourceCustomAttributes = $"[{bidirectionalAssociation.SourceCustomAttributes.Trim('[', ']')}]"; CheckSourceForDisplayText(bidirectionalAssociation); } break; } case "SourceDisplayText": { if (bidirectionalAssociation != null) { CheckSourceForDisplayText(bidirectionalAssociation); } break; } case "SourceMultiplicity": { if (!ValidateMultiplicity(element, modelRoot, errorMessages, store, ref doForeignKeyFixup)) { break; } Multiplicity priorSourceMultiplicity = (Multiplicity)e.OldValue; if (((priorSourceMultiplicity == Multiplicity.ZeroOne || priorSourceMultiplicity == Multiplicity.ZeroMany) && element.SourceMultiplicity == Multiplicity.One) || ((element.SourceMultiplicity == Multiplicity.ZeroOne || element.SourceMultiplicity == Multiplicity.ZeroMany) && priorSourceMultiplicity == Multiplicity.One)) { doForeignKeyFixup = true; } string defaultSourcePropertyName = priorSourceMultiplicity == Multiplicity.ZeroMany && ModelRoot.PluralizationService?.IsSingular(element.Source.Name) == true ? ModelRoot.PluralizationService.Pluralize(element.Source.Name) : element.Source.Name; if (element is BidirectionalAssociation bidirectional && bidirectional.SourcePropertyName == defaultSourcePropertyName) { bidirectional.SourcePropertyName = element.SourceMultiplicity == Multiplicity.ZeroMany && ModelRoot.PluralizationService?.IsSingular(element.Source.Name) == true ? ModelRoot.PluralizationService.Pluralize(element.Source.Name) : element.Source.Name; } break; } case "SourcePropertyName": { string sourcePropertyNameErrorMessage = ValidateAssociationIdentifier(element, element.Target, (string)e.NewValue); if (EFModelDiagram.IsDroppingExternal && sourcePropertyNameErrorMessage != null) { element.Delete(); } else { errorMessages.Add(sourcePropertyNameErrorMessage); } break; } case "SourceRole": { if (element.SourceRole == EndpointRole.NotApplicable) { element.SourceRole = EndpointRole.NotSet; } //if (element.Source.IsDependentType) //{ // element.SourceRole = EndpointRole.Dependent; // element.TargetRole = EndpointRole.Principal; //} //else if (!SetEndpointRoles(element)) { if (element.SourceRole == EndpointRole.Dependent && element.TargetRole != EndpointRole.Principal) { element.TargetRole = EndpointRole.Principal; } else if (element.SourceRole == EndpointRole.Principal && element.TargetRole != EndpointRole.Dependent) { element.TargetRole = EndpointRole.Dependent; } } doForeignKeyFixup = true; break; } case "TargetCustomAttributes": { if (!string.IsNullOrWhiteSpace(element.TargetCustomAttributes)) { element.TargetCustomAttributes = $"[{element.TargetCustomAttributes.Trim('[', ']')}]"; CheckTargetForDisplayText(element); } break; } case "TargetDisplayText": { CheckTargetForDisplayText(element); break; } case "TargetMultiplicity": { if (!ValidateMultiplicity(element, modelRoot, errorMessages, store, ref doForeignKeyFixup)) { break; } Multiplicity priorTargetMultiplicity = (Multiplicity)e.OldValue; if (((priorTargetMultiplicity == Multiplicity.ZeroOne || priorTargetMultiplicity == Multiplicity.ZeroMany) && element.TargetMultiplicity == Multiplicity.One) || ((element.TargetMultiplicity == Multiplicity.ZeroOne || element.TargetMultiplicity == Multiplicity.ZeroMany) && priorTargetMultiplicity == Multiplicity.One)) { doForeignKeyFixup = true; } string defaultTargetPropertyName = priorTargetMultiplicity == Multiplicity.ZeroMany && ModelRoot.PluralizationService?.IsSingular(element.Target.Name) == true ? ModelRoot.PluralizationService.Pluralize(element.Target.Name) : element.Target.Name; if (element.TargetPropertyName == defaultTargetPropertyName) { element.TargetPropertyName = element.TargetMultiplicity == Multiplicity.ZeroMany && ModelRoot.PluralizationService?.IsSingular(element.Target.Name) == true ? ModelRoot.PluralizationService.Pluralize(element.Target.Name) : element.Target.Name; } break; } case "TargetPropertyName": { // if we're creating an association via drag/drop, it's possible the existing property name // is the same as the default property name. The default doesn't get created until the transaction is // committed, so the drop's action will cause a name clash. Remove the clashing property, but // only if drag/drop. string targetPropertyNameErrorMessage = ValidateAssociationIdentifier(element, element.Source, (string)e.NewValue); if (EFModelDiagram.IsDroppingExternal && targetPropertyNameErrorMessage != null) { element.Delete(); } else { errorMessages.Add(targetPropertyNameErrorMessage); } break; } case "TargetRole": { if (element.TargetRole == EndpointRole.NotApplicable) { element.TargetRole = EndpointRole.NotSet; } //if (element.Target.IsDependentType && (element.SourceRole != EndpointRole.Principal || element.TargetRole != EndpointRole.Dependent)) //{ // element.SourceRole = EndpointRole.Principal; // element.TargetRole = EndpointRole.Dependent; // doForeignKeyFixup = true; //} //else if (!SetEndpointRoles(element)) { if (element.TargetRole == EndpointRole.Dependent && element.SourceRole != EndpointRole.Principal) { element.SourceRole = EndpointRole.Principal; doForeignKeyFixup = true; } else if (element.TargetRole == EndpointRole.Principal && element.SourceRole != EndpointRole.Dependent) { element.SourceRole = EndpointRole.Dependent; doForeignKeyFixup = true; } } break; } } if (doForeignKeyFixup) { FixupForeignKeys(element); } inner.Commit(); element.RedrawItem(); } errorMessages = errorMessages.Where(m => m != null).ToList(); if (errorMessages.Any()) { current.Rollback(); ErrorDisplay.Show(store, string.Join("\n", errorMessages)); } }