private EntityInfo EntityParse(string entityFilePath, ProjectInfo projectInfo) { string sourceText = File.ReadAllText(entityFilePath); SyntaxTree tree = CSharpSyntaxTree.ParseText(sourceText); CompilationUnitSyntax root = tree.GetCompilationUnitRoot(); string @namespace = root.DescendantNodes().OfType <NamespaceDeclarationSyntax>().Single().Name.ToString();//不满足项目命名空间 ClassDeclarationSyntax classDeclarationSyntax = root.DescendantNodes().OfType <ClassDeclarationSyntax>().Single(); string className = classDeclarationSyntax.Identifier.ToString(); BaseListSyntax baseList = classDeclarationSyntax.BaseList; GenericNameSyntax genericNameSyntax = baseList.DescendantNodes().OfType <SimpleBaseTypeSyntax>() .First(node => !node.ToFullString().StartsWith("I")) // Not interface .DescendantNodes().OfType <GenericNameSyntax>() .FirstOrDefault(); string baseType; string primaryKey; if (genericNameSyntax == null) { // No generic parameter -> Entity with Composite Keys baseType = baseList.DescendantNodes().OfType <SimpleBaseTypeSyntax>().Single().Type.ToString(); primaryKey = "long"; } else { // Normal entity baseType = genericNameSyntax.Identifier.ToString(); primaryKey = genericNameSyntax.DescendantNodes().OfType <TypeArgumentListSyntax>().Single().Arguments[0].ToString(); } List <PropertyInfo> properties = root.DescendantNodes().OfType <PropertyDeclarationSyntax>() .Select(prop => new PropertyInfo(prop.Type.ToString(), prop.Identifier.Value.ToString()) ) .ToList(); string xmlPath = _settingOptions.BaseDirectory + projectInfo.FullName + ".Core.xml"; string entityRemark = Util.GetEntityRemarkBySummary(xmlPath, properties, @namespace + "." + className); if (_settingOptions.Areas != null) { @namespace = projectInfo.FullName + "." + _settingOptions.Areas + "." + className.Pluralize(); } else { @namespace = projectInfo.FullName + "." + className.Pluralize(); } string relativeDirectory = @namespace.RemovePreFix(projectInfo.FullName + ".").Replace('.', '/'); EntityInfo entityInfo = new EntityInfo(@namespace, className, baseType, primaryKey, relativeDirectory); entityInfo.Properties.AddRange(properties); entityInfo.EntityRemark = entityRemark; return(entityInfo); }
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(); } }