Esempio n. 1
0
        /// <summary>
        /// Returns the property descriptors for the described ModelRoot domain class.
        /// </summary>
        private PropertyDescriptorCollection GetCustomProperties(Attribute[] attributes)
        {
            PropertyDescriptorCollection propertyDescriptors = base.GetProperties(attributes);

            if (ModelElement is ModelRoot modelRoot)
            {
                EFCoreValidator.AdjustEFCoreProperties(propertyDescriptors, modelRoot);
                //Add in extra custom properties here...
            }

            // Return the property descriptors for this element
            return(propertyDescriptors);
        }
        /// <summary>
        /// Returns the property descriptors for the described ModelRoot domain class.
        /// </summary>
        private global::System.ComponentModel.PropertyDescriptorCollection GetCustomProperties(global::System.Attribute[] attributes)
        {
            PropertyDescriptorCollection propertyDescriptors = base.GetProperties(attributes);

            if (ModelElement is ModelRoot modelRoot)
            {
                EFCoreValidator.RemoveHiddenProperties(propertyDescriptors, modelRoot);
                //Add in extra custom properties here...
            }

            // Return the property descriptors for this element
            return(propertyDescriptors);
        }
Esempio n. 3
0
        /// <summary>
        ///    Returns the property descriptors for the described ModelClass 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);

            if (ModelElement is ModelClass modelClass)
            {
                EFCoreValidator.AdjustEFCoreProperties(propertyDescriptors, modelClass);

                storeDomainDataDirectory = modelClass.Store.DomainDataDirectory;



                //Add the descriptors for the tracking properties

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelClass
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelClass.DatabaseSchemaDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelClass.IsDatabaseSchemaTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("Database Schema")
                    , new DescriptionAttribute("The schema to use for table creation. Overrides default schema for model if present.")
                    , new CategoryAttribute("Database")
                }));

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelClass
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelClass.NamespaceDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelClass.IsNamespaceTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("Namespace")
                    , new DescriptionAttribute("Overrides default namespace")
                    , new CategoryAttribute("Code Generation")
                }));

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelClass
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelClass.OutputDirectoryDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelClass.IsOutputDirectoryTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("Output Directory")
                    , new DescriptionAttribute("Overrides default output directory")
                    , new CategoryAttribute("Code Generation")
                    , new TypeConverterAttribute(typeof(ProjectDirectoryTypeConverter))
                }));
            }

            // Return the property descriptors for this element
            return(propertyDescriptors);
        }
Esempio n. 4
0
        /// <summary>
        ///    Returns the property descriptors for the described ModelAttribute domain class, adding tracking property
        ///    descriptor(s).
        /// </summary>
        private PropertyDescriptorCollection GetCustomProperties(Attribute[] attributes)
        {
            ModelAttribute modelAttribute = ModelElement as ModelAttribute;

            // Get the default property descriptors from the base class
            PropertyDescriptorCollection propertyDescriptors = base.GetProperties(attributes);

            if (modelAttribute != null)
            {
                storeDomainDataDirectory = modelAttribute.Store.DomainDataDirectory;

                EFCoreValidator.RemoveHiddenProperties(propertyDescriptors, modelAttribute);

                // dono't display IdentityType unless the IsIdentity is true
                if (!modelAttribute.IsIdentity)
                {
                    PropertyDescriptor identityTypeDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().Single(x => x.Name == "IdentityType");
                    propertyDescriptors.Remove(identityTypeDescriptor);
                }

                /********************************************************************************/

                DomainPropertyInfo columnNamePropertyInfo           = storeDomainDataDirectory.GetDomainProperty(ModelAttribute.ColumnNameDomainPropertyId);
                DomainPropertyInfo isColumnNameTrackingPropertyInfo = storeDomainDataDirectory.GetDomainProperty(ModelAttribute.IsColumnNameTrackingDomainPropertyId);

                // Define attributes for the tracking property/properties so that the Properties window displays them correctly.
                Attribute[] columnNameAttributes =
                {
                    new DisplayNameAttribute("Column Name"),
                    new DescriptionAttribute("Overrides default column name"),
                    new CategoryAttribute("Database")
                };

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelAttribute, columnNamePropertyInfo, isColumnNameTrackingPropertyInfo, columnNameAttributes));
            }

            // Return the property descriptors for this element
            return(propertyDescriptors);
        }
        /// <summary>
        ///    Returns the property descriptors for the described ModelEnum 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 ModelEnum modelEnum)
            {
                storeDomainDataDirectory = modelEnum.Store.DomainDataDirectory;

                EFCoreValidator.AdjustEFCoreProperties(propertyDescriptors, modelEnum);

                //Add the descriptors for the tracking properties

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelEnum
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelEnum.NamespaceDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelEnum.IsNamespaceTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("Namespace")
                    , new DescriptionAttribute("Overrides default namespace")
                    , new CategoryAttribute("Code Generation")
                }));

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelEnum
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelEnum.OutputDirectoryDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelEnum.IsOutputDirectoryTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("Output Directory")
                    , new DescriptionAttribute("Overrides default output directory")
                    , new CategoryAttribute("Code Generation")
                    , new TypeConverterAttribute(typeof(ProjectDirectoryTypeConverter))
                }));
            }

            // Return the property descriptors for this element
            return(propertyDescriptors);
        }
        public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
        {
            base.ElementPropertyChanged(e);

            ModelRoot   element = (ModelRoot)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();
            bool          redraw        = false;

            switch (e.DomainProperty.Name)
            {
            case "ConnectionString":

                if (e.NewValue != null)
                {
                    element.ConnectionStringName = null;
                }

                break;

            case "ConnectionStringName":

                if (e.NewValue != null)
                {
                    element.ConnectionString = null;
                }

                break;

            case "DatabaseSchema":

                if (string.IsNullOrEmpty((string)e.NewValue))
                {
                    element.DatabaseSchema = "dbo";
                }

                break;

            case "EntityFrameworkVersion":
                element.EntityFrameworkPackageVersion = "Latest";

                if (element.EntityFrameworkVersion == EFVersion.EFCore)
                {
                    element.InheritanceStrategy = CodeStrategy.TablePerHierarchy;
                }

                break;

            case "EnumOutputDirectory":

                if (string.IsNullOrEmpty((string)e.NewValue) && !string.IsNullOrEmpty(element.EntityOutputDirectory))
                {
                    element.EnumOutputDirectory = element.EntityOutputDirectory;
                }

                break;

            case "StructOutputDirectory":

                if (string.IsNullOrEmpty((string)e.NewValue) && !string.IsNullOrEmpty(element.EntityOutputDirectory))
                {
                    element.StructOutputDirectory = element.EntityOutputDirectory;
                }

                break;

            case "EntityOutputDirectory":

                if (string.IsNullOrEmpty(element.EnumOutputDirectory) || element.EnumOutputDirectory == (string)e.OldValue)
                {
                    element.EnumOutputDirectory = (string)e.NewValue;
                }

                if (string.IsNullOrEmpty(element.StructOutputDirectory) || element.StructOutputDirectory == (string)e.OldValue)
                {
                    element.StructOutputDirectory = (string)e.NewValue;
                }

                break;

            case "FileNameMarker":
                string newFileNameMarker = (string)e.NewValue;

                if (!Regex.Match($"a.{newFileNameMarker}.cs",
                                 @"^(?!^(PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d|\..*)(\..+)?$)[^\x00-\x1f\\?*:\"";|/]+$")
                    .Success)
                {
                    errorMessages.Add("Invalid value to make part of file name");
                }

                break;

            case "InheritanceStrategy":

                if ((element.EntityFrameworkVersion == EFVersion.EFCore) && (element.NuGetPackageVersion.MajorMinorVersionNum < 2.1))
                {
                    element.InheritanceStrategy = CodeStrategy.TablePerHierarchy;
                }

                break;

            case "LayoutAlgorithm":
                ModelDisplay.LayoutDiagram(element.Classes.FirstOrDefault()?.GetActiveDiagram() as EFModelDiagram);

                break;

            case "Namespace":
                errorMessages.Add(CommonRules.ValidateNamespace((string)e.NewValue, CodeGenerator.IsValidLanguageIndependentIdentifier));

                break;

            case "ShowCascadeDeletes":

                // need these change rules to fire even though nothing in Association has changed
                // so we need to set this early -- requires guarding against recursion.
                bool newShowCascadeDeletes = (bool)e.NewValue;

                if (element.ShowCascadeDeletes != newShowCascadeDeletes)
                {
                    element.ShowCascadeDeletes = newShowCascadeDeletes;

                    foreach (Association association in store.ElementDirectory.FindElements <Association>())
                    {
                        AssociationChangeRules.UpdateDisplayForCascadeDelete(association);
                    }
                }

                redraw = true;

                break;

            case "ShowWarningsInDesigner":
                redraw = true;

                break;

            case "WarnOnMissingDocumentation":

                if (element.ShowWarningsInDesigner)
                {
                    redraw = true;
                }

                ModelRoot.ExecuteValidator?.Invoke();

                break;
            }

            errorMessages = errorMessages.Where(m => m != null).ToList();

            if (errorMessages.Any())
            {
                current.Rollback();
                ErrorDisplay.Show(string.Join("\n", errorMessages));
            }

            if (redraw)
            {
                element.InvalidateDiagrams();
            }
        }
Esempio n. 7
0
        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 || ModelRoot.BatchUpdating)
            {
                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.GetAll <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.GetAll <UnidirectionalAssociation>()
                        .Any(a => a.Source == element))
                    {
                        errorMessages.Add($"Can't make {element.Name} a dependent class since it references other classes");

                        break;
                    }

                    if (store.GetAll <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.GetAll <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.GetAll <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(store, string.Join("\n", errorMessages));
            }
        }
        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>
        ///    Returns the property descriptors for the described ModelAttribute 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);

            if (ModelElement is ModelAttribute modelAttribute)
            {
                storeDomainDataDirectory = modelAttribute.Store.DomainDataDirectory;

                EFCoreValidator.RemoveHiddenProperties(propertyDescriptors, modelAttribute);

                // No sense asking for initial values if we won't use them
                if (!modelAttribute.SupportsInitialValue)
                {
                    PropertyDescriptor initialValueTypeDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().Single(x => x.Name == "InitialValue");
                    propertyDescriptors.Remove(initialValueTypeDescriptor);
                }

                // don't display IdentityType unless the IsIdentity is true
                if (!modelAttribute.IsIdentity)
                {
                    PropertyDescriptor identityTypeDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().Single(x => x.Name == "IdentityType");
                    propertyDescriptors.Remove(identityTypeDescriptor);
                }

                // ImplementNotify implicitly defines autoproperty as false, so we don't display it
                if (modelAttribute.ModelClass.ImplementNotify)
                {
                    PropertyDescriptor autoPropertyTypeDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().Single(x => x.Name == "AutoProperty");
                    propertyDescriptors.Remove(autoPropertyTypeDescriptor);
                }

                /********************************************************************************/

                // don't display String property modifiers unless the type is "String"
                if (modelAttribute.Type != "String")
                {
                    PropertyDescriptor minLengthTypeDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().Single(x => x.Name == "MinLength");
                    propertyDescriptors.Remove(minLengthTypeDescriptor);

                    PropertyDescriptor maxLengthTypeDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().Single(x => x.Name == "MaxLength");
                    propertyDescriptors.Remove(maxLengthTypeDescriptor);

                    PropertyDescriptor stringTypeTypeDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().Single(x => x.Name == "StringType");
                    propertyDescriptors.Remove(stringTypeTypeDescriptor);
                }

                /********************************************************************************/

                // don't display IndexedUnique unless the Indexed is true
                if (!modelAttribute.Indexed)
                {
                    PropertyDescriptor indexedUniqueTypeDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().Single(x => x.Name == "IndexedUnique");
                    propertyDescriptors.Remove(indexedUniqueTypeDescriptor);
                }

                /********************************************************************************/

                DomainPropertyInfo columnNamePropertyInfo           = storeDomainDataDirectory.GetDomainProperty(ModelAttribute.ColumnNameDomainPropertyId);
                DomainPropertyInfo isColumnNameTrackingPropertyInfo = storeDomainDataDirectory.GetDomainProperty(ModelAttribute.IsColumnNameTrackingDomainPropertyId);

                // Define attributes for the tracking property/properties so that the Properties window displays them correctly.
                Attribute[] columnNameAttributes =
                {
                    new DisplayNameAttribute("Column Name"),
                    new DescriptionAttribute("Overrides default column name"),
                    new CategoryAttribute("Database")
                };

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelAttribute, columnNamePropertyInfo, isColumnNameTrackingPropertyInfo, columnNameAttributes));
            }

            // Return the property descriptors for this element
            return(propertyDescriptors);
        }
        public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
        {
            base.ElementPropertyChanged(e);

            ModelAttribute element = (ModelAttribute)e.ModelElement;

            if (element.IsDeleted)
            {
                return;
            }

            ModelClass modelClass = element.ModelClass;
            ModelRoot  modelRoot  = element.Store.ModelRoot();

            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();

            switch (e.DomainProperty.Name)
            {
            case "AutoProperty":
            {
                if (element.AutoProperty)
                {
                    element.PersistencePoint = PersistencePointType.Property;
                    element.ImplementNotify  = false;
                }
            }

            break;

            case "IdentityType":
            {
                if (element.IsIdentity)
                {
                    if (element.IdentityType == IdentityType.None)
                    {
                        errorMessages.Add($"{modelClass.Name}.{element.Name}: Identity properties must have an identity type defined");
                    }
                }
                else
                {
                    element.IdentityType = IdentityType.None;
                }
            }

            break;

            case "ImplementNotify":
            {
                if (element.IsIdentity)
                {
                    element.ImplementNotify = false;
                }

                if (element.ImplementNotify)
                {
                    element.AutoProperty = false;
                }
            }

            break;

            case "Indexed":
            {
                if (element.IsIdentity)
                {
                    element.Indexed = true;
                }

                if (element.IsConcurrencyToken)
                {
                    element.Indexed = false;
                }

                if (element.Indexed)
                {
                    element.Persistent = true;
                }
            }

            break;

            case "InitialValue":
            {
                string newInitialValue = (string)e.NewValue;

                // if the property is an Enum and the user just typed the name of the Enum value without the Enum type name, help them out
                if (element.ModelClass.ModelRoot.Enums.Any(x => x.Name == element.Type) && !newInitialValue.Contains("."))
                {
                    newInitialValue = element.InitialValue = $"{element.Type}.{newInitialValue}";
                }

                if (!element.IsValidInitialValue(null, newInitialValue))
                {
                    errorMessages.Add($"{modelClass.Name}.{element.Name}: {newInitialValue} isn't a valid value for {element.Type}");
                }
            }

            break;

            case "IsConcurrencyToken":
            {
                bool newIsConcurrencyToken = (bool)e.NewValue;

                if (newIsConcurrencyToken)
                {
                    element.IsIdentity = false;
                    element.Persistent = true;
                    element.Required   = true;
                    element.Type       = "Binary";
                }
            }

            break;

            case "IsIdentity":
            {
                bool newIsIdentity = (bool)e.NewValue;

                if (newIsIdentity)
                {
                    if (element.ModelClass.IsDependentType)
                    {
                        errorMessages.Add($"{modelClass.Name}.{element.Name}: Can't make {element.Name} an identity because {modelClass.Name} is a dependent type and can't have an identity property.");
                    }
                    else
                    {
                        if (!modelRoot.ValidIdentityAttributeTypes.Contains(element.Type))
                        {
                            errorMessages.Add($"{modelClass.Name}.{element.Name}: Properties of type {element.Type} can't be used as identity properties.");
                        }
                        else
                        {
                            element.IsConcurrencyToken = false;
                            element.Indexed            = true;
                            element.IndexedUnique      = true;
                            element.Persistent         = true;
                            element.Required           = true;

                            if (element.IdentityType == IdentityType.None)
                            {
                                element.IdentityType = IdentityType.AutoGenerated;
                            }
                        }
                    }
                }
                else
                {
                    element.IdentityType = IdentityType.None;
                }
            }

            break;

            case "MinLength":
            {
                int minLengthValue = (int)e.NewValue;

                if (element.Type != "String")
                {
                    element.MinLength = 0;
                }
                else if (minLengthValue < 0)
                {
                    errorMessages.Add($"{modelClass.Name}.{element.Name}: MinLength must be zero or a positive number");
                }
                else if (element.MaxLength > 0 && minLengthValue > element.MaxLength)
                {
                    errorMessages.Add($"{modelClass.Name}.{element.Name}: MinLength cannot be greater than MaxLength");
                }
            }

            break;

            case "MaxLength":
            {
                if (element.Type != "String")
                {
                    element.MaxLength = null;
                }
                else
                {
                    int?maxLengthValue = (int?)e.NewValue;

                    if (maxLengthValue > 0 && element.MinLength > maxLengthValue)
                    {
                        errorMessages.Add($"{modelClass.Name}.{element.Name}: MinLength cannot be greater than MaxLength");
                    }
                }
            }

            break;

            case "Name":
            {
                string newName = (string)e.NewValue;

                if (string.IsNullOrEmpty(newName))
                {
                    errorMessages.Add("Name must be a valid .NET identifier");
                }
                else
                {
                    ParseResult fragment;

                    try
                    {
                        fragment = ModelAttribute.Parse(modelRoot, newName);

                        if (fragment == null)
                        {
                            errorMessages.Add($"{modelClass.Name}: Could not parse entry '{newName}'");
                        }
                        else
                        {
                            if (string.IsNullOrEmpty(fragment.Name) || !CodeGenerator.IsValidLanguageIndependentIdentifier(fragment.Name))
                            {
                                errorMessages.Add($"{modelClass.Name}: Property name '{fragment.Name}' isn't a valid .NET identifier");
                            }
                            else if (modelClass.AllAttributes.Except(new[] { element }).Any(x => x.Name == fragment.Name))
                            {
                                errorMessages.Add($"{modelClass.Name}: Property name '{fragment.Name}' already in use");
                            }
                            else if (modelClass.AllNavigationProperties().Any(p => p.PropertyName == fragment.Name))
                            {
                                errorMessages.Add($"{modelClass.Name}: Property name '{fragment.Name}' already in use");
                            }
                            else
                            {
                                element.Name = fragment.Name;

                                if (fragment.Type != null)
                                {
                                    element.Type = fragment.Type;
                                }

                                if (fragment.Required != null)
                                {
                                    element.Required = fragment.Required.Value;
                                }

                                element.MaxLength = fragment.MaxLength;
                                element.MinLength = fragment.MinLength ?? 0;

                                if (fragment.InitialValue != null)
                                {
                                    element.InitialValue = fragment.InitialValue;
                                }

                                if (fragment.IsIdentity)
                                {
                                    element.IsIdentity = true; // don't reset to false if not entered as part of name
                                }
                            }
                        }
                    }
                    catch (Exception exception)
                    {
                        errorMessages.Add($"{modelClass.Name}: Could not parse entry '{newName}': {exception.Message}");
                    }
                }
            }

            break;

            case "PersistencePoint":
            {
                if ((PersistencePointType)e.NewValue == PersistencePointType.Field)
                {
                    element.AutoProperty = false;
                }
            }

            break;

            case "Persistent":
            {
                bool newPersistent = (bool)e.NewValue;

                if (!newPersistent)
                {
                    element.IsIdentity         = false;
                    element.Indexed            = false;
                    element.IndexedUnique      = false;
                    element.IdentityType       = IdentityType.None;
                    element.IsConcurrencyToken = false;
                    element.Virtual            = false;
                }
            }

            break;

            case "ReadOnly":
            {
                if (!element.Persistent || element.SetterVisibility != SetterAccessModifier.Public)
                {
                    element.ReadOnly = false;
                }
            }

            break;

            case "Required":
            {
                bool newRequired = (bool)e.NewValue;

                if (!newRequired)
                {
                    if (element.IsIdentity || element.IsConcurrencyToken)
                    {
                        element.Required = true;
                    }
                }
            }

            break;

            case "Type":
            {
                string newType = (string)e.NewValue;

                if (element.IsIdentity)
                {
                    if (!modelRoot.ValidIdentityAttributeTypes.Contains(ModelAttribute.ToCLRType(newType)))
                    {
                        errorMessages.Add($"{modelClass.Name}.{element.Name}: Properties of type {newType} can't be used as identity properties.");
                    }
                    else
                    {
                        element.Required   = true;
                        element.Persistent = true;
                    }
                }

                if (newType != "String")
                {
                    element.MaxLength  = null;
                    element.MinLength  = 0;
                    element.StringType = HTML5Type.None;
                }
                else
                {
                    if (!element.IsValidInitialValue(newType))
                    {
                        element.InitialValue = null;
                    }
                }

                if (element.IsConcurrencyToken)
                {
                    element.Type = "Binary";
                }

                if (!element.SupportsInitialValue)
                {
                    element.InitialValue = null;
                }
            }

            break;
            }

            errorMessages = errorMessages.Where(m => m != null).ToList();

            if (errorMessages.Any())
            {
                current.Rollback();
                ErrorDisplay.Show(string.Join("\n", errorMessages));
            }
        }
Esempio n. 11
0
        /// <inheritdoc />
        public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
        {
            base.ElementPropertyChanged(e);

            ModelAttribute element = (ModelAttribute)e.ModelElement;

            if (element.IsDeleted)
            {
                return;
            }

            ModelClass modelClass = element.ModelClass;
            ModelRoot  modelRoot  = element.Store.ModelRoot();

            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();

            switch (e.DomainProperty.Name)
            {
            case "AutoProperty":
            {
                if (element.AutoProperty)
                {
                    element.PersistencePoint = PersistencePointType.Property;
                    element.ImplementNotify  = false;
                }
            }

            break;

            case "IdentityType":
            {
                if (element.IsIdentity)
                {
                    if (element.IdentityType == IdentityType.None)
                    {
                        errorMessages.Add($"{modelClass.Name}.{element.Name}: Identity properties must have an identity type defined");
                    }
                }
                else
                {
                    element.IdentityType = IdentityType.None;
                }

                foreach (Association association in element.ModelClass.LocalNavigationProperties()
                         .Where(nav => nav.AssociationObject.Dependent == element.ModelClass)
                         .Select(nav => nav.AssociationObject)
                         .Where(a => !string.IsNullOrWhiteSpace(a.FKPropertyName) &&
                                a.FKPropertyName.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Any(n => n.Trim() == element.Name)))
                {
                    association.CheckFKAutoIdentityErrors();
                }
            }

            break;

            case "ImplementNotify":
            {
                if (element.IsIdentity)
                {
                    element.ImplementNotify = false;
                }

                if (element.ImplementNotify)
                {
                    element.AutoProperty = false;
                }
            }

            break;

            case "Indexed":
            {
                if (element.IsIdentity)
                {
                    element.Indexed = true;
                }

                if (element.IsConcurrencyToken)
                {
                    element.Indexed = false;
                }

                if (element.Indexed)
                {
                    element.Persistent = true;
                }
            }

            break;

            case "InitialValue":
            {
                string newInitialValue = (string)e.NewValue;

                if (string.IsNullOrEmpty(newInitialValue))
                {
                    break;
                }

                // if the property is an Enum and the user just typed the name of the Enum value without the Enum type name, help them out
                if (element.ModelClass.ModelRoot.Enums.Any(x => x.Name == element.Type) && !newInitialValue.Contains("."))
                {
                    newInitialValue = element.InitialValue = $"{element.Type}.{newInitialValue}";
                }

                if (!element.IsValidInitialValue(null, newInitialValue))
                {
                    errorMessages.Add($"{modelClass.Name}.{element.Name}: {newInitialValue} isn't a valid value for {element.Type}");
                }
            }

            break;

            case "IsAbstract":
            {
                if ((bool)e.NewValue)
                {
                    modelClass.IsAbstract = true;
                }
            }

            break;

            case "IsConcurrencyToken":
            {
                bool newIsConcurrencyToken = (bool)e.NewValue;

                if (newIsConcurrencyToken)
                {
                    element.IsIdentity = false;
                    element.Persistent = true;
                    element.Required   = true;
                    element.Type       = "Binary";
                }
            }

            break;

            case "IsIdentity":
            {
                if ((bool)e.NewValue)
                {
                    if (element.ModelClass.IsDependentType)
                    {
                        errorMessages.Add($"{modelClass.Name}.{element.Name}: Can't make {element.Name} an identity because {modelClass.Name} is a dependent type and can't have an identity property.");
                    }
                    else
                    {
                        if (!modelRoot.ValidIdentityAttributeTypes.Contains(element.Type))
                        {
                            errorMessages.Add($"{modelClass.Name}.{element.Name}: Properties of type {element.Type} can't be used as identity properties.");
                        }
                        else
                        {
                            element.IsConcurrencyToken = false;
                            element.Indexed            = true;
                            element.IndexedUnique      = true;
                            element.Persistent         = true;
                            element.Required           = true;

                            if (element.IdentityType == IdentityType.None)
                            {
                                element.IdentityType = IdentityType.AutoGenerated;
                            }
                        }
                    }

                    foreach (Association association in element.ModelClass.LocalNavigationProperties()
                             .Where(nav => nav.AssociationObject.Dependent == element.ModelClass)
                             .Select(nav => nav.AssociationObject)
                             .Where(a => !string.IsNullOrWhiteSpace(a.FKPropertyName) &&
                                    a.FKPropertyName.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Any(n => n.Trim() == element.Name)))
                    {
                        association.CheckFKAutoIdentityErrors();
                    }
                }
                else
                {
                    element.IdentityType = IdentityType.None;
                }
            }

            break;

            case "MinLength":
            {
                int minLengthValue = (int)e.NewValue;

                if (element.Type != "String")
                {
                    element.MinLength = 0;
                }
                else if (minLengthValue < 0)
                {
                    errorMessages.Add($"{modelClass.Name}.{element.Name}: MinLength must be zero or a positive number");
                }
                else if (element.MaxLength > 0 && minLengthValue > element.MaxLength)
                {
                    errorMessages.Add($"{modelClass.Name}.{element.Name}: MinLength cannot be greater than MaxLength");
                }
            }

            break;

            case "MaxLength":
            {
                if (element.Type != "String")
                {
                    element.MaxLength = null;
                }
                else
                {
                    int?maxLengthValue = (int?)e.NewValue;

                    if (maxLengthValue > 0 && element.MinLength > maxLengthValue)
                    {
                        errorMessages.Add($"{modelClass.Name}.{element.Name}: MinLength cannot be greater than MaxLength");
                    }
                }
            }

            break;

            case "Name":
            {
                if (string.IsNullOrEmpty(element.Name) || !CodeGenerator.IsValidLanguageIndependentIdentifier(element.Name))
                {
                    errorMessages.Add($"{modelClass.Name}: Property name '{element.Name}' isn't a valid .NET identifier");
                }

                if (modelClass.AllAttributes.Except(new[] { element }).Any(x => x.Name == element.Name) ||
                    modelClass.AllNavigationProperties().Any(p => p.PropertyName == element.Name))
                {
                    errorMessages.Add($"{modelClass.Name}: Property name '{element.Name}' already in use");
                }
            }

            break;

            case "PersistencePoint":
            {
                if ((PersistencePointType)e.NewValue == PersistencePointType.Field)
                {
                    element.AutoProperty = false;
                }
            }

            break;

            case "Persistent":
            {
                bool newPersistent = (bool)e.NewValue;

                if (!newPersistent)
                {
                    element.IsIdentity         = false;
                    element.Indexed            = false;
                    element.IndexedUnique      = false;
                    element.IdentityType       = IdentityType.None;
                    element.IsConcurrencyToken = false;
                    element.Virtual            = false;
                }
            }

            break;

            case "ReadOnly":
            {
                if (!element.Persistent || element.SetterVisibility != SetterAccessModifier.Public)
                {
                    element.ReadOnly = false;
                }
            }

            break;

            case "Required":
            {
                bool newRequired = (bool)e.NewValue;

                if (!newRequired)
                {
                    if (element.IsIdentity || element.IsConcurrencyToken)
                    {
                        element.Required = true;
                    }
                }
            }

            break;

            case "Type":
            {
                string newType = (string)e.NewValue;

                if (element.IsIdentity)
                {
                    if (!modelRoot.ValidIdentityAttributeTypes.Contains(ModelAttribute.ToCLRType(newType)))
                    {
                        errorMessages.Add($"{modelClass.Name}.{element.Name}: Properties of type {newType} can't be used as identity properties.");
                    }
                    else
                    {
                        element.Required   = true;
                        element.Persistent = true;
                    }
                }

                if (newType != "String")
                {
                    element.MaxLength  = null;
                    element.MinLength  = 0;
                    element.StringType = HTML5Type.None;
                }
                else
                {
                    if (!element.IsValidInitialValue(newType))
                    {
                        element.InitialValue = null;
                    }

                    //if (!modelClass.Store.InSerializationTransaction && !element.MaxLength.HasValue)
                    //   element.MaxLength = ModelAttribute.GetDefaultStringLength?.Invoke();
                }

                if (element.IsConcurrencyToken)
                {
                    element.Type = "Binary";
                }

                if (!element.SupportsInitialValue)
                {
                    element.InitialValue = null;
                }
            }

            break;
            }

            errorMessages = errorMessages.Where(m => m != null).ToList();

            if (errorMessages.Any())
            {
                current.Rollback();
                ErrorDisplay.Show(store, string.Join("\n", errorMessages));
            }
        }
Esempio n. 12
0
        /// <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)
            {
                storeDomainDataDirectory = association.Store.DomainDataDirectory;

                EFCoreValidator.AdjustEFCoreProperties(propertyDescriptors, association);

                // show FKPropertyName only when possible and required
                if (!association.Source.ModelRoot.ExposeForeignKeys ||
                    (association.SourceRole != EndpointRole.Dependent && association.TargetRole != EndpointRole.Dependent))
                {
                    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
                if (association.SourceRole != EndpointRole.Principal)
                {
                    propertyDescriptors.Remove("SourceDeleteAction");
                }

                if (association.TargetRole != EndpointRole.Principal)
                {
                    propertyDescriptors.Remove("TargetDeleteAction");
                }

                // only show JoinTableName if is *..* association
                if (association.SourceMultiplicity != Multiplicity.ZeroMany || association.TargetMultiplicity != Multiplicity.ZeroMany)
                {
                    propertyDescriptors.Remove("JoinTableName");
                }

                /********************************************************************************/

                //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")
                    }));
                }

                if (association is BidirectionalAssociation bidirectionalAssociation && (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")
                    }));
                }
            }

            // Return the property descriptors for this element
            return(propertyDescriptors);
        }
        /// <summary>
        ///    Returns the property descriptors for the described ModelClass 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)
            {
                storeDomainDataDirectory = association.Store.DomainDataDirectory;

                EFCoreValidator.AdjustEFCoreProperties(propertyDescriptors, association);

                // ImplementNotify implicitly defines autoproperty as false, so we don't display it
                // Similarly, collections are autoproperty == true, so no need to display it then either
                if ((association.Target.ImplementNotify || association.SourceMultiplicity == Multiplicity.ZeroMany) && association is BidirectionalAssociation)
                {
                    PropertyDescriptor sourceAutoPropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "SourceAutoProperty");
                    if (sourceAutoPropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(sourceAutoPropertyDescriptor);
                    }
                }

                if (association.Source.ImplementNotify || association.TargetMultiplicity == Multiplicity.ZeroMany)
                {
                    PropertyDescriptor targetAutoPropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "TargetAutoProperty");
                    if (targetAutoPropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(targetAutoPropertyDescriptor);
                    }
                }

                // 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)))
                {
                    PropertyDescriptor sourceRolePropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "SourceRole");
                    if (sourceRolePropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(sourceRolePropertyDescriptor);
                    }

                    PropertyDescriptor targetRolePropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "TargetRole");
                    if (targetRolePropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(targetRolePropertyDescriptor);
                    }
                }

                // only display delete behavior on the principal end
                if (association.SourceRole != EndpointRole.Principal)
                {
                    PropertyDescriptor sourceDeleteActionPropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "SourceDeleteAction");
                    if (sourceDeleteActionPropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(sourceDeleteActionPropertyDescriptor);
                    }
                }

                if (association.TargetRole != EndpointRole.Principal)
                {
                    PropertyDescriptor targetDeleteActionPropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "TargetDeleteAction");
                    if (targetDeleteActionPropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(targetDeleteActionPropertyDescriptor);
                    }
                }

                /********************************************************************************/

                DomainPropertyInfo collectionClassPropertyInfo           = storeDomainDataDirectory.GetDomainProperty(Association.CollectionClassDomainPropertyId);
                DomainPropertyInfo isCollectionClassTrackingPropertyInfo = storeDomainDataDirectory.GetDomainProperty(Association.IsCollectionClassTrackingDomainPropertyId);

                // Define attributes for the tracking property/properties so that the Properties window displays them correctly.
                Attribute[] collectionClassAttributes =
                {
                    new DisplayNameAttribute("Collection Class"),
                    new DescriptionAttribute("Type of collections generated. Overrides the default collection class for the model"),
                    new CategoryAttribute("Code Generation")
                };

                propertyDescriptors.Add(new TrackingPropertyDescriptor(association, collectionClassPropertyInfo, isCollectionClassTrackingPropertyInfo, collectionClassAttributes));
            }

            // Return the property descriptors for this element
            return(propertyDescriptors);
        }
Esempio n. 14
0
        public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
        {
            base.ElementPropertyChanged(e);

            ModelClass  element = (ModelClass)e.ModelElement;
            Store       store   = element.Store;
            Transaction current = store.TransactionManager.CurrentTransaction;

            if (current.IsSerializing)
            {
                return;
            }

            List <string> errorMessages = EFCoreValidator.GetErrors(element).ToList();

            switch (e.DomainProperty.Name)
            {
            case "IsAbstract":
                bool newIsAbstract = (bool)e.NewValue;

                foreach (ClassShape classShape in PresentationViewsSubject.GetPresentation(element).OfType <ClassShape>())
                {
                    if (newIsAbstract)
                    {
                        classShape.OutlineColor     = Color.OrangeRed;
                        classShape.OutlineThickness = 0.02f;
                        classShape.OutlineDashStyle = element.ImplementNotify ? DashStyle.Dot : DashStyle.Dash;
                    }
                    else if (element.ImplementNotify)
                    {
                        classShape.OutlineColor     = Color.CornflowerBlue;
                        classShape.OutlineThickness = 0.02f;
                        classShape.OutlineDashStyle = DashStyle.Dot;
                    }
                    else
                    {
                        classShape.OutlineColor     = Color.Black;
                        classShape.OutlineThickness = 0.01f;
                        classShape.OutlineDashStyle = DashStyle.Solid;
                    }
                }

                break;

            case "ImplementNotify":
                bool newImplementNotify = (bool)e.NewValue;

                if (!element.IsAbstract) // IsAbstract takes precedence
                {
                    foreach (ClassShape classShape in PresentationViewsSubject.GetPresentation(element).OfType <ClassShape>())
                    {
                        if (newImplementNotify)
                        {
                            classShape.OutlineColor     = Color.CornflowerBlue;
                            classShape.OutlineThickness = 0.02f;
                            classShape.OutlineDashStyle = DashStyle.Dot;
                        }
                        else
                        {
                            classShape.OutlineColor     = Color.Black;
                            classShape.OutlineThickness = 0.01f;
                            classShape.OutlineDashStyle = DashStyle.Solid;
                        }
                    }
                }

                break;

            case "TableName":
                string newTableName = (string)e.NewValue;

                if (string.IsNullOrEmpty(newTableName))
                {
                    element.TableName = MakeDefaultName(element.Name);
                }

                if (store.ElementDirectory
                    .AllElements
                    .OfType <ModelClass>()
                    .Except(new[] { element })
                    .Any(x => x.TableName == newTableName))
                {
                    errorMessages.Add($"Table name '{newTableName}' already in use");
                }
                break;

            case "DbSetName":
                string newDbSetName = (string)e.NewValue;

                if (string.IsNullOrEmpty(newDbSetName))
                {
                    element.DbSetName = MakeDefaultName(element.Name);
                }

                if (current.Name.ToLowerInvariant() != "paste" &&
                    (string.IsNullOrWhiteSpace(newDbSetName) || !CodeGenerator.IsValidLanguageIndependentIdentifier(newDbSetName)))
                {
                    errorMessages.Add("DbSet name must be a valid .NET identifier");
                }

                else if (store.ElementDirectory
                         .AllElements
                         .OfType <ModelClass>()
                         .Except(new[] { element })
                         .Any(x => x.DbSetName == newDbSetName))
                {
                    errorMessages.Add($"DbSet name '{newDbSetName}' already in use");
                }

                break;

            case "Name":
                string newName = (string)e.NewValue;

                if (current.Name.ToLowerInvariant() != "paste" &&
                    (string.IsNullOrWhiteSpace(newName) || !CodeGenerator.IsValidLanguageIndependentIdentifier(newName)))
                {
                    errorMessages.Add("Name must be 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;
            }

            errorMessages = errorMessages.Where(m => m != null).ToList();
            if (errorMessages.Any())
            {
                current.Rollback();
                MessageBox.Show(string.Join("; ", errorMessages));
            }
        }
        /// <summary>
        ///    Returns the property descriptors for the described ModelClass 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);

            if (ModelElement is ModelClass modelClass)
            {
                EFCoreValidator.RemoveHiddenProperties(propertyDescriptors, modelClass);

                storeDomainDataDirectory = modelClass.Store.DomainDataDirectory;

                //Add the descriptors for the tracking properties
                /********************************************************************************/

                DomainPropertyInfo databaseSchemaPropertyInfo           = storeDomainDataDirectory.GetDomainProperty(ModelClass.DatabaseSchemaDomainPropertyId);
                DomainPropertyInfo isDatabaseSchemaTrackingPropertyInfo = storeDomainDataDirectory.GetDomainProperty(ModelClass.IsDatabaseSchemaTrackingDomainPropertyId);

                // Define attributes for the tracking property/properties so that the Properties window displays them correctly.
                Attribute[] databaseSchemaAttributes =
                {
                    new DisplayNameAttribute("Database Schema"),
                    new DescriptionAttribute("The schema to use for table creation. Overrides default schema for model if present."),
                    new CategoryAttribute("Database")
                };

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelClass, databaseSchemaPropertyInfo, isDatabaseSchemaTrackingPropertyInfo, databaseSchemaAttributes));

                /********************************************************************************/

                DomainPropertyInfo namespacePropertyInfo           = storeDomainDataDirectory.GetDomainProperty(ModelClass.NamespaceDomainPropertyId);
                DomainPropertyInfo isNamespaceTrackingPropertyInfo = storeDomainDataDirectory.GetDomainProperty(ModelClass.IsNamespaceTrackingDomainPropertyId);

                // Define attributes for the tracking property/properties so that the Properties window displays them correctly.
                Attribute[] namespaceAttributes =
                {
                    new DisplayNameAttribute("Namespace"),
                    new DescriptionAttribute("Overrides default namespace"),
                    new CategoryAttribute("Code Generation")
                };

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelClass, namespacePropertyInfo, isNamespaceTrackingPropertyInfo, namespaceAttributes));

                /********************************************************************************/

                DomainPropertyInfo outputDirectoryPropertyInfo           = storeDomainDataDirectory.GetDomainProperty(ModelClass.OutputDirectoryDomainPropertyId);
                DomainPropertyInfo isOutputDirectoryTrackingPropertyInfo = storeDomainDataDirectory.GetDomainProperty(ModelClass.IsOutputDirectoryTrackingDomainPropertyId);

                // Define attributes for the tracking property/properties so that the Properties window displays them correctly.
                Attribute[] outputDirectoryAttributes =
                {
                    new DisplayNameAttribute("Output Directory"),
                    new DescriptionAttribute("Overrides default output directory"),
                    new CategoryAttribute("Code Generation"),
                    new TypeConverterAttribute(typeof(ProjectDirectoryTypeConverter))
                };

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelClass, outputDirectoryPropertyInfo, isOutputDirectoryTrackingPropertyInfo, outputDirectoryAttributes));
            }

            // Return the property descriptors for this element
            return(propertyDescriptors);
        }
Esempio n. 16
0
        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));
            }
        }
Esempio n. 17
0
        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 newSourceMultiplicity = (Multiplicity)e.NewValue;

                if (element.Target.IsDependentType && !element.Source.IsDependentType && newSourceMultiplicity == Multiplicity.ZeroMany)
                {
                    errorMessages.Add($"Can't have a 0..* association from {element.Source.Name} to dependent type {element.Target.Name}");

                    break;
                }

                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 "SourcePropertyName":
                string sourcePropertyNameErrorMessage = ValidateAssociationIdentifier(element, element.Target, (string)e.NewValue);
                if (EFModelDiagram.IsDropping && sourcePropertyNameErrorMessage != null)
                {
                    element.Delete();
                }
                else
                {
                    errorMessages.Add(sourcePropertyNameErrorMessage);
                }
                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 "TargetDeleteAction":
                DeleteAction targetDeleteAction = (DeleteAction)e.NewValue;
                UpdateDisplayForCascadeDelete(element, null, targetDeleteAction);
                break;

            case "TargetMultiplicity":
                Multiplicity newTargetMultiplicity = (Multiplicity)e.NewValue;

                if (element.Source.IsDependentType && !element.Target.IsDependentType && newTargetMultiplicity == Multiplicity.ZeroMany)
                {
                    errorMessages.Add($"Can't have a 0..* association from {element.Target.Name} to dependent type {element.Source.Name}");

                    break;
                }

                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 "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":
                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;
            }

            errorMessages = errorMessages.Where(m => m != null).ToList();
            if (errorMessages.Any())
            {
                current.Rollback();
                ErrorDisplay.Show(string.Join("; ", errorMessages));
            }
        }
        /// <summary>
        ///    Returns the property descriptors for the described ModelAttribute 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);

            if (ModelElement is ModelAttribute modelAttribute)
            {
                storeDomainDataDirectory = modelAttribute.Store.DomainDataDirectory;

                EFCoreValidator.AdjustEFCoreProperties(propertyDescriptors, modelAttribute);

                // No sense asking for initial values if we won't use them
                if (!modelAttribute.SupportsInitialValue)
                {
                    PropertyDescriptor initialValuePropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "InitialValue");

                    if (initialValuePropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(initialValuePropertyDescriptor);
                    }
                }

                // don't display IdentityType unless the IsIdentity is true
                if (!modelAttribute.IsIdentity)
                {
                    PropertyDescriptor identityPropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "IdentityType");

                    if (identityPropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(identityPropertyDescriptor);
                    }
                }

                // ImplementNotify implicitly defines autoproperty as false, so we don't display it
                if (modelAttribute.ModelClass.ImplementNotify)
                {
                    PropertyDescriptor autoPropertyPropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "AutoProperty");

                    if (autoPropertyPropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(autoPropertyPropertyDescriptor);
                    }
                }

                // don't need a persistence point type if it's not persistent
                if (!modelAttribute.Persistent)
                {
                    PropertyDescriptor persistencePointPropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "PersistencePoint");

                    if (persistencePointPropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(persistencePointPropertyDescriptor);
                    }
                }

                /********************************************************************************/

                // don't display String property modifiers unless the type is "String"
                if (modelAttribute.Type != "String")
                {
                    PropertyDescriptor minLengthPropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "MinLength");

                    if (minLengthPropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(minLengthPropertyDescriptor);
                    }

                    PropertyDescriptor maxLengthPropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "MaxLength");

                    if (maxLengthPropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(maxLengthPropertyDescriptor);
                    }

                    PropertyDescriptor stringTypePropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "StringType");

                    if (stringTypePropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(stringTypePropertyDescriptor);
                    }
                }

                /********************************************************************************/

                // don't display IndexedUnique unless the Indexed is true
                if (!modelAttribute.Indexed)
                {
                    PropertyDescriptor indexedUniquePropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>().SingleOrDefault(x => x.Name == "IndexedUnique");

                    if (indexedUniquePropertyDescriptor != null)
                    {
                        propertyDescriptors.Remove(indexedUniquePropertyDescriptor);
                    }
                }

                /********************************************************************************/

                //Add the descriptors for the tracking properties

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelAttribute
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.ColumnNameDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.IsColumnNameTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("Column Name")
                    , new DescriptionAttribute("Overrides default column name")
                    , new CategoryAttribute("Database")
                }));

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelAttribute
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.ColumnTypeDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.IsColumnTypeTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("Column Type")
                    , new DescriptionAttribute("Overrides default column type")
                    , new CategoryAttribute("Database")
                }));

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelAttribute
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.AutoPropertyDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.IsAutoPropertyTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("AutoProperty")
                    , new DescriptionAttribute("Overrides default autoproperty setting")
                    , new CategoryAttribute("Code Generation")
                }));

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelAttribute
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.ImplementNotifyDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.IsImplementNotifyTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("Implement INotifyPropertyChanged")
                    , new DescriptionAttribute("Should this attribute implement INotifyPropertyChanged?")
                    , new CategoryAttribute("Code Generation")
                }));
            }

            // Return the property descriptors for this element
            return(propertyDescriptors);
        }
        /// <summary>
        ///    Returns the property descriptors for the described ModelAttribute 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);

            if (ModelElement is ModelAttribute modelAttribute)
            {
                storeDomainDataDirectory = modelAttribute.Store.DomainDataDirectory;

                EFCoreValidator.AdjustEFCoreProperties(propertyDescriptors, modelAttribute);

                // No sense asking for initial values if we won't use them
                if (!modelAttribute.SupportsInitialValue)
                {
                    propertyDescriptors.Remove("InitialValue");
                }

                // don't display IdentityType if IsIdentity is false
                if (!modelAttribute.IsIdentity)
                {
                    propertyDescriptors.Remove("IdentityType");
                }

                // if IsIdentity is true, since we don't encourage changing identity properties (!), don't show the implement notify switch
                if (modelAttribute.IsIdentity)
                {
                    propertyDescriptors.Remove("ImplementNotify");
                }

                // don't display SetterVisibility if IsIdentity is true and automatically generated
                if (modelAttribute.IsIdentity && modelAttribute.IdentityType == IdentityType.AutoGenerated)
                {
                    propertyDescriptors.Remove("SetterVisibility");
                }

                // ImplementNotify implicitly defines autoproperty as false, so we don't display it
                if (modelAttribute.ImplementNotify)
                {
                    propertyDescriptors.Remove("AutoProperty");
                }

                // this is about to be removed
                propertyDescriptors.Remove("PersistencePoint");

                // don't display String property modifiers unless the type is "String"
                if (modelAttribute.Type != "String")
                {
                    propertyDescriptors.Remove("MinLength");
                    propertyDescriptors.Remove("MaxLength");
                    propertyDescriptors.Remove("StringType");
                }

                // don't display IndexedUnique unless the Indexed is true
                if (!modelAttribute.Indexed)
                {
                    propertyDescriptors.Remove("IndexedUnique");
                }

                // don't display BackingField unless AutoProperty is false
                if (modelAttribute.AutoProperty)
                {
                    propertyDescriptors.Remove("BackingFieldName");
                }

                /********************************************************************************/

                //Add the descriptors for the tracking properties

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelAttribute
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.ColumnNameDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.IsColumnNameTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("Column Name")
                    , new DescriptionAttribute("Overrides default column name")
                    , new CategoryAttribute("Database")
                }));

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelAttribute
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.ColumnTypeDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.IsColumnTypeTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("Column Type")
                    , new DescriptionAttribute("Overrides default column type")
                    , new CategoryAttribute("Database")
                }));

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelAttribute
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.AutoPropertyDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.IsAutoPropertyTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("AutoProperty")
                    , new DescriptionAttribute("Overrides default autoproperty setting")
                    , new CategoryAttribute("Code Generation")
                }));

                propertyDescriptors.Add(new TrackingPropertyDescriptor(modelAttribute
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.ImplementNotifyDomainPropertyId)
                                                                       , storeDomainDataDirectory.GetDomainProperty(ModelAttribute.IsImplementNotifyTrackingDomainPropertyId)
                                                                       , new Attribute[]
                {
                    new DisplayNameAttribute("Implement INotifyPropertyChanged")
                    , new DescriptionAttribute("Should this attribute implement INotifyPropertyChanged?")
                    , new CategoryAttribute("Code Generation")
                }));
            }

            // Return the property descriptors for this element
            return(propertyDescriptors);
        }
        public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
        {
            base.ElementPropertyChanged(e);

            ModelClass  element = (ModelClass)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 "IsDependentType":
                bool newIsStruct = (bool)e.NewValue;

                if (newIsStruct)
                {
                    List <Association> associations = store.ElementDirectory
                                                      .AllElements
                                                      .OfType <Association>()
                                                      .Where(a => (a.Source == element && a.SourceMultiplicity == Multiplicity.ZeroMany) ||
                                                             (a.Target == element && a.TargetMultiplicity == Multiplicity.ZeroMany))
                                                      .ToList();

                    if (associations.Any())
                    {
                        List <string> classNameList = associations.Select(a => a.Target.Name).ToList();
                        if (classNameList.Count > 1)
                        {
                            classNameList[classNameList.Count - 1] = "and " + classNameList[classNameList.Count - 1];
                        }
                        string classNames = string.Join(", ", classNameList);

                        errorMessages.Add($"Can't have a 0..* association to a dependent type. Found 0..* link(s) with {classNames}");

                        break;
                    }

                    foreach (ModelAttribute modelAttribute in element.AllAttributes.Where(a => a.IsIdentity))
                    {
                        modelAttribute.IsIdentity = false;
                    }
                }

                break;

            case "IsAbstract":
                bool newIsAbstract = (bool)e.NewValue;

                foreach (ClassShape classShape in PresentationViewsSubject.GetPresentation(element).OfType <ClassShape>())
                {
                    if (newIsAbstract)
                    {
                        classShape.OutlineColor     = Color.OrangeRed;
                        classShape.OutlineThickness = 0.02f;
                        classShape.OutlineDashStyle = element.ImplementNotify ? DashStyle.Dot : DashStyle.Dash;
                    }
                    else if (element.ImplementNotify)
                    {
                        classShape.OutlineColor     = Color.CornflowerBlue;
                        classShape.OutlineThickness = 0.02f;
                        classShape.OutlineDashStyle = DashStyle.Dot;
                    }
                    else
                    {
                        classShape.OutlineColor     = Color.Black;
                        classShape.OutlineThickness = 0.01f;
                        classShape.OutlineDashStyle = DashStyle.Solid;
                    }
                }

                break;

            case "ImplementNotify":
                bool newImplementNotify = (bool)e.NewValue;

                if (!element.IsAbstract) // IsAbstract takes precedence
                {
                    foreach (ClassShape classShape in PresentationViewsSubject.GetPresentation(element).OfType <ClassShape>())
                    {
                        if (newImplementNotify)
                        {
                            classShape.OutlineColor     = Color.CornflowerBlue;
                            classShape.OutlineThickness = 0.02f;
                            classShape.OutlineDashStyle = DashStyle.Dot;
                        }
                        else
                        {
                            classShape.OutlineColor     = Color.Black;
                            classShape.OutlineThickness = 0.01f;
                            classShape.OutlineDashStyle = DashStyle.Solid;
                        }
                    }
                }

                if (element.ImplementNotify)
                {
                    foreach (ModelAttribute modelAttribute in element.Attributes.Where(x => x.AutoProperty))
                    {
                        WarningDisplay.Show($"{modelAttribute.Name} is an autoproperty, so will not participate in INotifyPropertyChanged messages");
                    }
                }

                break;

            case "TableName":
                string newTableName = (string)e.NewValue;

                if (string.IsNullOrEmpty(newTableName))
                {
                    element.TableName = MakeDefaultName(element.Name);
                }

                if (store.ElementDirectory
                    .AllElements
                    .OfType <ModelClass>()
                    .Except(new[] { element })
                    .Any(x => x.TableName == newTableName))
                {
                    errorMessages.Add($"Table name '{newTableName}' already in use");
                }
                break;

            case "DbSetName":
                string newDbSetName = (string)e.NewValue;

                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.ElementDirectory
                         .AllElements
                         .OfType <ModelClass>()
                         .Except(new[] { element })
                         .Any(x => x.DbSetName == newDbSetName))
                {
                    errorMessages.Add($"DbSet name '{newDbSetName}' already in use");
                }

                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;
            }

            errorMessages = errorMessages.Where(m => m != null).ToList();
            if (errorMessages.Any())
            {
                current.Rollback();
                ErrorDisplay.Show(string.Join("; ", errorMessages));
            }
        }
Esempio n. 21
0
        public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
        {
            base.ElementPropertyChanged(e);

            ModelRoot   element = (ModelRoot)e.ModelElement;
            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();
            bool          redraw        = false;

            switch (e.DomainProperty.Name)
            {
            case "ConnectionString":

                if (e.NewValue != null)
                {
                    element.ConnectionStringName = null;
                }

                break;

            case "ConnectionStringName":

                if (e.NewValue != null)
                {
                    element.ConnectionString = null;
                }

                break;

            case "EntityFrameworkVersion":
                element.EntityFrameworkPackageVersion = "Latest";

                if (element.EntityFrameworkVersion == EFVersion.EFCore)
                {
                    element.InheritanceStrategy = CodeStrategy.TablePerHierarchy;
                }

                if (element.EntityFrameworkVersion == EFVersion.EF6)
                {
                    List <Association> associations = store.ElementDirectory
                                                      .AllElements
                                                      .OfType <Association>()
                                                      .Where(a => !string.IsNullOrEmpty(a.FKPropertyName) && a.SourceMultiplicity != Multiplicity.ZeroMany && a.TargetMultiplicity != Multiplicity.ZeroMany)
                                                      .ToList();

                    string message = $"This will remove declared foreign key properties from {associations.Count} association{(associations.Count == 1 ? "" : "s")}. Are you sure?";

                    if (associations.Any() && BooleanQuestionDisplay.Show(store, message) == true)
                    {
                        foreach (Association association in associations)
                        {
                            association.FKPropertyName = null;
                            AssociationChangedRules.FixupForeignKeys(association);
                        }
                    }
                }

                ModelRoot.ExecuteValidator?.Invoke();

                break;

            case "EntityOutputDirectory":

                if (string.IsNullOrEmpty(element.EnumOutputDirectory) || element.EnumOutputDirectory == (string)e.OldValue)
                {
                    element.EnumOutputDirectory = (string)e.NewValue;
                }

                if (string.IsNullOrEmpty(element.StructOutputDirectory) || element.StructOutputDirectory == (string)e.OldValue)
                {
                    element.StructOutputDirectory = (string)e.NewValue;
                }

                break;

            case "EnumOutputDirectory":

                if (string.IsNullOrEmpty((string)e.NewValue) && !string.IsNullOrEmpty(element.EntityOutputDirectory))
                {
                    element.EnumOutputDirectory = element.EntityOutputDirectory;
                }

                break;

            case "ExposeForeignKeys":
                if (!element.ExposeForeignKeys)
                {
                    foreach (Association association in element.Store.GetAll <Association>()
                             .Where(a => (a.SourceRole == EndpointRole.Dependent || a.TargetRole == EndpointRole.Dependent) &&
                                    !string.IsNullOrWhiteSpace(a.FKPropertyName)))
                    {
                        association.FKPropertyName = null;
                        AssociationChangedRules.FixupForeignKeys(association);
                    }
                }

                break;

            case "FileNameMarker":
                string newFileNameMarker = (string)e.NewValue;

                if (!Regex.Match($"a.{newFileNameMarker}.cs",
                                 @"^(?!^(PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d|\..*)(\..+)?$)[^\x00-\x1f\\?*:\"";|/]+$")
                    .Success)
                {
                    errorMessages.Add("Invalid value to make part of file name");
                }

                break;

            case "GridColor":
                foreach (EFModelDiagram diagram in element.GetDiagrams())
                {
                    diagram.GridColor = (Color)e.NewValue;
                }

                redraw = true;

                break;

            case "InheritanceStrategy":

                if ((element.EntityFrameworkVersion == EFVersion.EFCore) && (element.NuGetPackageVersion.MajorMinorVersionNum < 2.1))
                {
                    element.InheritanceStrategy = CodeStrategy.TablePerHierarchy;
                }

                break;

            case "Namespace":
                errorMessages.Add(CommonRules.ValidateNamespace((string)e.NewValue, CodeGenerator.IsValidLanguageIndependentIdentifier));

                break;

            case "ShowCascadeDeletes":
                // Normally you'd think that we should be able to register this in a AssociateValueWith call
                // in AssociationConnector, but that doesn't appear to work. So call the update method here.
                foreach (Association association in store.ElementDirectory.FindElements <Association>())
                {
                    PresentationHelper.UpdateAssociationDisplay(association);
                }

                redraw = true;

                break;

            case "ShowGrid":
                foreach (EFModelDiagram diagram in element.GetDiagrams())
                {
                    diagram.ShowGrid = (bool)e.NewValue;
                }

                redraw = true;

                break;

            case "ShowWarningsInDesigner":
                redraw = true;

                if ((bool)e.NewValue)
                {
                    ModelRoot.ExecuteValidator?.Invoke();
                }

                break;

            case "SnapToGrid":
                foreach (EFModelDiagram diagram in element.GetDiagrams())
                {
                    diagram.SnapToGrid = (bool)e.NewValue;
                }

                redraw = true;

                break;

            case "StructOutputDirectory":

                if (string.IsNullOrEmpty((string)e.NewValue) && !string.IsNullOrEmpty(element.EntityOutputDirectory))
                {
                    element.StructOutputDirectory = element.EntityOutputDirectory;
                }

                break;

            case "WarnOnMissingDocumentation":

                if (element.ShowWarningsInDesigner)
                {
                    redraw = true;
                }

                ModelRoot.ExecuteValidator?.Invoke();

                break;
            }

            errorMessages = errorMessages.Where(m => m != null).ToList();

            if (errorMessages.Any())
            {
                current.Rollback();
                ErrorDisplay.Show(store, string.Join("\n", errorMessages));
            }

            if (redraw)
            {
                foreach (EFModelDiagram diagram in element.GetDiagrams().Where(d => d.ActiveDiagramView != null))
                {
                    diagram.Invalidate(true);
                }
            }
        }
Esempio n. 22
0
        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));
            }
        }
Esempio n. 23
0
        /// <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));
            }
        }