public static void AdjustEFCoreProperties(PropertyDescriptorCollection propertyDescriptors, Association element) { //ModelRoot modelRoot = element.Source.ModelRoot; //for (int index = 0; index < propertyDescriptors.Count; index++) //{ // bool shouldRemove = false; // switch (propertyDescriptors[index].Name) // { // default: // break; // } // if (shouldRemove) // propertyDescriptors.Remove(propertyDescriptors[index--]); //} }
internal static bool SetEndpointRoles(Association element) { switch (element.TargetMultiplicity) { case Multiplicity.ZeroMany: switch (element.SourceMultiplicity) { case Multiplicity.ZeroMany: element.SourceRole = EndpointRole.NotApplicable; element.TargetRole = EndpointRole.NotApplicable; return(true); case Multiplicity.One: element.SourceRole = EndpointRole.Principal; element.TargetRole = EndpointRole.Dependent; return(true); case Multiplicity.ZeroOne: element.SourceRole = EndpointRole.Principal; element.TargetRole = EndpointRole.Dependent; return(true); } break; case Multiplicity.One: switch (element.SourceMultiplicity) { case Multiplicity.ZeroMany: element.SourceRole = EndpointRole.Dependent; element.TargetRole = EndpointRole.Principal; return(true); case Multiplicity.One: return(false); case Multiplicity.ZeroOne: element.SourceRole = EndpointRole.Dependent; element.TargetRole = EndpointRole.Principal; return(true); } break; case Multiplicity.ZeroOne: switch (element.SourceMultiplicity) { case Multiplicity.ZeroMany: element.SourceRole = EndpointRole.Dependent; element.TargetRole = EndpointRole.Principal; return(true); case Multiplicity.One: element.SourceRole = EndpointRole.Principal; element.TargetRole = EndpointRole.Dependent; return(true); case Multiplicity.ZeroOne: return(false); } break; } return(false); }
/// <inheritdoc /> 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; using (Transaction inner = store.TransactionManager.BeginTransaction("Redraw Association")) { 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)) .Where(x => x != null) .ToArray(); string summaryBoilerplate = element.GetSummaryBoilerplate(); 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; } } fkPropertyError &= CheckFkAutoIdentityErrors(element, errorMessages); if (!fkPropertyError) { // remove any flags and locks on the attributes that were foreign keys foreach (ModelAttribute modelAttribute in priorForeignKeyModelAttributes) { modelAttribute.ClearFKData(summaryBoilerplate); } element.EnsureForeignKeyAttributes(); IEnumerable <ModelAttribute> currentForeignKeyModelAttributes = foreignKeyPropertyNames.Select(newValue => element.Dependent.Attributes.FirstOrDefault(a => a.Name == newValue)); foreach (ModelAttribute modelAttribute in currentForeignKeyModelAttributes) { modelAttribute.SetFKData(summaryBoilerplate); } } } else { // foreign key was removed // remove locks foreach (ModelAttribute modelAttribute in priorForeignKeyModelAttributes) { modelAttribute.ClearFKData(summaryBoilerplate); } } } 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; } element.RedrawItem(); inner.Commit(); } errorMessages = errorMessages.Where(m => m != null).ToList(); if (errorMessages.Any()) { current.Rollback(); ErrorDisplay.Show(string.Join("\n", errorMessages)); } }
public override void ElementAdded(ElementAddedEventArgs e) { base.ElementAdded(e); Association element = (Association)e.ModelElement; Store store = element.Store; Transaction current = store.TransactionManager.CurrentTransaction; PluralizationService pluralizationService = ModelRoot.PluralizationService; if (current.IsSerializing) { return; } // add unidirectional // source can't be dependent (connection builder handles this) // if target is dependent, // source cardinality is 0..1 // target cardinality is 0..1 // source is principal if (element is UnidirectionalAssociation && element.Target.IsDependentType) { element.TargetMultiplicity = Multiplicity.ZeroOne; element.SourceRole = EndpointRole.Principal; element.TargetRole = EndpointRole.Dependent; } // add bidirectional // neither can be dependent (connection builder handles this) if (string.IsNullOrEmpty(element.TargetPropertyName)) { string rootName = element.TargetMultiplicity == Multiplicity.ZeroMany && pluralizationService?.IsSingular(element.Target.Name) == true ? pluralizationService.Pluralize(element.Target.Name) : element.Target.Name; string identifierName = rootName; int index = 0; while (element.Source.HasPropertyNamed(identifierName)) { identifierName = $"{rootName}_{++index}"; } element.TargetPropertyName = identifierName; } if (element is BidirectionalAssociation bidirectionalAssociation) { if (string.IsNullOrEmpty(bidirectionalAssociation.SourcePropertyName)) { string rootName = element.SourceMultiplicity == Multiplicity.ZeroMany && pluralizationService?.IsSingular(element.Source.Name) == true ? pluralizationService.Pluralize(element.Source.Name) : element.Source.Name; string identifierName = rootName; int index = 0; while (element.Target.HasPropertyNamed(identifierName)) { identifierName = $"{rootName}_{++index}"; } bidirectionalAssociation.SourcePropertyName = identifierName; } } AssociationChangeRules.SetEndpointRoles(element); }
/// <summary> /// Method to set IsTargetImplementNotifyTracking 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(Association element) { // Force the IsTargetImplementNotifyTracking property to false so that the value // of the TargetImplementNotify property is retrieved from storage. element.isTargetImplementNotifyTrackingPropertyStorage = false; }
public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e) { base.ElementPropertyChanged(e); ModelClass element = (ModelClass)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(); switch (e.DomainProperty.Name) { case "DbSetName": { string newDbSetName = (string)e.NewValue; if (element.IsDependentType) { if (!string.IsNullOrEmpty(newDbSetName)) { element.DbSetName = string.Empty; } } else { if (string.IsNullOrEmpty(newDbSetName)) { element.DbSetName = MakeDefaultName(element.Name); } if (current.Name.ToLowerInvariant() != "paste" && (string.IsNullOrWhiteSpace(newDbSetName) || !CodeGenerator.IsValidLanguageIndependentIdentifier(newDbSetName))) { errorMessages.Add($"DbSet name '{newDbSetName}' isn't a valid .NET identifier."); } else if (store.Get <ModelClass>() .Except(new[] { element }) .Any(x => x.DbSetName == newDbSetName)) { errorMessages.Add($"DbSet name '{newDbSetName}' already in use"); } } break; } case "ImplementNotify": { bool newImplementNotify = (bool)e.NewValue; if (newImplementNotify) { List <string> nameList = element.Attributes.Where(x => x.AutoProperty).Select(x => x.Name).ToList(); if (nameList.Any()) { string names = nameList.Count > 1 ? string.Join(", ", nameList.Take(nameList.Count - 1)) + " and " + nameList.Last() : nameList.First(); string verb = nameList.Count > 1 ? "is an autoproperty" : "are autoproperties"; WarningDisplay.Show($"{names} {verb}, so will not participate in INotifyPropertyChanged messages"); } } PresentationHelper.UpdateClassDisplay(element); break; } case "IsAbstract": { bool newIsAbstract = (bool)e.NewValue; if (newIsAbstract && element.IsDependentType) { errorMessages.Add($"Can't make {element.Name} abstract since it's a dependent type"); break; } PresentationHelper.UpdateClassDisplay(element); break; } case "IsDependentType": { bool newIsDependentType = (bool)e.NewValue; if (newIsDependentType) { if (element.IsAbstract) { errorMessages.Add($"Can't make {element.Name} a dependent class since it's abstract"); break; } // dependent type can't be source in an association if (store.Get <UnidirectionalAssociation>() .Any(a => a.Source == element)) { errorMessages.Add($"Can't make {element.Name} a dependent class since it references other classes"); break; } if (store.Get <BidirectionalAssociation>() .Any(a => a.Source == element || a.Target == element)) { errorMessages.Add($"Can't make {element.Name} a dependent class since it's in a bidirectional association"); break; } if (store.Get <Association>() .Any(a => a.Target == element && a.TargetMultiplicity == Multiplicity.ZeroMany)) { errorMessages.Add($"Can't make {element.Name} a dependent class since it's the target of a 0..* association"); break; } foreach (ModelAttribute modelAttribute in element.AllAttributes.Where(a => a.IsIdentity)) { modelAttribute.IsIdentity = false; } foreach (UnidirectionalAssociation association in Association.GetLinksToTargets(element).OfType <UnidirectionalAssociation>()) { if (association.SourceMultiplicity == Multiplicity.ZeroMany) { association.SourceMultiplicity = Multiplicity.ZeroOne; } if (association.TargetMultiplicity == Multiplicity.ZeroMany) { association.TargetMultiplicity = Multiplicity.ZeroOne; } association.TargetRole = EndpointRole.Dependent; } element.TableName = string.Empty; element.DbSetName = string.Empty; } else { element.DbSetName = MakeDefaultName(element.Name); element.TableName = MakeDefaultName(element.Name); } PresentationHelper.UpdateClassDisplay(element); break; } case "Name": { string newName = (string)e.NewValue; if (current.Name.ToLowerInvariant() != "paste" && (string.IsNullOrWhiteSpace(newName) || !CodeGenerator.IsValidLanguageIndependentIdentifier(newName))) { errorMessages.Add($"Class name '{newName}' isn't a valid .NET identifier."); } else if (store.ElementDirectory .AllElements .OfType <ModelClass>() .Except(new[] { element }) .Any(x => x.Name == newName)) { errorMessages.Add($"Class name '{newName}' already in use by another class"); } else if (store.ElementDirectory .AllElements .OfType <ModelEnum>() .Any(x => x.Name == newName)) { errorMessages.Add($"Class name '{newName}' already in use by an enum"); } else if (!string.IsNullOrEmpty((string)e.OldValue)) { string oldDefaultName = MakeDefaultName((string)e.OldValue); string newDefaultName = MakeDefaultName(newName); if (element.DbSetName == oldDefaultName) { element.DbSetName = newDefaultName; } if (element.TableName == oldDefaultName) { element.TableName = newDefaultName; } } break; } case "Namespace": { string newNamespace = (string)e.NewValue; if (current.Name.ToLowerInvariant() != "paste") { errorMessages.Add(CommonRules.ValidateNamespace(newNamespace, CodeGenerator.IsValidLanguageIndependentIdentifier)); } break; } case "TableName": { string newTableName = (string)e.NewValue; if (element.IsDependentType) { if (!string.IsNullOrEmpty(newTableName)) { element.TableName = string.Empty; } } else { if (string.IsNullOrEmpty(newTableName)) { element.TableName = MakeDefaultName(element.Name); } if (store.Get <ModelClass>() .Except(new[] { element }) .Any(x => x.TableName == newTableName)) { errorMessages.Add($"Table name '{newTableName}' already in use"); } } break; } } errorMessages = errorMessages.Where(m => m != null).ToList(); if (errorMessages.Any()) { current.Rollback(); ErrorDisplay.Show(string.Join("\n", errorMessages)); } }
internal static void SetEndpointRoles(Association element) { switch (element.TargetMultiplicity) { case Multiplicity.ZeroMany: switch (element.SourceMultiplicity) { case Multiplicity.ZeroMany: element.SourceRole = EndpointRole.NotApplicable; element.TargetRole = EndpointRole.NotApplicable; break; case Multiplicity.One: element.SourceRole = EndpointRole.Principal; element.TargetRole = EndpointRole.Dependent; break; case Multiplicity.ZeroOne: element.SourceRole = EndpointRole.Principal; element.TargetRole = EndpointRole.Dependent; break; } break; case Multiplicity.One: switch (element.SourceMultiplicity) { case Multiplicity.ZeroMany: element.SourceRole = EndpointRole.Dependent; element.TargetRole = EndpointRole.Principal; break; case Multiplicity.One: break; case Multiplicity.ZeroOne: element.SourceRole = EndpointRole.Dependent; element.TargetRole = EndpointRole.Principal; break; } break; case Multiplicity.ZeroOne: switch (element.SourceMultiplicity) { case Multiplicity.ZeroMany: element.SourceRole = EndpointRole.Dependent; element.TargetRole = EndpointRole.Principal; break; case Multiplicity.One: element.SourceRole = EndpointRole.Principal; element.TargetRole = EndpointRole.Dependent; break; case Multiplicity.ZeroOne: break; } break; } }
public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e) { base.ElementPropertyChanged(e); Association element = (Association)e.ModelElement; Store store = element.Store; Transaction current = store.TransactionManager.CurrentTransaction; ModelRoot modelRoot = store.ElementDirectory.FindElements <ModelRoot>().FirstOrDefault(); if (current.IsSerializing) { return; } List <string> errorMessages = EFCoreValidator.GetErrors(element).ToList(); switch (e.DomainProperty.Name) { case "Persistent": UpdateDisplayForPersistence(element); break; case "TargetPropertyName": errorMessages.Add(ValidateAssociationIdentifier(element, element.Source, element.Target, (string)e.NewValue)); break; case "SourcePropertyName": errorMessages.Add(ValidateAssociationIdentifier(element, element.Target, element.Source, (string)e.NewValue)); break; case "SourceMultiplicity": Multiplicity newSourceMultiplicity = (Multiplicity)e.NewValue; if ((newSourceMultiplicity == Multiplicity.One && element.TargetMultiplicity == Multiplicity.One) || (newSourceMultiplicity == Multiplicity.ZeroOne && element.TargetMultiplicity == Multiplicity.ZeroOne)) { element.SourceRole = EndpointRole.NotSet; element.TargetRole = EndpointRole.NotSet; } else { SetEndpointRoles(element); } UpdateDisplayForCascadeDelete(element, null, null, newSourceMultiplicity); break; case "TargetMultiplicity": Multiplicity newTargetMultiplicity = (Multiplicity)e.NewValue; if ((element.SourceMultiplicity == Multiplicity.One && newTargetMultiplicity == Multiplicity.One) || (element.SourceMultiplicity == Multiplicity.ZeroOne && newTargetMultiplicity == Multiplicity.ZeroOne)) { element.SourceRole = EndpointRole.NotSet; element.TargetRole = EndpointRole.NotSet; } else { SetEndpointRoles(element); } UpdateDisplayForCascadeDelete(element, null, null, null, newTargetMultiplicity); break; case "SourceRole": EndpointRole newSourceRole = (EndpointRole)e.NewValue; if (element.TargetRole == EndpointRole.NotSet && newSourceRole == EndpointRole.Dependent) { element.TargetRole = EndpointRole.Principal; } else if (element.TargetRole == EndpointRole.NotSet && newSourceRole == EndpointRole.Principal) { element.TargetRole = EndpointRole.Dependent; } break; case "TargetRole": EndpointRole newTargetRole = (EndpointRole)e.NewValue; if (element.SourceRole == EndpointRole.NotSet && newTargetRole == EndpointRole.Dependent) { element.SourceRole = EndpointRole.Principal; } else if (element.SourceRole == EndpointRole.NotSet && newTargetRole == EndpointRole.Principal) { element.SourceRole = EndpointRole.Dependent; } break; case "SourceDeleteAction": DeleteAction sourceDeleteAction = (DeleteAction)e.NewValue; UpdateDisplayForCascadeDelete(element, sourceDeleteAction); break; case "TargetDeleteAction": DeleteAction targetDeleteAction = (DeleteAction)e.NewValue; UpdateDisplayForCascadeDelete(element, null, targetDeleteAction); break; } errorMessages = errorMessages.Where(m => m != null).ToList(); if (errorMessages.Any()) { current.Rollback(); MessageBox.Show(string.Join("; ", errorMessages)); } }
public static void AdjustEFCoreProperties(PropertyDescriptorCollection propertyDescriptors, Association element) { ModelRoot modelRoot = element.Source.ModelRoot; for (int index = 0; index < propertyDescriptors.Count; index++) { bool shouldRemove = false; switch (propertyDescriptors[index].Name) { case "TargetPersistencePoint": shouldRemove = element.TargetAutoProperty || modelRoot.EntityFrameworkVersion == EFVersion.EF6; break; case "SourcePersistencePoint": if (element is BidirectionalAssociation bidirectionalAssociation) { shouldRemove = bidirectionalAssociation.SourceAutoProperty || modelRoot.EntityFrameworkVersion == EFVersion.EF6; } break; default: break; } if (shouldRemove) { propertyDescriptors.Remove(propertyDescriptors[index--]); } } }
public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e) { base.ElementPropertyChanged(e); Association element = (Association)e.ModelElement; 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(); switch (e.DomainProperty.Name) { case "Persistent": UpdateDisplayForPersistence(element); break; case "SourceDeleteAction": DeleteAction sourceDeleteAction = (DeleteAction)e.NewValue; UpdateDisplayForCascadeDelete(element, sourceDeleteAction); 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); } UpdateDisplayForCascadeDelete(element, null, null, sourceMultiplicity); 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 { EndpointRole sourceRole = (EndpointRole)e.NewValue; if (sourceRole == EndpointRole.Dependent && element.TargetRole != EndpointRole.Principal) { element.TargetRole = EndpointRole.Principal; } else if (sourceRole == EndpointRole.Principal && element.TargetRole != EndpointRole.Dependent) { element.TargetRole = EndpointRole.Dependent; } SetEndpointRoles(element); } break; case "TargetDeleteAction": DeleteAction targetDeleteAction = (DeleteAction)e.NewValue; UpdateDisplayForCascadeDelete(element, null, targetDeleteAction); 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); } UpdateDisplayForCascadeDelete(element, null, null, null, newTargetMultiplicity); 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 { EndpointRole targetRole = (EndpointRole)e.NewValue; if (targetRole == EndpointRole.Dependent && element.SourceRole != EndpointRole.Principal) { element.SourceRole = EndpointRole.Principal; } else if (targetRole == EndpointRole.Principal && element.SourceRole != EndpointRole.Dependent) { element.SourceRole = EndpointRole.Dependent; } SetEndpointRoles(element); } break; } errorMessages = errorMessages.Where(m => m != null).ToList(); if (errorMessages.Any()) { current.Rollback(); ErrorDisplay.Show(string.Join("\n", errorMessages)); } }