/// <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));
         }
      }
Пример #2
0
        //internal static bool IsProcessingChange => ProcessingChangeCount > 0;

        ///// <summary>
        ///// Number of times the ElementPropertyChanged method has been recursed into
        ///// </summary>
        //private static int ProcessingChangeCount { get; set; }

        /// <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("Association ElementPropertyChanged"))
            {
                bool doForeignKeyFixup = false;

                switch (e.DomainProperty.Name)
                {
                case "FKPropertyName":
                {
                    if (element.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":
                    Multiplicity currentSourceMultiplicity = (Multiplicity)e.NewValue;
                    Multiplicity priorSourceMultiplicity   = (Multiplicity)e.OldValue;

                    // change unidirectional source cardinality
                    // if target is dependent
                    //    source cardinality is 0..1 or 1
                    if (element.Target.IsDependentType && currentSourceMultiplicity == Multiplicity.ZeroMany)
                    {
                        errorMessages.Add($"Can't have a 0..* association from {element.Target.Name} to dependent type {element.Source.Name}");

                        break;
                    }

                    if ((currentSourceMultiplicity == Multiplicity.One && element.TargetMultiplicity == Multiplicity.One) ||
                        (currentSourceMultiplicity == 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;

                    if (element.Dependent == null)
                    {
                        element.FKPropertyName = null;
                        doForeignKeyFixup      = true;
                    }

                    if (element.Store.ModelRoot().EntityFrameworkVersion == EFVersion.EF6 &&
                        element.SourceMultiplicity != Multiplicity.ZeroMany &&
                        element.TargetMultiplicity != Multiplicity.ZeroMany)
                    {
                        element.FKPropertyName = null;
                        doForeignKeyFixup      = true;
                    }

                    if (((priorSourceMultiplicity == Multiplicity.ZeroOne || priorSourceMultiplicity == Multiplicity.ZeroMany) && currentSourceMultiplicity == Multiplicity.One) ||
                        ((currentSourceMultiplicity == Multiplicity.ZeroOne || currentSourceMultiplicity == Multiplicity.ZeroMany) && priorSourceMultiplicity == Multiplicity.One))
                    {
                        doForeignKeyFixup = true;
                    }

                    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":
                    Multiplicity currentTargetMultiplicity = (Multiplicity)e.NewValue;
                    Multiplicity priorTargetMultiplicity   = (Multiplicity)e.OldValue;

                    // change unidirectional target cardinality
                    // if target is dependent
                    //    target cardinality must be 0..1 or 1
                    if (element.Target.IsDependentType && currentTargetMultiplicity == 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 && currentTargetMultiplicity == Multiplicity.One) ||
                        (element.SourceMultiplicity == Multiplicity.ZeroOne && currentTargetMultiplicity == 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;

                    if (element.Dependent == null)
                    {
                        element.FKPropertyName = null;
                        doForeignKeyFixup      = true;
                    }

                    if (element.Store.ModelRoot().EntityFrameworkVersion == EFVersion.EF6 &&
                        element.SourceMultiplicity != Multiplicity.ZeroMany &&
                        element.TargetMultiplicity != Multiplicity.ZeroMany)
                    {
                        element.FKPropertyName = null;
                        doForeignKeyFixup      = true;
                    }

                    if (((priorTargetMultiplicity == Multiplicity.ZeroOne || priorTargetMultiplicity == Multiplicity.ZeroMany) && currentTargetMultiplicity == Multiplicity.One) ||
                        ((currentTargetMultiplicity == Multiplicity.ZeroOne || currentTargetMultiplicity == Multiplicity.ZeroMany) && priorTargetMultiplicity == Multiplicity.One))
                    {
                        doForeignKeyFixup = true;
                    }

                    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));
            }
        }
Пример #3
0
        /// <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));
            }
        }