public bool IsInCircularInheritance() { List <ModelClass> classes = new List <ModelClass>(); ModelClass modelClass = Subclass; while (modelClass != null) { if (classes.Contains(modelClass)) { return(true); } classes.Add(modelClass); modelClass = modelClass.Superclass; } return(false); }
private static bool CanAcceptModelClassAsSource(ModelClass candidate) { // valid unidirectional associations: // EF6 - entity to entity, entity to dependent // EFCore - entity to entity, entity to dependent // EFCore5Plus - entity to entity, entity to dependent, dependent to dependent, keyless to entity ModelRoot modelRoot = candidate.ModelRoot; EFVersion entityFrameworkVersion = modelRoot.EntityFrameworkVersion; if (entityFrameworkVersion == EFVersion.EF6) { if (candidate.IsEntity()) { return(true); } } else if (entityFrameworkVersion == EFVersion.EFCore && !modelRoot.IsEFCore5Plus) { if (candidate.IsEntity()) { return(true); } } else if (entityFrameworkVersion == EFVersion.EFCore && modelRoot.IsEFCore5Plus) { if (candidate.IsEntity()) { return(true); } if (candidate.IsDependent()) { return(true); } if (candidate.IsKeyless()) { return(true); } } return(false); }
private void DerivedClassesShouldNotHaveIdentity(ValidationContext context) { if (Attributes.Any(x => x.IsIdentity)) { ModelClass modelClass = Superclass; while (modelClass != null) { if (modelClass.Attributes.Any(x => x.IsIdentity)) { context.LogWarning($"{modelClass.Name}: Identity attribute in derived class {Name} becomes a composite key", "MCWDerivedIdentity", this); hasWarning = true; RedrawItem(); return; } modelClass = modelClass.Superclass; } } }
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { Store store = GetStore(context.Instance); ClassShape classShape = context.Instance as ClassShape; ModelClass modelClass = classShape?.Subject as ModelClass; string targetClassName = modelClass?.Name; List <string> validNames = store.ElementDirectory .FindElements <ModelClass>() .Where(e => e.Name != targetClassName) .OrderBy(c => c.Name) .Select(c => c.Name) .ToList(); validNames.Insert(0, null); return(new StandardValuesCollection(validNames)); }
private void ProcessProperties(ModelClass modelClass, List<ModelProperty> properties) { foreach (ModelProperty data in properties) { ModelAttribute element = modelClass.Attributes.FirstOrDefault(x => x.Name == data.Name); if (element == null) { // we've never seen this one before. Add it. element = new ModelAttribute(Store, new PropertyAssignment(ModelAttribute.TypeDomainPropertyId, data.TypeName), new PropertyAssignment(ModelAttribute.NameDomainPropertyId, data.Name), new PropertyAssignment(ModelAttribute.CustomAttributesDomainPropertyId, data.CustomAttributes), new PropertyAssignment(ModelAttribute.IndexedDomainPropertyId, data.Indexed), new PropertyAssignment(ModelAttribute.RequiredDomainPropertyId, data.Required), new PropertyAssignment(ModelAttribute.MaxLengthDomainPropertyId, data.MaxStringLength), new PropertyAssignment(ModelAttribute.MinLengthDomainPropertyId, data.MinStringLength), new PropertyAssignment(ModelAttribute.IsIdentityDomainPropertyId, data.IsIdentity), new PropertyAssignment(ModelAttribute.IdentityTypeDomainPropertyId, data.IsIdentity ? data.IsIdentityGenerated ? IdentityType.AutoGenerated : IdentityType.Manual : IdentityType.None)); modelClass.Attributes.Add(element); } else { // somehow, we have seen this before. Update it. element.Type = data.TypeName; element.Name = data.Name; element.CustomAttributes = data.CustomAttributes; element.Indexed = data.Indexed; element.Required = data.Required; element.MaxLength = data.MaxStringLength; element.MinLength = data.MinStringLength; element.IsIdentity = data.IsIdentity; element.IdentityType = data.IsIdentity ? data.IsIdentityGenerated ? IdentityType.AutoGenerated : IdentityType.Manual : IdentityType.None; } } }
//private static void SetConnectorWidth(AssociationConnector connector) //{ // if (!(connector?.ModelElement is Association element)) // return; // BidirectionalAssociation bidirectionalElement = connector.ModelElement as BidirectionalAssociation; // PenSettings settings = connector.StyleSet.GetOverriddenPenSettings(DiagramPens.ConnectionLine) ?? new PenSettings(); // bool hasAutoInclude = element.TargetAutoInclude || (bidirectionalElement?.SourceAutoInclude == true); // settings.Width = hasAutoInclude ? 0.04f : 0.01f; // connector.StyleSet.OverridePen(DiagramPens.ConnectionLine, settings); //} /// <summary> /// Redraws the class on every open diagram /// </summary> /// <param name="element"></param> public static void UpdateClassDisplay(ModelClass element) { if (element == null) { return; } // ensure foreign key attributes have the proper setting to surface the right glyph foreach (var data in element.Store.ElementDirectory.AllElements .OfType <Association>() .Where(a => a.Dependent == element && !string.IsNullOrEmpty(a.FKPropertyName)) .SelectMany(association => association.FKPropertyName.Split(',') .Where(propertyName => element.Attributes.Any(attr => attr.Name == propertyName)) .Select(propertyName => new { Assoc = association , Attr = element.Attributes.FirstOrDefault(attr => attr.Name == propertyName) }))) { data.Attr.IsForeignKeyFor = data.Assoc.Id; } // update on every diagram foreach (ClassShape classShape in PresentationViewsSubject .GetPresentation(element) .OfType <ClassShape>()) { classShape.Invalidate(); } // ensure any associations have the correct end for composition ownership foreach (AssociationConnector connector in element.Store.ElementDirectory .AllElements.OfType <Association>() .Where(a => a.Dependent == element) .SelectMany(association => PresentationViewsSubject.GetPresentation(association) .OfType <AssociationConnector>())) { connector.Invalidate(); } }
private void ProcessClasses(ModelRoot modelRoot, List<ParsingModels.ModelClass> classDataList) { foreach (ParsingModels.ModelClass data in classDataList) { StatusDisplay.Show($"Processing {data.FullName}"); ModelClass element = modelRoot.Classes.FirstOrDefault(x => x.FullName == data.FullName); if (element == null) { element = new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, data.Name), new PropertyAssignment(ModelClass.NamespaceDomainPropertyId, data.Namespace), new PropertyAssignment(ModelClass.CustomAttributesDomainPropertyId, data.CustomAttributes), new PropertyAssignment(ModelClass.CustomInterfacesDomainPropertyId, data.CustomInterfaces), new PropertyAssignment(ModelClass.IsAbstractDomainPropertyId, data.IsAbstract), new PropertyAssignment(ModelClass.BaseClassDomainPropertyId, data.BaseClass), new PropertyAssignment(ModelClass.TableNameDomainPropertyId, data.TableName), new PropertyAssignment(ModelClass.IsDependentTypeDomainPropertyId, data.IsDependentType)); modelRoot.Classes.Add(element); } else { element.Name = data.Name; element.Namespace = data.Namespace; element.CustomAttributes = data.CustomAttributes; element.CustomInterfaces = data.CustomInterfaces; element.IsAbstract = data.IsAbstract; element.BaseClass = data.BaseClass; element.TableName = data.TableName; element.IsDependentType = data.IsDependentType; } ProcessProperties(element, data.Properties); ProcessUnidirectionalAssociations(data.UnidirectionalAssociations); ProcessBidirectionalAssociations(data.BidirectionalAssociations); } }
/// <summary>Performs the reset operation for the IsOutputDirectoryTracking property for a model element.</summary> /// <param name="element">The model element that has the property to reset.</param> internal void ResetValue(ModelClass element) { object calculatedValue = null; ModelRoot modelRoot = element.Store.ModelRoot(); try { calculatedValue = element.IsDependentType ? modelRoot?.StructOutputDirectory : element.ModelRoot?.EntityOutputDirectory; } catch (NullReferenceException) { } catch (Exception e) { if (CriticalException.IsCriticalException(e)) { throw; } } if (calculatedValue != null && element.OutputDirectory == (string)calculatedValue) { element.isOutputDirectoryTrackingPropertyStorage = true; } }
/// <summary>Performs the reset operation for the IsDefaultConstructorVisibilityTracking property for a model element.</summary> /// <param name="element">The model element that has the property to reset.</param> internal void ResetValue(ModelClass element) { object calculatedValue = null; ModelRoot modelRoot = element.Store.ModelRoot(); try { calculatedValue = modelRoot?.EntityDefaultConstructorVisibilityDefault; } catch (NullReferenceException) { } catch (Exception e) { if (CriticalException.IsCriticalException(e)) { throw; } } if (calculatedValue != null && element.DefaultConstructorVisibility == (TypeAccessModifierExt)calculatedValue) { element.isDefaultConstructorVisibilityTrackingPropertyStorage = true; } }
private void SetTargetPropertyName(Association element) { if (string.IsNullOrEmpty(element.TargetPropertyName)) { string rootName = element.TargetMultiplicity == Multiplicity.ZeroMany && ModelRoot.PluralizationService?.IsSingular(element.Target.Name) == true ? ModelRoot.PluralizationService.Pluralize(element.Target.Name) : element.Target.Name; string identifierName = rootName; int index = 0; ModelClass modelClass = element.Source; while (modelClass.HasPropertyNamed(identifierName) || modelClass.AllSubclasses.Any(c => c.HasPropertyNamed(identifierName)) || modelClass.AllSuperclasses.Any(c => c.HasPropertyNamed(identifierName))) { identifierName = $"{rootName}_{++index}"; } element.TargetPropertyName = identifierName; } }
/// <summary>Performs the reset operation for the IsDatabaseSchemaTracking property for a model element.</summary> /// <param name="element">The model element that has the property to reset.</param> internal void ResetValue(ModelClass element) { object calculatedValue = null; ModelRoot modelRoot = element.Store.ModelRoot(); try { calculatedValue = modelRoot?.DatabaseSchema; } catch (NullReferenceException) { } catch (Exception e) { if (CriticalException.IsCriticalException(e)) { throw; } } if (calculatedValue != null && element.DatabaseSchema == (string)calculatedValue) { element.isDatabaseSchemaTrackingPropertyStorage = true; } }
public static void UpdateClassDisplay(ModelClass element) { if (element == null) { return; } foreach (ClassShape classShape in PresentationViewsSubject .GetPresentation(element) .OfType <ClassShape>()) { if (element.IsAbstract) { classShape.OutlineColor = Color.OrangeRed; classShape.OutlineThickness = 0.03f; classShape.OutlineDashStyle = DashStyle.Dot; } else if (element.IsDependentType) { classShape.OutlineColor = Color.ForestGreen; classShape.OutlineThickness = 0.03f; classShape.OutlineDashStyle = DashStyle.Dot; } else if (element.ImplementNotify) { classShape.OutlineColor = Color.CornflowerBlue; classShape.OutlineThickness = 0.03f; classShape.OutlineDashStyle = DashStyle.Dot; } else { classShape.OutlineColor = Color.Black; classShape.OutlineThickness = 0.01f; classShape.OutlineDashStyle = DashStyle.Solid; } } }
private void ProcessUnidirectionalAssociations(List<ModelUnidirectionalAssociation> unidirectionalAssociations) { foreach (ModelUnidirectionalAssociation data in unidirectionalAssociations) { if (Store.Get<UnidirectionalAssociation>() .Any(x => x.Target.FullName == data.TargetClassFullName && x.Source.FullName == data.SourceClassFullName && x.TargetPropertyName == data.TargetPropertyName)) continue; ModelClass source = Store.Get<ModelClass>().FirstOrDefault(c => c.FullName == data.SourceClassFullName); if (source == null) continue; ModelClass target = Store.Get<ModelClass>().FirstOrDefault(c => c.FullName == data.TargetClassFullName); if (target == null) continue; // ReSharper disable once UnusedVariable UnidirectionalAssociation element = new UnidirectionalAssociation(Store, new[] { new RoleAssignment(UnidirectionalAssociation.UnidirectionalSourceDomainRoleId, source), new RoleAssignment(UnidirectionalAssociation.UnidirectionalTargetDomainRoleId, target) }, new[] { new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, ConvertMultiplicity(data.SourceMultiplicity)), new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, ConvertMultiplicity(data.TargetMultiplicity)), new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, data.TargetPropertyName), new PropertyAssignment(Association.TargetSummaryDomainPropertyId, data.TargetSummary), new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, data.TargetDescription) }); } }
/// <inheritdoc /> public override void ElementDeleting(ElementDeletingEventArgs e) { base.ElementDeleting(e); Generalization element = (Generalization)e.ModelElement; Store store = element.Store; Transaction current = store.TransactionManager.CurrentTransaction; if (current.IsSerializing || ModelRoot.BatchUpdating) { return; } if (element.Superclass.IsDeleting) { return; } ModelClass subclass = element.Subclass; ModelClass superclass = element.Superclass; List <Association> associations = superclass.AllNavigationProperties() .Select(n => n.AssociationObject) .Distinct() .ToList(); if (!superclass.AllAttributes.Any() && !associations.Any()) { return; } if (!subclass.IsDeleting && BooleanQuestionDisplay.Show(store, $"Push {superclass.Name} attributes and associations down to {subclass.Name}?") == true) { superclass.PushDown(subclass); } }
private void ProcessAssociation([NotNull] ModelClass source, [NotNull] ModelClass target, [NotNull] PropertyDeclarationSyntax propertyDecl, bool toMany = false) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (target == null) { throw new ArgumentNullException(nameof(target)); } if (propertyDecl == null) { throw new ArgumentNullException(nameof(propertyDecl)); } Transaction tx = Store.TransactionManager.CurrentTransaction == null ? Store.TransactionManager.BeginTransaction() : null; try { string propertyName = propertyDecl.Identifier.ToString(); // since we don't have enough information from the code, we'll create unidirectional associations // cardinality 1 on the source end, 0..1 or 0..* on the target, depending on the parameter XMLDocumentation xmlDocumentation = new XMLDocumentation(propertyDecl); // if the association doesn't yet exist, create it if (!Store.ElementDirectory .AllElements .OfType <UnidirectionalAssociation>() .Any(a => a.Source == source && a.Target == target && a.TargetPropertyName == propertyName)) { // if there's a unidirectional going the other direction, we'll whack that one and make a bidirectional // otherwise, proceed as planned UnidirectionalAssociation compliment = Store.ElementDirectory .AllElements .OfType <UnidirectionalAssociation>() .FirstOrDefault(a => a.Source == target && a.Target == source); if (compliment == null) { UnidirectionalAssociation _ = new UnidirectionalAssociation(Store, new[] { new RoleAssignment(UnidirectionalAssociation.UnidirectionalSourceDomainRoleId, source), new RoleAssignment(UnidirectionalAssociation.UnidirectionalTargetDomainRoleId, target) }, new[] { new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, Multiplicity.One), new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, toMany ? Multiplicity.ZeroMany : Multiplicity.ZeroOne), new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, propertyName), new PropertyAssignment(Association.TargetSummaryDomainPropertyId, xmlDocumentation.Summary), new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, xmlDocumentation.Description) }); } else { compliment.Delete(); BidirectionalAssociation _ = new BidirectionalAssociation(Store, new[] { new RoleAssignment(BidirectionalAssociation.BidirectionalSourceDomainRoleId, source), new RoleAssignment(BidirectionalAssociation.BidirectionalTargetDomainRoleId, target) }, new[] { new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, compliment.TargetMultiplicity), new PropertyAssignment(BidirectionalAssociation.SourcePropertyNameDomainPropertyId, compliment.TargetPropertyName), new PropertyAssignment(BidirectionalAssociation.SourceSummaryDomainPropertyId, compliment.TargetSummary), new PropertyAssignment(BidirectionalAssociation.SourceDescriptionDomainPropertyId, compliment.TargetDescription), new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, toMany ? Multiplicity.ZeroMany : Multiplicity.ZeroOne), new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, propertyName), new PropertyAssignment(Association.TargetSummaryDomainPropertyId, xmlDocumentation.Summary), new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, xmlDocumentation.Description) }); } } } catch { tx.Rollback(); tx = null; throw; } finally { tx?.Commit(); } }
private void ProcessProperties([NotNull] ClassDeclarationSyntax classDecl) { if (classDecl == null) { throw new ArgumentNullException(nameof(classDecl)); } Transaction tx = Store.TransactionManager.CurrentTransaction == null ? Store.TransactionManager.BeginTransaction() : null; try { string className = classDecl.Identifier.Text; ModelRoot modelRoot = Store.ModelRoot(); ModelClass modelClass = Store.Get <ModelClass>().FirstOrDefault(c => c.Name == className); modelClass.Attributes.Clear(); foreach (PropertyDeclarationSyntax propertyDecl in classDecl.DescendantNodes().OfType <PropertyDeclarationSyntax>()) { // if the property has a fat arrow expression as its direct descendent, it's a readonly calculated property // TODO: we should handle this // but for this release, ignore it if (propertyDecl.ChildNodes().OfType <ArrowExpressionClauseSyntax>().Any()) { continue; } AccessorDeclarationSyntax getAccessor = (AccessorDeclarationSyntax)propertyDecl.DescendantNodes().FirstOrDefault(node => node.IsKind(SyntaxKind.GetAccessorDeclaration)); AccessorDeclarationSyntax setAccessor = (AccessorDeclarationSyntax)propertyDecl.DescendantNodes().FirstOrDefault(node => node.IsKind(SyntaxKind.SetAccessorDeclaration)); // if there's no getAccessor, why are we bothering? if (getAccessor == null) { continue; } string propertyName = propertyDecl.Identifier.ToString(); string propertyType = propertyDecl.Type.ToString(); ModelClass target = modelRoot.Classes.FirstOrDefault(t => t.Name == propertyType); // is the property type a generic? // assume it's a list // TODO: this really isn't a good assumption. Fix later if (propertyDecl.ChildNodes().OfType <GenericNameSyntax>().Any()) { GenericNameSyntax genericDecl = propertyDecl.ChildNodes().OfType <GenericNameSyntax>().FirstOrDefault(); List <string> contentTypes = genericDecl.DescendantNodes().OfType <IdentifierNameSyntax>().Select(i => i.Identifier.ToString()).ToList(); // there can only be one generic argument if (contentTypes.Count != 1) { WarningDisplay.Show($"Found {className}.{propertyName}, but its type ({genericDecl.Identifier}<{String.Join(", ", contentTypes)}>) isn't anything expected. Ignoring..."); continue; } propertyType = contentTypes[0]; target = modelRoot.Classes.FirstOrDefault(t => t.Name == propertyType); if (target == null) { target = new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, propertyType)); modelRoot.Classes.Add(target); } ProcessAssociation(modelClass, target, propertyDecl, true); continue; } // is the property type an existing ModelClass? if (target != null) { ProcessAssociation(modelClass, target, propertyDecl); continue; } bool propertyShowsNullable = propertyDecl.DescendantNodes().OfType <NullableTypeSyntax>().Any(); // is the property type something we don't know about? if (!modelRoot.IsValidCLRType(propertyType)) { // might be an enum. If so, we'll handle it like a CLR type // if it's nullable, it's definitely an enum, but if we don't know about it, it could be an enum or a class if (!KnownEnums.Contains(propertyType) && !propertyShowsNullable) { // assume it's a class and create the class target = new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, propertyType)); modelRoot.Classes.Add(target); ProcessAssociation(modelClass, target, propertyDecl); continue; } } // if we're here, it's just a property (CLR or enum) try { // ReSharper disable once UseObjectOrCollectionInitializer ModelAttribute modelAttribute = new ModelAttribute(Store, new PropertyAssignment(ModelAttribute.NameDomainPropertyId, propertyName)) { Type = ModelAttribute.ToCLRType(propertyDecl.Type.ToString()).Trim('?'), Required = propertyDecl.HasAttribute("RequiredAttribute") || !propertyShowsNullable, Indexed = propertyDecl.HasAttribute("IndexedAttribute"), IsIdentity = propertyDecl.HasAttribute("KeyAttribute"), Virtual = propertyDecl.DescendantTokens().Any(t => t.IsKind(SyntaxKind.VirtualKeyword)) }; if (modelAttribute.Type.ToLower() == "string") { AttributeSyntax maxLengthAttribute = propertyDecl.GetAttribute("MaxLengthAttribute"); AttributeArgumentSyntax maxLength = maxLengthAttribute?.GetAttributeArguments()?.FirstOrDefault(); if (maxLength != null) { modelAttribute.MaxLength = TryParse(maxLength.Expression.ToString(), out int _max) ? _max : -1; } AttributeSyntax minLengthAttribute = propertyDecl.GetAttribute("MinLengthAttribute"); AttributeArgumentSyntax minLength = minLengthAttribute?.GetAttributeArguments()?.FirstOrDefault(); if (minLength != null) { modelAttribute.MinLength = TryParse(minLength.Expression.ToString(), out int _min) ? _min : 0; } } else { modelAttribute.MaxLength = -1; modelAttribute.MinLength = 0; } // if no setAccessor, it's a calculated readonly property if (setAccessor == null) { modelAttribute.Persistent = false; modelAttribute.ReadOnly = true; } modelAttribute.AutoProperty = !getAccessor.DescendantNodes().Any(node => node.IsKind(SyntaxKind.Block)) && !setAccessor.DescendantNodes().Any(node => node.IsKind(SyntaxKind.Block)); modelAttribute.SetterVisibility = setAccessor.Modifiers.Any(m => m.ToString() == "protected") ? SetterAccessModifier.Protected : setAccessor.Modifiers.Any(m => m.ToString() == "internal") ? SetterAccessModifier.Internal : SetterAccessModifier.Public; XMLDocumentation xmlDocumentation = new XMLDocumentation(propertyDecl); modelAttribute.Summary = xmlDocumentation.Summary; modelAttribute.Description = xmlDocumentation.Description; modelClass.Attributes.Add(modelAttribute); } catch { WarningDisplay.Show($"Could not parse '{className}.{propertyDecl.Identifier}'."); } } } catch { tx = null; throw; } finally { tx?.Commit(); } }
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)); } }
private void ProcessBidirectionalAssociations(ParsingModels.ModelClass modelClass) { List<ModelBidirectionalAssociation> bidirectionalAssociations = modelClass.BidirectionalAssociations; foreach (ModelBidirectionalAssociation data in bidirectionalAssociations) { if (Store.ModelRoot().EntityFrameworkVersion == EFVersion.EF6 && data.SourceMultiplicity != ParsingModels.Multiplicity.ZeroMany && data.TargetMultiplicity != ParsingModels.Multiplicity.ZeroMany) data.ForeignKey = null; BidirectionalAssociation existing = Store.GetAll<BidirectionalAssociation>() .FirstOrDefault(x => x.Target.Name == data.TargetClassName && x.Source.Name == data.SourceClassName && x.Source.Name == modelClass.Name // just to be sure && x.TargetPropertyName == data.TargetPropertyName && x.SourcePropertyName == data.SourcePropertyName) ?? Store.GetAll<BidirectionalAssociation>() .FirstOrDefault(x => x.Source.Name == data.TargetClassName && x.Target.Name == data.SourceClassName && x.Target.Name == modelClass.Name // just to be sure && x.SourcePropertyName == data.TargetPropertyName && x.TargetPropertyName == data.SourcePropertyName); if (existing != null) { if (string.IsNullOrWhiteSpace(existing.FKPropertyName) && !string.IsNullOrWhiteSpace(data.ForeignKey)) { existing.FKPropertyName = data.ForeignKey; existing.Source.ModelRoot.ExposeForeignKeys = true; } continue; } ModelClass source = Store.GetAll<ModelClass>().FirstOrDefault(c => c.Name == data.SourceClassName); ModelClass target = Store.GetAll<ModelClass>().FirstOrDefault(c => c.Name == data.TargetClassName); if (source == null || target == null || source.FullName != modelClass.FullName) continue; // ReSharper disable once UnusedVariable BidirectionalAssociation element = new BidirectionalAssociation(Store, new[] { new RoleAssignment(BidirectionalAssociation.BidirectionalSourceDomainRoleId, source), new RoleAssignment(BidirectionalAssociation.BidirectionalTargetDomainRoleId, target) }, new[] { new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, ConvertMultiplicity(data.SourceMultiplicity)), new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, ConvertMultiplicity(data.TargetMultiplicity)), new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, data.TargetPropertyName), new PropertyAssignment(Association.TargetSummaryDomainPropertyId, data.TargetSummary), new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, data.TargetDescription), new PropertyAssignment(Association.FKPropertyNameDomainPropertyId, data.ForeignKey), new PropertyAssignment(Association.SourceRoleDomainPropertyId, ConvertRole(data.SourceRole)), new PropertyAssignment(Association.TargetRoleDomainPropertyId, ConvertRole(data.TargetRole)), new PropertyAssignment(BidirectionalAssociation.SourcePropertyNameDomainPropertyId, data.SourcePropertyName), new PropertyAssignment(BidirectionalAssociation.SourceSummaryDomainPropertyId, data.SourceSummary), new PropertyAssignment(BidirectionalAssociation.SourceDescriptionDomainPropertyId, data.SourceDescription), }); AssociationChangedRules.SetEndpointRoles(element); } }
private List<ModelElement> ProcessClasses(ModelRoot modelRoot, List<ParsingModels.ModelClass> classDataList) { List<ModelElement> result = new List<ModelElement>(); RemoveDuplicateBidirectionalAssociations(classDataList); Dictionary<string, List<ModelClass>> baseClasses = new Dictionary<string, List<ModelClass>>(); foreach (ParsingModels.ModelClass data in classDataList) { StatusDisplay.Show($"Processing {data.FullName}"); ModelClass element = modelRoot.Classes.FirstOrDefault(x => x.FullName == data.FullName); if (element == null) { element = new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, data.Name), new PropertyAssignment(ModelClass.NamespaceDomainPropertyId, data.Namespace), new PropertyAssignment(ModelClass.CustomAttributesDomainPropertyId, data.CustomAttributes), new PropertyAssignment(ModelClass.CustomInterfacesDomainPropertyId, data.CustomInterfaces), new PropertyAssignment(ModelClass.IsAbstractDomainPropertyId, data.IsAbstract), new PropertyAssignment(ModelClass.TableNameDomainPropertyId, data.TableName), new PropertyAssignment(ModelClass.IsDependentTypeDomainPropertyId, data.IsDependentType)); modelRoot.Classes.Add(element); result.Add(element); } else { element.Name = data.Name; element.Namespace = data.Namespace; element.CustomAttributes = data.CustomAttributes; element.CustomInterfaces = data.CustomInterfaces; element.IsAbstract = data.IsAbstract; element.TableName = data.TableName; element.IsDependentType = data.IsDependentType; } // if base class exists and isn't in the list yet, we can't hook it up to this class // so we'll defer base class linkage for all classes until we're sure they're all in the model if (!string.IsNullOrEmpty(data.BaseClass)) { if (!baseClasses.ContainsKey(data.BaseClass)) baseClasses.Add(data.BaseClass, new List<ModelClass>()); baseClasses[data.BaseClass].Add(element); } ProcessProperties(element, data.Properties); } // now we can fixup the generalization links foreach (string baseClassName in baseClasses.Keys) { foreach (ModelClass subClass in baseClasses[baseClassName]) { ModelClass superClass = modelRoot.Classes.FirstOrDefault(s => s.Name == baseClassName); if (superClass == null) { // we couldn't find the superclass, so we'll assume it's external to this assembly superClass = new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, subClass.BaseClass), new PropertyAssignment(ModelClass.NamespaceDomainPropertyId, subClass.Namespace), new PropertyAssignment(ModelClass.GenerateCodeDomainPropertyId, false)); modelRoot.Classes.Add(superClass); } else subClass.Superclass = superClass; subClass.BaseClass = baseClassName; } } // classes are all created, so we can work the associations foreach (ParsingModels.ModelClass data in classDataList) { ProcessUnidirectionalAssociations(data); ProcessBidirectionalAssociations(data); } return result; }
public bool Process(string filename) { if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException(nameof(filename)); } try { // read the file string fileContents = File.ReadAllText(filename); // parse the contents SyntaxTree tree = CSharpSyntaxTree.ParseText(fileContents); if (tree.GetRoot() is CompilationUnitSyntax root) { List <ClassDeclarationSyntax> classDecls = root.DescendantNodes().OfType <ClassDeclarationSyntax>().Where(classDecl => classDecl.BaseList == null || classDecl.BaseList.Types.FirstOrDefault()?.ToString() != "DbContext").ToList(); List <EnumDeclarationSyntax> enumDecls = root.DescendantNodes().OfType <EnumDeclarationSyntax>().ToList(); if (!classDecls.Any() && !enumDecls.Any()) { WarningDisplay.Show($"Couldn't find any classes or enums to add to the model in {filename}"); return(false); } // keep this order: enums, classes, class properties foreach (EnumDeclarationSyntax enumDecl in enumDecls) { ProcessEnum(enumDecl); } List <ModelClass> processedClasses = new List <ModelClass>(); List <ClassDeclarationSyntax> badClasses = new List <ClassDeclarationSyntax>(); foreach (ClassDeclarationSyntax classDecl in classDecls) { ModelClass modelClass = ProcessClass(classDecl); if (modelClass == null) { badClasses.Add(classDecl); } else { processedClasses.Add(modelClass); } } // process last so all classes and enums are already in the model foreach (ClassDeclarationSyntax classDecl in classDecls.Except(badClasses)) { ProcessProperties(classDecl); } // now that all the properties are in, go through the classes again and ensure identities are present based on convention // ReSharper disable once LoopCanBePartlyConvertedToQuery foreach (ModelClass modelClass in processedClasses.Where(c => !c.AllIdentityAttributes.Any())) { // no identity attribute. Only look in current class for attributes that could be identity by convention List <ModelAttribute> identitiesByConvention = modelClass.Attributes.Where(a => a.Name == "Id" || a.Name == $"{modelClass.Name}Id").ToList(); // if both 'Id' and '[ClassName]Id' are present, don't do anything since we don't know which to make the identity if (identitiesByConvention.Count == 1) { using (Transaction transaction = Store.TransactionManager.BeginTransaction("Add identity")) { identitiesByConvention[0].IsIdentity = true; transaction.Commit(); } } } } } catch { ErrorDisplay.Show("Error interpreting " + filename); return(false); } return(true); }
private ModelClass ProcessClass([NotNull] ClassDeclarationSyntax classDecl, NamespaceDeclarationSyntax namespaceDecl = null) { ModelClass result; if (classDecl == null) { throw new ArgumentNullException(nameof(classDecl)); } ModelRoot modelRoot = Store.ModelRoot(); string className = classDecl.Identifier.Text.Split(':').LastOrDefault(); if (!ValidateInput()) { return(null); } Transaction tx = Store.TransactionManager.CurrentTransaction == null ? Store.TransactionManager.BeginTransaction() : null; List <string> customInterfaces = new List <string>(); try { result = Store.Get <ModelClass>().FirstOrDefault(c => c.Name == className); if (result == null) { result = new ModelClass(Store , new PropertyAssignment(ModelClass.NameDomainPropertyId, className) , new PropertyAssignment(ModelClass.NamespaceDomainPropertyId, namespaceDecl?.Name?.ToString() ?? modelRoot.Namespace) , new PropertyAssignment(ModelClass.IsAbstractDomainPropertyId, classDecl.DescendantNodes().Any(n => n.Kind() == SyntaxKind.AbstractKeyword))); modelRoot.Classes.Add(result); } ModelClass superClass = FindSuperClass(); if (superClass != null) { result.Superclass = superClass; } if (result.CustomInterfaces != null) { customInterfaces.AddRange(result.CustomInterfaces .Split(',') .Where(i => !string.IsNullOrEmpty(i)) .Select(i => i.Trim())); } if (customInterfaces.Contains("INotifyPropertyChanged")) { result.ImplementNotify = true; customInterfaces.Remove("INotifyPropertyChanged"); } if (result.Superclass != null && customInterfaces.Contains(result.Superclass.Name)) { customInterfaces.Remove(result.Superclass.Name); } result.CustomInterfaces = customInterfaces.Any() ? string.Join(",", customInterfaces.Distinct()) : null; AttributeSyntax tableAttribute = classDecl.GetAttribute("Table"); if (tableAttribute != null) { result.TableName = tableAttribute.GetAttributeArguments().First().Expression.ToString().Trim('"'); string schemaName = tableAttribute.GetNamedArgumentValue("Schema"); if (schemaName != null) { result.DatabaseSchema = schemaName; } } XMLDocumentation xmlDocumentation = new XMLDocumentation(classDecl); result.Summary = xmlDocumentation.Summary; result.Description = xmlDocumentation.Description; tx?.Commit(); } catch { tx?.Rollback(); throw; } return(result); ModelClass FindSuperClass() { ModelClass superClass = null; // Base classes and interfaces // Check these first. If we need to add new models, we want the base class already in the store IEnumerable <BaseTypeSyntax> baseTypes = (classDecl.BaseList?.Types ?? Enumerable.Empty <BaseTypeSyntax>()); foreach (string baseName in baseTypes.Select(type => type.ToString().Split(':').Last())) { // Do we know this is an interface? if (KnownInterfaces.Contains(baseName) || superClass != null || result.Superclass != null) { customInterfaces.Add(baseName); if (!KnownInterfaces.Contains(baseName)) { KnownInterfaces.Add(baseName); } continue; } // is it inheritance or an interface? superClass = modelRoot.Classes.FirstOrDefault(c => c.Name == baseName); // if it's not in the model, we just don't know. Ask the user if (superClass == null && (KnownClasses.Contains(baseName) || QuestionDisplay.Show($"For class {className}, is {baseName} the base class?") == true)) { string[] nameparts = baseName.Split('.'); superClass = nameparts.Length == 1 ? new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, nameparts.Last())) : new ModelClass(Store , new PropertyAssignment(ModelClass.NameDomainPropertyId, nameparts.Last()) , new PropertyAssignment(ModelClass.NamespaceDomainPropertyId, string.Join(".", nameparts.Take(nameparts.Length - 1)))); modelRoot.Classes.Add(superClass); } else { customInterfaces.Add(baseName); KnownInterfaces.Add(baseName); } } return(superClass); } bool ValidateInput() { if (className == null) { ErrorDisplay.Show("Can't find class name"); return(false); } if (namespaceDecl == null && classDecl.Parent is NamespaceDeclarationSyntax classDeclParent) { namespaceDecl = classDeclParent; } if (Store.Get <ModelEnum>().Any(c => c.Name == className)) { ErrorDisplay.Show($"'{className}' already exists in model as an Enum."); return(false); } if (classDecl.TypeParameterList != null) { ErrorDisplay.Show($"Can't add generic class '{className}'."); return(false); } return(true); } }
/// <summary> /// Method to set IsNamespaceTracking to false so that this instance of this tracking property is not /// storage-based. /// </summary> /// <param name="element"> /// The element on which to reset the property /// value. /// </param> internal void PreResetValue(ModelClass element) => // Force the IsNamespaceTracking property to false so that the value // of the Namespace property is retrieved from storage. element.isNamespaceTrackingPropertyStorage = false;
protected override void OnSelectionChanged(EventArgs e) { base.OnSelectionChanged(e); // select element in tree if (PrimarySelection != null && PrimarySelection is ModelElement element) { using (Transaction t = element.Store.TransactionManager.BeginTransaction("TreeSelectionChanged")) { Diagram diagram = element.GetActiveDiagramView()?.Diagram; switch (PrimarySelection) { case ModelDiagramData modelDiagramData: // user selected a diagram. Open it. EFModelDocData docData = (EFModelDocData)TreeContainer.ModelingDocData; docData.OpenView(Constants.LogicalView, new Mexedge.VisualStudio.Modeling.ViewContext(modelDiagramData.Name, typeof(EFModelDiagram), docData.RootElement)); break; case ModelClass modelClass: // user selected a class. If it's in the current diagram, find it, center it and make it visible ShapeElement primaryShapeElement = PresentationViewsSubject.GetPresentation(modelClass) .OfType <ShapeElement>() .FirstOrDefault(s => s.Diagram == diagram); //if (primaryShapeElement == null || !primaryShapeElement.IsVisible) // break; modelClass.LocateInDiagram(true); // then fix up the compartments since they might need it ModelElement[] classElements = { modelClass }; CompartmentItemAddRule.UpdateCompartments(classElements, typeof(ClassShape), "AttributesCompartment", false); CompartmentItemAddRule.UpdateCompartments(classElements, typeof(ClassShape), "AssociationsCompartment", false); CompartmentItemAddRule.UpdateCompartments(classElements, typeof(ClassShape), "SourcesCompartment", false); // any associations to visible classes on this diagram need to be visible as well foreach (NavigationProperty navigationProperty in modelClass.LocalNavigationProperties()) { ModelClass other = navigationProperty.AssociationObject.Source == modelClass ? navigationProperty.AssociationObject.Target : navigationProperty.AssociationObject.Source; // should never happen if (other == null) { continue; } ShapeElement shapeElement = PresentationViewsSubject.GetPresentation(other) .OfType <ShapeElement>() .FirstOrDefault(s => s.Diagram == diagram); if (shapeElement != null && shapeElement.IsVisible) { ShapeElement connectorElement = PresentationViewsSubject.GetPresentation(navigationProperty.AssociationObject) .OfType <AssociationConnector>() .FirstOrDefault(s => s.Diagram == diagram); connectorElement?.Show(); } } // so do generalizations, as long as both classes are available foreach (Generalization generalization in modelClass.Store.ElementDirectory.AllElements.OfType <Generalization>().Where(g => g.Superclass == modelClass || g.Subclass == modelClass)) { ModelClass other = generalization.Superclass == modelClass ? generalization.Subclass : generalization.Superclass; // should never happen if (other == null) { continue; } ShapeElement shapeElement = PresentationViewsSubject.GetPresentation(other) .OfType <ShapeElement>() .FirstOrDefault(s => s.Diagram == diagram); if (shapeElement != null && shapeElement.IsVisible) { ShapeElement connectorElement = PresentationViewsSubject.GetPresentation(generalization) .OfType <GeneralizationConnector>() .FirstOrDefault(s => s.Diagram == diagram); connectorElement?.Show(); } } FixUpAllDiagrams.FixUp(diagram, modelClass.ModelRoot, modelClass); break; case ModelEnum modelEnum: // user selected an enum. Find it in the current diagram, center it and make it visible modelEnum.LocateInDiagram(true); // then fix up the compartment since it might need it ModelElement[] enumElements = { modelEnum }; CompartmentItemAddRule.UpdateCompartments(enumElements, typeof(EnumShape), "ValuesCompartment", false); FixUpAllDiagrams.FixUp(diagram, modelEnum.ModelRoot, modelEnum); break; } t.Commit(); } } }
private ModelClass ProcessClass([NotNull] ClassDeclarationSyntax classDecl, NamespaceDeclarationSyntax namespaceDecl = null) { ModelClass result = null; if (classDecl == null) { throw new ArgumentNullException(nameof(classDecl)); } ModelRoot modelRoot = Store.ModelRoot(); string className = classDecl.Identifier.Text; if (namespaceDecl == null && classDecl.Parent is NamespaceDeclarationSyntax classDeclParent) { namespaceDecl = classDeclParent; } if (Store.Get <ModelEnum>().Any(c => c.Name == className)) { ErrorDisplay.Show($"'{className}' already exists in model as an Enum."); // ReSharper disable once ExpressionIsAlwaysNull return(result); } if (classDecl.TypeParameterList != null) { ErrorDisplay.Show($"Can't add generic class '{className}'."); // ReSharper disable once ExpressionIsAlwaysNull return(result); } Transaction tx = Store.TransactionManager.CurrentTransaction == null ? Store.TransactionManager.BeginTransaction() : null; List <string> customInterfaces = new List <string>(); try { ModelClass superClass = null; result = Store.Get <ModelClass>().FirstOrDefault(c => c.Name == className); // Base classes and interfaces // Check these first. If we need to add new models, we want the base class already in the store if (classDecl.BaseList != null) { foreach (BaseTypeSyntax type in classDecl.BaseList.Types) { string baseName = type.ToString(); // Do we know this is an interface? if (KnownInterfaces.Contains(baseName) || superClass != null || result?.Superclass != null) { customInterfaces.Add(baseName); if (!KnownInterfaces.Contains(baseName)) { KnownInterfaces.Add(baseName); } continue; } // is it inheritance or an interface? superClass = modelRoot.Classes.FirstOrDefault(c => c.Name == baseName); // if it's not in the model, we just don't know. Ask the user if (superClass == null && (KnownClasses.Contains(baseName) || QuestionDisplay.Show($"For class {className}, is {baseName} the base class?") == true)) { superClass = new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, baseName)); modelRoot.Classes.Add(superClass); } else { customInterfaces.Add(baseName); KnownInterfaces.Add(baseName); } } } if (result == null) { result = new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, className)) { Namespace = namespaceDecl?.Name?.ToString() ?? modelRoot.Namespace, IsAbstract = classDecl.DescendantNodes().Any(n => n.Kind() == SyntaxKind.AbstractKeyword) }; modelRoot.Classes.Add(result); } if (superClass != null) { result.Superclass = superClass; } if (result.CustomInterfaces != null) { customInterfaces.AddRange(result.CustomInterfaces .Split(',') .Where(i => !String.IsNullOrEmpty(i)) .Select(i => i.Trim())); } if (customInterfaces.Contains("INotifyPropertyChanged")) { result.ImplementNotify = true; customInterfaces.Remove("INotifyPropertyChanged"); } if (result.Superclass != null && customInterfaces.Contains(result.Superclass.Name)) { customInterfaces.Remove(result.Superclass.Name); } result.CustomInterfaces = customInterfaces.Any() ? String.Join(",", customInterfaces.Distinct()) : null; XMLDocumentation xmlDocumentation = new XMLDocumentation(classDecl); result.Summary = xmlDocumentation.Summary; result.Description = xmlDocumentation.Description; } catch { tx = null; throw; } finally { tx?.Commit(); } return(result); }
/// <summary> /// Method to set IsDatabaseSchemaTracking to false so that this instance of this tracking property is not /// storage-based. /// </summary> /// <param name="element"> /// The element on which to reset the property /// value. /// </param> internal void PreResetValue(ModelClass element) => // Force the IsDatabaseSchemaTracking property to false so that the value // of the DatabaseSchema property is retrieved from storage. element.isDatabaseSchemaTrackingPropertyStorage = false;
internal void MoveAttribute(ModelAttribute attribute, ModelClass destination) { MergeDisconnect(attribute); destination.MergeRelate(attribute, null); }
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)); } }
/// <summary> /// Method to set IsDefaultConstructorVisibilityTracking to false so that this instance of this tracking property is not /// storage-based. /// </summary> /// <param name="element"> /// The element on which to reset the property value. /// </param> internal void PreResetValue(ModelClass element) => // Force the IsDefaultConstructorVisibilityTracking property to false so that the value // of the DefaultConstructorVisibility property is retrieved from storage. element.isDefaultConstructorVisibilityTrackingPropertyStorage = false;
public static void RemoveHiddenProperties(PropertyDescriptorCollection propertyDescriptors, ModelClass element) { //ModelRoot modelRoot = element.ModelRoot; //for (int index = 0; index < propertyDescriptors.Count; index++) //{ // bool shouldRemove = false; // switch (propertyDescriptors[index].Name) // { // } // if (shouldRemove) // propertyDescriptors.Remove(propertyDescriptors[index--]); //} }
/// <summary>Performs the reset operation for the IsNamespaceTracking property for a model element.</summary> /// <param name="element">The model element that has the property to reset.</param> internal void ResetValue(ModelClass element) { element.isNamespaceTrackingPropertyStorage = string.IsNullOrWhiteSpace(element.namespaceStorage); }