public SourceText GetOutputTextForStoredProcedure(Definition.Schema schema, Definition.StoredProcedure storedProcedure) { var rootDir = _output.GetOutputRootDir(); var fileContent = File.ReadAllText(Path.Combine(rootDir.FullName, "DataContext", "Outputs", "Output.cs")); var tree = CSharpSyntaxTree.ParseText(fileContent); var root = tree.GetCompilationUnitRoot(); // Replace Namespace if (_configFile.Config.Project.Role.Kind == ERoleKind.Lib) { root = root.ReplaceNamespace(ns => ns.Replace("Source.DataContext", _configFile.Config.Project.Output.Namespace).Replace("Schema", schema.Name)); } else { root = root.ReplaceNamespace(ns => ns.Replace("Source", _configFile.Config.Project.Output.Namespace).Replace("Schema", schema.Name)); } // Replace ClassName root = root.ReplaceClassName(ci => ci.Replace("Output", storedProcedure.GetOutputTypeName())); // Generate Properies // https://stackoverflow.com/questions/45160694/adding-new-field-declaration-to-class-with-roslyn var nsNode = (NamespaceDeclarationSyntax)root.Members[0]; var classNode = (ClassDeclarationSyntax)nsNode.Members[0]; var propertyNode = (PropertyDeclarationSyntax)classNode.Members[0]; var outputs = storedProcedure.Input?.Where(i => i.IsOutput).ToList() ?? new List <StoredProcedureInputModel>(); foreach (var output in outputs) { nsNode = (NamespaceDeclarationSyntax)root.Members[0]; classNode = (ClassDeclarationSyntax)nsNode.Members[0]; var propertyIdentifier = TokenHelper.Parse(output.Name); propertyNode = propertyNode .WithType(ParseTypeFromSqlDbTypeName(output.SqlTypeName, output.IsNullable ?? false)); propertyNode = propertyNode .WithIdentifier(propertyIdentifier); root = root.AddProperty(ref classNode, propertyNode); } // Remove template Property nsNode = (NamespaceDeclarationSyntax)root.Members[0]; classNode = (ClassDeclarationSyntax)nsNode.Members[0]; root = root.ReplaceNode(classNode, classNode.WithMembers(new SyntaxList <MemberDeclarationSyntax>(classNode.Members.Cast <PropertyDeclarationSyntax>().Skip(1)))); return(root.NormalizeWhitespace().GetText()); }
private MethodDeclarationSyntax GenerateStoredProcedureMethodText(MethodDeclarationSyntax methodNode, Definition.StoredProcedure storedProcedure, bool isOverload = false) { // Replace MethodName var methodName = $"{storedProcedure.Name}Async"; var methodIdentifier = SyntaxFactory.ParseToken(methodName); methodNode = methodNode.WithIdentifier(methodIdentifier); var parameters = new[] { SyntaxFactory.Parameter(SyntaxFactory.Identifier("input")) .WithType(SyntaxFactory.ParseTypeName($"{storedProcedure.Name}Input")) }; var parameterList = methodNode.ParameterList; parameterList = parameterList.WithParameters( parameterList.Parameters.InsertRange(2, parameters).RemoveAt(1) ); methodNode = methodNode.WithParameterList(parameterList); // Get Method Body as Statements var methodBody = methodNode.Body; var statements = methodBody.Statements.ToList(); var returnExpression = (statements.Last() as ReturnStatementSyntax).Expression.GetText().ToString(); if (isOverload) { returnExpression = returnExpression.Replace("CrudActionAsync", methodName); } else { // Generate Sql-Parameters var sqlParamSyntax = (LocalDeclarationStatementSyntax)statements.Single(i => i is LocalDeclarationStatementSyntax); var sqlParamSyntaxIndex = statements.IndexOf(sqlParamSyntax); var arguments = new List <SyntaxNodeOrToken>(); var inputs = storedProcedure.Input.ToList(); var lastInput = inputs.Last(); inputs.ForEach(i => { var isLastItem = i == lastInput; var args = new List <SyntaxNodeOrToken>(); args.Add(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(i.Name.Remove(0, 1))))); args.Add(SyntaxFactory.Token(SyntaxKind.CommaToken)); args.Add(SyntaxFactory.Argument(SyntaxFactory.IdentifierName($"input.{GetPropertyFromSqlInputTableType(i.Name)}"))); if (i.IsOutput) { args.Add(SyntaxFactory.Token(SyntaxKind.CommaToken)); args.Add(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression))); } arguments.Add(SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName("AppDbContext"), SyntaxFactory.IdentifierName((i.IsTableType ?? false) ? "GetCollectionParameter" : "GetParameter"))) .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList <ArgumentSyntax>(args)))); if (!isLastItem) { arguments.Add(SyntaxFactory.Token(SyntaxKind.CommaToken)); } }); statements[sqlParamSyntaxIndex] = SyntaxFactory.LocalDeclarationStatement(SyntaxFactory.VariableDeclaration(SyntaxFactory.IdentifierName("var")) .WithVariables(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("parameters")) .WithInitializer(SyntaxFactory.EqualsValueClause( SyntaxFactory.ObjectCreationExpression( SyntaxFactory.GenericName( SyntaxFactory.Identifier("List")) .WithTypeArgumentList(SyntaxFactory.TypeArgumentList(SyntaxFactory.SingletonSeparatedList <TypeSyntax>(SyntaxFactory.IdentifierName("SqlParameter"))))) .WithInitializer(SyntaxFactory.InitializerExpression(SyntaxKind.CollectionInitializerExpression, SyntaxFactory.SeparatedList <ExpressionSyntax>(arguments)))))))); methodBody = methodBody.WithStatements(new SyntaxList <StatementSyntax>(statements.Skip(2))); returnExpression = returnExpression.Replace("schema.CrudAction", storedProcedure.SqlObjectName); } methodNode = methodNode.WithBody(methodBody); // Replace ReturnType and ReturnLine var returnType = "Task<CrudResult>"; var returnModel = "CrudResult"; if (!storedProcedure.HasResult() && storedProcedure.HasOutputs()) { var outputType = storedProcedure.GetOutputTypeName(); returnType = $"Task<{outputType}>"; returnExpression = returnExpression.Replace("ExecuteSingleAsync<CrudResult>", $"ExecuteAsync<{outputType}>"); } else if (storedProcedure.IsScalarResult()) { var output = storedProcedure.Output.FirstOrDefault(); returnModel = ParseTypeFromSqlDbTypeName(output.SqlTypeName, output.IsNullable ?? false).ToString(); returnType = $"Task<{returnModel}>"; returnExpression = returnExpression.Replace("ExecuteSingleAsync<CrudResult>", $"ReadJsonAsync"); } else { switch (storedProcedure.OperationKind) { case Definition.OperationKindEnum.Find: case Definition.OperationKindEnum.List: returnModel = storedProcedure.Name; break; } switch (storedProcedure.ResultKind) { case Definition.ResultKindEnum.Single: returnType = $"Task<{returnModel}>"; returnExpression = returnExpression.Replace("ExecuteSingleAsync<CrudResult>", $"ExecuteSingleAsync<{returnModel}>"); break; case Definition.ResultKindEnum.List: returnType = $"Task<List<{returnModel}>>"; returnExpression = returnExpression.Replace("ExecuteSingleAsync<CrudResult>", $"ExecuteListAsync<{returnModel}>"); break; } } methodNode = methodNode.WithReturnType(SyntaxFactory.ParseTypeName(returnType).WithTrailingTrivia(SyntaxFactory.Space)); var returnStatementSyntax = statements.Single(i => i is ReturnStatementSyntax); var returnStatementSyntaxIndex = statements.IndexOf(returnStatementSyntax); statements[returnStatementSyntaxIndex] = SyntaxFactory.ReturnStatement(SyntaxFactory.ParseExpression(returnExpression).WithLeadingTrivia(SyntaxFactory.Space)) .WithLeadingTrivia(SyntaxFactory.Tab, SyntaxFactory.Tab, SyntaxFactory.Tab) .WithTrailingTrivia(SyntaxFactory.CarriageReturn); methodBody = methodBody.WithStatements(new SyntaxList <StatementSyntax>(statements)); methodNode = methodNode.WithBody(methodBody); return(methodNode.NormalizeWhitespace()); }
public SourceText GetInputTextForStoredProcedure(Definition.Schema schema, Definition.StoredProcedure storedProcedure) { var rootDir = _output.GetOutputRootDir(); var fileContent = File.ReadAllText(Path.Combine(rootDir.FullName, "DataContext", "Inputs", "Input.cs")); var tree = CSharpSyntaxTree.ParseText(fileContent); var root = tree.GetCompilationUnitRoot(); // If Inputs contains a TableType, add using for TableTypes var schemesForTableTypes = storedProcedure.Input.Where(i => i.IsTableType ?? false) .GroupBy(t => (t.TableTypeSchemaName, t.TableTypeName), (key, group) => new { TableTypeSchemaName = key.TableTypeSchemaName, Result = group.First() }).Select(g => g.Result).ToList(); var tableTypeSchemas = storedProcedure.Input.Where(i => i.IsTableType ?? false) .GroupBy(t => t.TableTypeSchemaName, (key, group) => key).ToList(); foreach (var tableTypeSchema in tableTypeSchemas) { var tableTypeSchemaConfig = _configFile.Config.Schema.Find(s => s.Name.Equals(tableTypeSchema)); // is schema of table type ignored and its an extension? var useFromLib = tableTypeSchemaConfig?.Status != SchemaStatusEnum.Build && _configFile.Config.Project.Role.Kind == ERoleKind.Extension; var paramUsingDirective = useFromLib ? SyntaxFactory.UsingDirective(SyntaxFactory.ParseName($"{_configFile.Config.Project.Role.LibNamespace}.TableTypes.{tableTypeSchema.FirstCharToUpper()}")) : _configFile.Config.Project.Role.Kind == ERoleKind.Lib ? SyntaxFactory.UsingDirective(SyntaxFactory.ParseName($"{_configFile.Config.Project.Output.Namespace}.TableTypes.{tableTypeSchema.FirstCharToUpper()}")) : SyntaxFactory.UsingDirective(SyntaxFactory.ParseName($"{_configFile.Config.Project.Output.Namespace}.DataContext.TableTypes.{tableTypeSchema.FirstCharToUpper()}")); root = root.AddUsings(paramUsingDirective); } // Replace Namespace if (_configFile.Config.Project.Role.Kind == ERoleKind.Lib) { root = root.ReplaceNamespace(ns => ns.Replace("Source.DataContext", _configFile.Config.Project.Output.Namespace).Replace("Schema", schema.Name)); } else { root = root.ReplaceNamespace(ns => ns.Replace("Source", _configFile.Config.Project.Output.Namespace).Replace("Schema", schema.Name)); } // Replace ClassName root = root.ReplaceClassName(ci => ci.Replace("Input", $"{storedProcedure.Name}Input")); var nsNode = (NamespaceDeclarationSyntax)root.Members[0]; var classNode = (ClassDeclarationSyntax)nsNode.Members[0]; // Create obsolete constructor var obsoleteContructor = classNode.CreateConstructor($"{storedProcedure.Name}Input"); root = root.AddObsoleteAttribute(ref obsoleteContructor, "This empty contructor will be removed in vNext. Please use constructor with parameters."); root = root.AddConstructor(ref classNode, obsoleteContructor); nsNode = (NamespaceDeclarationSyntax)root.Members[0]; classNode = (ClassDeclarationSyntax)nsNode.Members[0]; var inputs = storedProcedure.Input.Where(i => !i.IsOutput).ToList(); // Constructor with params var constructor = classNode.CreateConstructor($"{storedProcedure.Name}Input"); var parameters = inputs.Select(input => { return(SyntaxFactory.Parameter(SyntaxFactory.Identifier(GetIdentifierFromSqlInputTableType(input.Name))) .WithType( input.IsTableType ?? false ? GetTypeSyntaxForTableType(input) : ParseTypeFromSqlDbTypeName(input.SqlTypeName, input.IsNullable ?? false) )); }).ToArray(); var constructorParams = constructor.ParameterList.AddParameters(parameters); constructor = constructor.WithParameterList(constructorParams); foreach (var input in inputs) { var constructorStatement = ExpressionHelper.AssignmentStatement(TokenHelper.Parse(input.Name).ToString(), GetIdentifierFromSqlInputTableType(input.Name)); var newStatements = constructor.Body.Statements.Add(constructorStatement); constructor = constructor.WithBody(constructor.Body.WithStatements(newStatements)); } root = root.AddConstructor(ref classNode, constructor); nsNode = (NamespaceDeclarationSyntax)root.Members[0]; classNode = (ClassDeclarationSyntax)nsNode.Members[0]; // Generate Properies // https://stackoverflow.com/questions/45160694/adding-new-field-declaration-to-class-with-roslyn foreach (var item in storedProcedure.Input) { nsNode = (NamespaceDeclarationSyntax)root.Members[0]; classNode = (ClassDeclarationSyntax)nsNode.Members[0]; var isTableType = item.IsTableType ?? false; var propertyType = isTableType ? GetTypeSyntaxForTableType(item) : ParseTypeFromSqlDbTypeName(item.SqlTypeName, item.IsNullable ?? false); var propertyNode = classNode.CreateProperty(propertyType, item.Name); if (!isTableType) { // Add Attribute for NVARCHAR with MaxLength if ((item.SqlTypeName?.Equals(SqlDbType.NVarChar.ToString(), StringComparison.InvariantCultureIgnoreCase) ?? false) && item.MaxLength.HasValue) { var attributes = propertyNode.AttributeLists.Add( SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList <AttributeSyntax>( SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("MaxLength"), SyntaxFactory.ParseAttributeArgumentList($"({item.MaxLength})")) )).NormalizeWhitespace()); propertyNode = propertyNode.WithAttributeLists(attributes); } } root = root.AddProperty(ref classNode, propertyNode); } return(root.NormalizeWhitespace().GetText()); }