Saturday, July 4, 2026

Article - sql generation

In this Article, we will discuss about sql generation





DatabaseHelper.cs
----------------------------
using System;
using System.Collections.Generic;
using Microsoft.Data.SqlClient;

namespace BoilerplateApiGenerator
{
    public static class DatabaseHelper
    {
        public static List<string> GetDatabases(string connection)
        {
            using var con = new SqlConnection(connection);
            con.Open();
            using var cmd = new SqlCommand("SELECT name FROM sys.databases WHERE database_id > 4 ORDER BY name", con);
            using var rdr = cmd.ExecuteReader();
            var list = new List<string>();
            while (rdr.Read()) list.Add(rdr.GetString(0));
            return list;
        }

        public static List<TableSchema> GetTables(string connection, string dbName)
        {
            var tables = new Dictionary<string, TableSchema>(StringComparer.OrdinalIgnoreCase);
            using var con = new SqlConnection($"{connection};Initial Catalog={dbName}");
            con.Open();

            // Columns
            using (var cmd = new SqlCommand(@"
                SELECT c.TABLE_NAME, c.COLUMN_NAME, c.DATA_TYPE
                FROM INFORMATION_SCHEMA.COLUMNS c
                ORDER BY c.TABLE_NAME, c.ORDINAL_POSITION", con))
            using (var rdr = cmd.ExecuteReader())
            {
                while (rdr.Read())
                {
                    var t = rdr.GetString(0);
                    var col = rdr.GetString(1);
                    var dt = rdr.GetString(2);
                    if (!tables.TryGetValue(t, out var ts))
                    {
                        ts = new TableSchema { Name = t };
                        tables[t] = ts;
                    }
                    ts.Columns.Add(new ColumnSchema { Name = col, DataType = dt });
                }
            }

            // Primary keys
            using (var cmd = new SqlCommand(@"
                SELECT tc.TABLE_NAME, kcu.COLUMN_NAME
                FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
                JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
                  ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
                 AND tc.TABLE_NAME = kcu.TABLE_NAME
                WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
            ", con))
            using (var rdr = cmd.ExecuteReader())
            {
                while (rdr.Read())
                {
                    var t = rdr.GetString(0);
                    var pk = rdr.GetString(1);
                    if (tables.TryGetValue(t, out var ts))
                    {
                        ts.PrimaryKey = pk;
                    }
                }
            }

            return new List<TableSchema>(tables.Values);
        }
    }

    public class TableSchema
    {
        public string Name { get; set; } = string.Empty;
        public string? PrimaryKey { get; set; }
        public List<ColumnSchema> Columns { get; set; } = new();
    }

    public class ColumnSchema
    {
        public string Name { get; set; } = string.Empty;
        public string DataType { get; set; } = string.Empty;
    }
}



GeneratorService.cs
-----------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace BoilerplateApiGenerator
{
    public class GeneratorService
    {
        private readonly string _outputDir;
        public GeneratorService(string outputDir) => _outputDir = outputDir;

        // ---------------------------------------------------------------------
        // Main entry point to generate the complete 4-layer project
        // ---------------------------------------------------------------------
        public void GenerateSolution(
            string projectName,
            string connString,
            string dbName,
            List<TableSchema> tables)
        {
            // string solutionPath = Path.Combine(_outputDir, $"{projectName}.Generated");
            string solutionPath = Path.Combine(_outputDir, projectName);

            Directory.CreateDirectory(solutionPath);

            string nsDomain = $"{projectName}.Domain";
            string nsApp = $"{projectName}.Application";
            string nsInfra = $"{projectName}.Infrastructure";
            string nsApi = $"{projectName}.Api";

            // ---------------- DOMAIN ----------------
            string domDir = Path.Combine(solutionPath, $"{projectName}.Domain");
            Directory.CreateDirectory(Path.Combine(domDir, "Entities"));
            Directory.CreateDirectory(Path.Combine(domDir, "Abstractions"));

            foreach (var tbl in tables)
            {
                File.WriteAllText(Path.Combine(domDir, "Entities", $"{tbl.Name}.cs"),
                    TemplateEngine.GenerateDomainEntity(nsDomain, tbl));

                File.WriteAllText(Path.Combine(domDir, "Abstractions", $"I{tbl.Name}Repository.cs"),
                    TemplateEngine.GenerateDomainRepositoryAbstraction(nsDomain, tbl));
            }

            File.WriteAllText(Path.Combine(domDir, $"{projectName}.Domain.csproj"),
                TemplateEngine.GenerateDomainCsproj());

            // ---------------- APPLICATION ----------------
            string appDir = Path.Combine(solutionPath, $"{projectName}.Application");
            Directory.CreateDirectory(Path.Combine(appDir, "DTOs"));
            Directory.CreateDirectory(Path.Combine(appDir, "Interfaces"));
            Directory.CreateDirectory(Path.Combine(appDir, "Mapping"));

            foreach (var tbl in tables)
            {
                File.WriteAllText(Path.Combine(appDir, "DTOs", $"{tbl.Name}Dtos.cs"),
                    TemplateEngine.GenerateApplicationDtos(nsApp, tbl));

                File.WriteAllText(Path.Combine(appDir, "Interfaces", $"I{tbl.Name}Service.cs"),
                    TemplateEngine.GenerateApplicationInterfaces(nsApp, tbl));
            }

            File.WriteAllText(Path.Combine(appDir, "Mapping", "MappingProfile.cs"),
                TemplateEngine.GenerateMappingProfile(nsApp, nsDomain, tables));

            File.WriteAllText(Path.Combine(appDir, "DependencyInjection.cs"),
                TemplateEngine.GenerateAppDependencyInjection(nsApp));

            File.WriteAllText(Path.Combine(appDir, $"{projectName}.Application.csproj"),
                TemplateEngine.GenerateApplicationCsproj().Replace("{PROJECT}", projectName));

            // ---------------- INFRASTRUCTURE ----------------
            string infraDir = Path.Combine(solutionPath, $"{projectName}.Infrastructure");
            Directory.CreateDirectory(Path.Combine(infraDir, "Data"));
            Directory.CreateDirectory(Path.Combine(infraDir, "Repositories"));
            Directory.CreateDirectory(Path.Combine(infraDir, "Services"));
            Directory.CreateDirectory(Path.Combine(infraDir, "Identity"));
            Directory.CreateDirectory(Path.Combine(infraDir, "Logging"));

            File.WriteAllText(Path.Combine(infraDir, "Data", "AppDbContext.cs"),
                TemplateEngine.GenerateInfrastructureDbContext(nsInfra, nsDomain, tables));

            File.WriteAllText(Path.Combine(infraDir, "Identity", "ApplicationUser.cs"),
                TemplateEngine.GenerateIdentityUser(nsInfra));

            foreach (var tbl in tables)
            {
                File.WriteAllText(Path.Combine(infraDir, "Repositories", $"{tbl.Name}Repository.cs"),
                    TemplateEngine.GenerateInfrastructureRepository(nsInfra, nsDomain, tbl));

                File.WriteAllText(Path.Combine(infraDir, "Services", $"{tbl.Name}Service.cs"),
                    TemplateEngine.GenerateInfrastructureService(nsInfra, nsApp, nsDomain, tbl));
            }

            //  Add Logging Service (Singleton NLog)
            File.WriteAllText(Path.Combine(infraDir, "Logging", "LoggingService.cs"),
                TemplateEngine.GenerateInfrastructureLoggingService(nsInfra));

            //  Add DI setup including LoggingService Singleton
            File.WriteAllText(Path.Combine(infraDir, "DependencyInjection.cs"),
                TemplateEngine.GenerateInfraDependencyInjection(nsInfra, nsDomain, nsApp, tables));



            File.WriteAllText(Path.Combine(infraDir, $"{projectName}.Infrastructure.csproj"),
                TemplateEngine.GenerateInfrastructureCsproj().Replace("{PROJECT}", projectName));

            // ---------------- API ----------------
            string apiDir = Path.Combine(solutionPath, $"{projectName}.Api");
            Directory.CreateDirectory(Path.Combine(apiDir, "Controllers"));
            Directory.CreateDirectory(Path.Combine(apiDir, "Properties"));
            Directory.CreateDirectory(Path.Combine(apiDir, "Middleware"));
            Directory.CreateDirectory(Path.Combine(apiDir, "Extensions"));
            Directory.CreateDirectory(Path.Combine(apiDir, "Models"));

            foreach (var tbl in tables)
            {
                File.WriteAllText(Path.Combine(apiDir, "Controllers", $"{tbl.Name}Controller.cs"),
                    TemplateEngine.GenerateApiController(nsApi, nsApp, tbl));
            }

            //  Add Global Exception Middleware under API
            File.WriteAllText(Path.Combine(apiDir, "Middleware", "GlobalExceptionMiddleware.cs"),
                TemplateEngine.GenerateGlobalExceptionMiddleware(nsApi));

            //  Add Error Response model
            File.WriteAllText(Path.Combine(apiDir, "Models", "ErrorResponse.cs"),
                TemplateEngine.GenerateErrorResponseModel(nsApi));

            //  Add Middleware Extension
            File.WriteAllText(Path.Combine(apiDir, "Extensions", "ExceptionMiddlewareExtensions.cs"),
                TemplateEngine.GenerateExceptionMiddlewareExtension(nsApi));

            //  Program.cs with app.UseGlobalException()
            File.WriteAllText(Path.Combine(apiDir, "Program.cs"),
                TemplateEngine.GenerateApiProgramCs(nsApp, nsInfra, nsApi));

            //  NLog configuration
            File.WriteAllText(Path.Combine(apiDir, "nlog.config"),
                TemplateEngine.GenerateNLogConfig());

            //  appsettings.json
            File.WriteAllText(Path.Combine(apiDir, "appsettings.json"),
                TemplateEngine.GenerateAppSettings(connString, dbName));

            //  launchSettings.json for auto Swagger
            Directory.CreateDirectory(Path.Combine(apiDir, "Properties"));
            File.WriteAllText(Path.Combine(apiDir, "Properties", "launchSettings.json"),
                TemplateEngine.GenerateLaunchSettings(projectName));

            //  API .csproj
            File.WriteAllText(Path.Combine(apiDir, $"{projectName}.Api.csproj"),
                TemplateEngine.GenerateApiCsproj(projectName));

            //  Create the Solution File
            CreateSolutionFile(solutionPath, projectName);

            //  Write README Summary
            File.WriteAllText(Path.Combine(solutionPath, "README_Generated.txt"),
                BuildSummary(projectName, solutionPath, tables));
        }

        // ---------------------------------------------------------------------
        // Solution Summary
        // ---------------------------------------------------------------------
        private static string BuildSummary(string projectName, string solutionPath, List<TableSchema> tables)
        {
            var sb = new StringBuilder();
            sb.AppendLine($"=== {projectName} Boilerplate Generated ===");
            sb.AppendLine($"Location: {solutionPath}");
            sb.AppendLine();
            sb.AppendLine("Tables Processed:");
            foreach (var t in tables)
                sb.AppendLine($"  - {t.Name} ({t.Columns.Count} columns)");
            sb.AppendLine();
            sb.AppendLine("Projects:");
            sb.AppendLine($"  • {projectName}.Domain");
            sb.AppendLine($"  • {projectName}.Application");
            sb.AppendLine($"  • {projectName}.Infrastructure");
            sb.AppendLine($"  • {projectName}.Api");
            sb.AppendLine();
            sb.AppendLine("Includes:");
            sb.AppendLine("   NLog Singleton Logger");
            sb.AppendLine("   Global Exception Handling Middleware");
            sb.AppendLine("   Swagger Auto Launch");
            sb.AppendLine("   Full DI Configuration");
            sb.AppendLine();
            sb.AppendLine("To run:");
            sb.AppendLine($"  1. Open {projectName}.sln");
            sb.AppendLine($"  2. Set {projectName}.Api as startup project.");
            sb.AppendLine("  3. Run → Swagger UI auto-opens with JWT + GlobalExceptionMiddleware.");
            sb.AppendLine();


            sb.AppendLine("If you get error in the generated application do the below:");
            sb.AppendLine("Add - <PackageReference Include=\"Microsoft.AspNetCore.Authentication.JwtBearer\" Version=\"8.0.0\" />     in  in SmartCertify.API\\SmartCertify.API.Infrastructure\\SmartCertify.API.Infrastructure.csproj - ");

            sb.AppendLine("change verion number to 5.3.0 for NLog - <PackageReference Include=\"NLog\" Version=\"5.3.0\" />");
            sb.AppendLine("Comment the code in(NLog.Config) in Infrastructure - <PackageReference Include = \"NLog.Config\" Version = \"5.0.5\" />");

            sb.AppendLine("Generated by BoilerplateApiGenerator");
            return sb.ToString();
        }

        // ---------------------------------------------------------------------
        // Create Visual Studio Solution File
        // ---------------------------------------------------------------------
        private static void CreateSolutionFile(string solutionPath, string projectName)
        {
            string solutionFile = Path.Combine(solutionPath, $"{projectName}.sln");
            var sb = new StringBuilder();

            sb.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00");
            sb.AppendLine("# Visual Studio Version 17");
            sb.AppendLine("VisualStudioVersion = 17.0.31903.59");
            sb.AppendLine("MinimumVisualStudioVersion = 10.0.40219.1");

            string[] layers = { "Domain", "Application", "Infrastructure", "Api" };

            foreach (var layer in layers)
            {
                string guid = Guid.NewGuid().ToString("B").ToUpper();
                string projPath = $"{projectName}.{layer}\\{projectName}.{layer}.csproj";
                sb.AppendLine($"Project(\"{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}\") = \"{projectName}.{layer}\", \"{projPath}\", \"{guid}\"");
                sb.AppendLine("EndProject");
            }

            sb.AppendLine("Global");
            sb.AppendLine(" GlobalSection(SolutionConfigurationPlatforms) = preSolution");
            sb.AppendLine(" Debug|Any CPU = Debug|Any CPU");
            sb.AppendLine(" Release|Any CPU = Release|Any CPU");
            sb.AppendLine(" EndGlobalSection");

            sb.AppendLine(" GlobalSection(SolutionProperties) = preSolution");
            sb.AppendLine(" HideSolutionNode = FALSE");
            sb.AppendLine(" EndGlobalSection");
            sb.AppendLine("EndGlobal");

            File.WriteAllText(solutionFile, sb.ToString(), Encoding.UTF8);
        }
    }
}




MainForm.cs
-------------------------
using System;
using System.IO;
using System.Linq;
using System.Reflection.Emit;
using System.Windows.Forms;
using System.Xml.Linq;

namespace BoilerplateApiGenerator
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private void btnLoadDBs_Click(object sender, EventArgs e)
        {
            try
            {
                var dbs = DatabaseHelper.GetDatabases(txtConnection.Text);
                cmbDatabases.Items.Clear();
                foreach (var d in dbs) cmbDatabases.Items.Add(d);
                if (dbs.Count > 0) cmbDatabases.SelectedIndex = 0;
                MessageBox.Show($"Loaded {dbs.Count} databases.");
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: " + ex.Message);
            }
        }

        private void btnLoadTables_Click(object sender, EventArgs e)
        {
            try
            {
                if (cmbDatabases.SelectedItem is null)
                {
                    MessageBox.Show("Please select a database first.");
                    return;
                }
                chkTables.Items.Clear();
                var tables = DatabaseHelper.GetTables(txtConnection.Text, cmbDatabases.SelectedItem!.ToString()!);
                foreach (var t in tables.Select(t => t.Name).Distinct())
                    chkTables.Items.Add(t, true);
                MessageBox.Show("Tables loaded. Select/deselect as needed.");
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: " + ex.Message);
            }
        }

        private void btnBrowse_Click(object sender, EventArgs e)
        {
            using var fbd = new FolderBrowserDialog();
            if (fbd.ShowDialog() == DialogResult.OK)
                txtOutput.Text = fbd.SelectedPath;
        }


        private void btnGenerate_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(txtProjectName.Text))
            {
                MessageBox.Show("Enter a project name (e.g., MyCompany.Api).");
                return;
            }
            if (cmbDatabases.SelectedItem is null)
            {
                MessageBox.Show("Select a database.");
                return;
            }
            if (string.IsNullOrWhiteSpace(txtOutput.Text) || !Directory.Exists(txtOutput.Text))
            {
                MessageBox.Show("Choose a valid output folder.");
                return;
            }

            var allTables = DatabaseHelper.GetTables(txtConnection.Text, cmbDatabases.SelectedItem!.ToString()!);
            var selected = chkTables.CheckedItems.Cast<string>().ToHashSet(StringComparer.OrdinalIgnoreCase);
            var useTables = allTables.Where(t => selected.Contains(t.Name)).ToList();

            if (useTables.Count == 0)
            {
                MessageBox.Show("Please select at least one table.");
                return;
            }

            progressBar1.Visible = true;
            btnGenerate.Enabled = false;

            try
            {
                var gen = new GeneratorService(txtOutput.Text);
                gen.GenerateSolution(txtProjectName.Text.Trim(), txtConnection.Text, cmbDatabases.SelectedItem!.ToString()!, useTables);

                //await gen.GenerateApiAsync(txtProjectName.Text.Trim(), txtConnection.Text, cmbDatabases.SelectedItem!.ToString()!, useTables);
                MessageBox.Show("API generated successfully.");
            }
            catch (Exception ex)
            {
                MessageBox.Show("Generation failed: " + ex.Message);
            }
            finally
            {
                btnGenerate.Enabled = true;
                progressBar1.Visible = false;
            }
        }
    }
}



MainForm.Designer.cs
-------------------------------------
namespace BoilerplateApiGenerator
{
    partial class MainForm
    {
        /// <summary>
        ///  Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        ///  Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        ///  Required method for Designer support - do not modify
        ///  the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.txtConnection = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.btnLoadDBs = new System.Windows.Forms.Button();
            this.cmbDatabases = new System.Windows.Forms.ComboBox();
            this.label2 = new System.Windows.Forms.Label();
            this.btnLoadTables = new System.Windows.Forms.Button();
            this.chkTables = new System.Windows.Forms.CheckedListBox();
            this.label3 = new System.Windows.Forms.Label();
            this.txtProjectName = new System.Windows.Forms.TextBox();
            this.label4 = new System.Windows.Forms.Label();
            this.txtOutput = new System.Windows.Forms.TextBox();
            this.btnBrowse = new System.Windows.Forms.Button();
            this.btnGenerate = new System.Windows.Forms.Button();
            this.progressBar1 = new System.Windows.Forms.ProgressBar();
            this.SuspendLayout();
            // 
            // txtConnection
            // 
            this.txtConnection.Location = new System.Drawing.Point(24, 44);
            this.txtConnection.Name = "txtConnection";
            this.txtConnection.Size = new System.Drawing.Size(612, 27);
            this.txtConnection.TabIndex = 0;
            this.txtConnection.Text = "Server=.;Integrated Security=True;TrustServerCertificate=True;";
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(24, 20);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(202, 20);
            this.label1.TabIndex = 1;
            this.label1.Text = "SQL Server connection string";
            // 
            // btnLoadDBs
            // 
            this.btnLoadDBs.Location = new System.Drawing.Point(642, 44);
            this.btnLoadDBs.Name = "btnLoadDBs";
            this.btnLoadDBs.Size = new System.Drawing.Size(117, 29);
            this.btnLoadDBs.TabIndex = 2;
            this.btnLoadDBs.Text = "Load DBs";
            this.btnLoadDBs.UseVisualStyleBackColor = true;
            this.btnLoadDBs.Click += new System.EventHandler(this.btnLoadDBs_Click);
            // 
            // cmbDatabases
            // 
            this.cmbDatabases.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cmbDatabases.FormattingEnabled = true;
            this.cmbDatabases.Location = new System.Drawing.Point(24, 110);
            this.cmbDatabases.Name = "cmbDatabases";
            this.cmbDatabases.Size = new System.Drawing.Size(349, 28);
            this.cmbDatabases.TabIndex = 3;
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(24, 87);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(77, 20);
            this.label2.TabIndex = 4;
            this.label2.Text = "Database";
            // 
            // btnLoadTables
            // 
            this.btnLoadTables.Location = new System.Drawing.Point(379, 110);
            this.btnLoadTables.Name = "btnLoadTables";
            this.btnLoadTables.Size = new System.Drawing.Size(118, 29);
            this.btnLoadTables.TabIndex = 5;
            this.btnLoadTables.Text = "Load Tables";
            this.btnLoadTables.UseVisualStyleBackColor = true;
            this.btnLoadTables.Click += new System.EventHandler(this.btnLoadTables_Click);
            // 
            // chkTables
            // 
            this.chkTables.CheckOnClick = true;
            this.chkTables.FormattingEnabled = true;
            this.chkTables.Location = new System.Drawing.Point(24, 170);
            this.chkTables.Name = "chkTables";
            this.chkTables.Size = new System.Drawing.Size(349, 312);
            this.chkTables.TabIndex = 6;
            // 
            // label3
            // 
            this.label3.AutoSize = true;
            this.label3.Location = new System.Drawing.Point(24, 147);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(56, 20);
            this.label3.TabIndex = 7;
            this.label3.Text = "Tables";
            // 
            // txtProjectName
            // 
            this.txtProjectName.Location = new System.Drawing.Point(397, 170);
            this.txtProjectName.Name = "txtProjectName";
            this.txtProjectName.PlaceholderText = "MyCompany.Api";
            this.txtProjectName.Size = new System.Drawing.Size(362, 27);
            this.txtProjectName.TabIndex = 8;
            // 
            // label4
            // 
            this.label4.AutoSize = true;
            this.label4.Location = new System.Drawing.Point(397, 147);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(99, 20);
            this.label4.TabIndex = 9;
            this.label4.Text = "Project name";
            // 
            // txtOutput
            // 
            this.txtOutput.Location = new System.Drawing.Point(397, 226);
            this.txtOutput.Name = "txtOutput";
            this.txtOutput.ReadOnly = true;
            this.txtOutput.Size = new System.Drawing.Size(302, 27);
            this.txtOutput.TabIndex = 10;
            // 
            // btnBrowse
            // 
            this.btnBrowse.Location = new System.Drawing.Point(705, 225);
            this.btnBrowse.Name = "btnBrowse";
            this.btnBrowse.Size = new System.Drawing.Size(54, 29);
            this.btnBrowse.TabIndex = 11;
            this.btnBrowse.Text = "...";
            this.btnBrowse.UseVisualStyleBackColor = true;
            this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click);
            // 
            // btnGenerate
            // 
            this.btnGenerate.Location = new System.Drawing.Point(397, 275);
            this.btnGenerate.Name = "btnGenerate";
            this.btnGenerate.Size = new System.Drawing.Size(362, 39);
            this.btnGenerate.TabIndex = 12;
            this.btnGenerate.Text = "Generate API Boilerplate";
            this.btnGenerate.UseVisualStyleBackColor = true;
            this.btnGenerate.Click += new System.EventHandler(this.btnGenerate_Click);
            // 
            // progressBar1
            // 
            this.progressBar1.Location = new System.Drawing.Point(397, 333);
            this.progressBar1.MarqueeAnimationSpeed = 30;
            this.progressBar1.Name = "progressBar1";
            this.progressBar1.Size = new System.Drawing.Size(362, 18);
            this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
            this.progressBar1.TabIndex = 13;
            this.progressBar1.Visible = false;
            // 
            // MainForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(784, 511);
            this.Controls.Add(this.progressBar1);
            this.Controls.Add(this.btnGenerate);
            this.Controls.Add(this.btnBrowse);
            this.Controls.Add(this.txtOutput);
            this.Controls.Add(this.label4);
            this.Controls.Add(this.txtProjectName);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.chkTables);
            this.Controls.Add(this.btnLoadTables);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.cmbDatabases);
            this.Controls.Add(this.btnLoadDBs);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.txtConnection);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "MainForm";
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
            this.Text = "Boilerplate API Code Generator (EF Core + Swagger)";
            this.ResumeLayout(false);
            this.PerformLayout();
        }

        #endregion

        private System.Windows.Forms.TextBox txtConnection;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Button btnLoadDBs;
        private System.Windows.Forms.ComboBox cmbDatabases;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.Button btnLoadTables;
        private System.Windows.Forms.CheckedListBox chkTables;
        private System.Windows.Forms.Label label3;
        private System.Windows.Forms.TextBox txtProjectName;
        private System.Windows.Forms.Label label4;
        private System.Windows.Forms.TextBox txtOutput;
        private System.Windows.Forms.Button btnBrowse;
        private System.Windows.Forms.Button btnGenerate;
        private System.Windows.Forms.ProgressBar progressBar1;
    }
}




Program.cs
--------------------------------------------------
using System;
using System.Windows.Forms;

namespace BoilerplateApiGenerator
{
    internal static class Program
    {
        [STAThread]
        static void Main()
        {
            ApplicationConfiguration.Initialize();
            Application.Run(new MainForm());
        }
    }
}



TemplateEngine.cs
----------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BoilerplateApiGenerator
{
    public static class TemplateEngine
    {
        // ------------------- DOMAIN -------------------
        public static string GenerateDomainEntity(string nsDomain, TableSchema tbl)
        {
            var pkCol = tbl.Columns.FirstOrDefault(c =>
                string.Equals(c.Name, tbl.PrimaryKey ?? "Id", StringComparison.OrdinalIgnoreCase))
                ?? tbl.Columns.First();

            var sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("using System.ComponentModel.DataAnnotations;");
            sb.AppendLine();
            sb.AppendLine($"namespace {nsDomain}.Entities;");
            sb.AppendLine();
            sb.AppendLine($"public class {tbl.Name}");
            sb.AppendLine("{");

            foreach (var col in tbl.Columns)
            {
                var type = MapCSharpType(col.DataType);
                if (string.Equals(col.Name, pkCol.Name, StringComparison.OrdinalIgnoreCase))
                    sb.AppendLine("    [Key]");
                sb.AppendLine($"    public {type} {col.Name} {{ get; set; }}");
            }

            sb.AppendLine("}");
            return sb.ToString();
        }

        public static string GenerateDomainRepositoryAbstraction(string ns, TableSchema tbl)
        {
            return $@"using System.Linq;
using System.Threading.Tasks;
using {ns}.Entities;

namespace {ns}.Abstractions;
public interface I{tbl.Name}Repository
{{
    Task<{tbl.Name}?> GetAsync(int id);
    IQueryable<{tbl.Name}> Query();
    Task<{tbl.Name}> AddAsync({tbl.Name} e);
    Task UpdateAsync({tbl.Name} e);
    Task DeleteAsync({tbl.Name} e);
    Task SaveAsync();
}}";
        }

        public static string GenerateDomainCsproj() =>
@"<Project Sdk=""Microsoft.NET.Sdk"">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>";

        // ------------------- APPLICATION -------------------
        public static string GenerateApplicationDtos(string ns, TableSchema tbl)
        {
            var props = string.Join(Environment.NewLine,
                tbl.Columns.Select(c => $"    public {MapCSharpType(c.DataType)} {c.Name} {{ get; set; }}"));
            return $@"namespace {ns}.DTOs;
public class {tbl.Name}ReadDto
{{
{props}
}}
public class {tbl.Name}CreateDto
{{
{props}
}}
public class {tbl.Name}UpdateDto
{{
{props}
}}";
        }

        public static string GenerateApplicationInterfaces(string ns, TableSchema tbl)
        {
            return $@"using System.Collections.Generic;
using System.Threading.Tasks;
using {ns}.DTOs;

namespace {ns}.Interfaces;
public interface I{tbl.Name}Service
{{
    Task<{tbl.Name}ReadDto?> GetAsync(int id);
    Task<IEnumerable<{tbl.Name}ReadDto>> GetAllAsync();
    Task<{tbl.Name}ReadDto> CreateAsync({tbl.Name}CreateDto dto);
    Task<{tbl.Name}ReadDto?> UpdateAsync(int id, {tbl.Name}UpdateDto dto);
    Task<bool> DeleteAsync(int id);
}}";
        }

        public static string GenerateMappingProfile(string nsApp, string nsDomain, IEnumerable<TableSchema> tables)
        {
            var sb = new StringBuilder();
            sb.AppendLine("using AutoMapper;");
            sb.AppendLine($"using {nsApp}.DTOs;");
            sb.AppendLine($"using {nsDomain}.Entities;");
            sb.AppendLine();
            sb.AppendLine($"namespace {nsApp}.Mapping;");
            sb.AppendLine("public class MappingProfile : Profile");
            sb.AppendLine("{");
            sb.AppendLine("    public MappingProfile()");
            sb.AppendLine("    {");
            foreach (var tbl in tables)
            {
                sb.AppendLine($"        CreateMap<{tbl.Name}, {tbl.Name}ReadDto>();");
                sb.AppendLine($"        CreateMap<{tbl.Name}CreateDto, {tbl.Name}>();");
                sb.AppendLine($"        CreateMap<{tbl.Name}UpdateDto, {tbl.Name}>();");
                sb.AppendLine($"        CreateMap<{tbl.Name}, {tbl.Name}UpdateDto>();");
            }
            sb.AppendLine("    }");
            sb.AppendLine("}");
            return sb.ToString();
        }

        public static string GenerateAppDependencyInjection(string ns)
        {
            return $@"using Microsoft.Extensions.DependencyInjection;
using AutoMapper;
using System.Reflection;

namespace {ns};
public static class DependencyInjection
{{
    public static IServiceCollection AddApplication(this IServiceCollection services)
    {{
        services.AddAutoMapper(Assembly.GetExecutingAssembly());
        return services;
    }}
}}";
        }

        public static string GenerateApplicationCsproj() =>
@"<Project Sdk=""Microsoft.NET.Sdk"">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include=""..\{PROJECT}.Domain\{PROJECT}.Domain.csproj"" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include=""AutoMapper.Extensions.Microsoft.DependencyInjection"" Version=""12.0.1"" />
  </ItemGroup>
</Project>";

        // ------------------- INFRASTRUCTURE -------------------
        public static string GenerateInfrastructureCsproj() =>
@"<Project Sdk=""Microsoft.NET.Sdk"">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include=""..\{PROJECT}.Domain\{PROJECT}.Domain.csproj"" />
    <ProjectReference Include=""..\{PROJECT}.Application\{PROJECT}.Application.csproj"" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include=""Microsoft.EntityFrameworkCore.SqlServer"" Version=""8.0.0"" />
    <PackageReference Include=""Microsoft.AspNetCore.Identity.EntityFrameworkCore"" Version=""8.0.0"" />
    <PackageReference Include=""Microsoft.AspNetCore.Authentication.JwtBearer"" Version=""8.0.0"" /> 
    <PackageReference Include=""NLog"" Version=""5.3.0"" />
    <!--<PackageReference Include=""NLog.Config"" Version=""5.0.5"" /> -->
    <PackageReference Include=""NLog.Extensions.Logging"" Version=""5.3.0"" />
  </ItemGroup>
</Project>";


        public static string GenerateInfraDependencyInjection(string nsInfra, string nsDomain, string nsApp, List<TableSchema> tables)
        {
            var registrations = new StringBuilder();

            // Dynamically register all repositories and services for each table/entity
            foreach (var tbl in tables)
            {
                registrations.AppendLine(
                    $"        services.AddScoped<{nsDomain}.Abstractions.I{tbl.Name}Repository, {nsInfra}.Repositories.{tbl.Name}Repository>();");
                registrations.AppendLine(
                    $"        services.AddScoped<{nsApp}.Interfaces.I{tbl.Name}Service, {nsInfra}.Services.{tbl.Name}Service>();");
            }

            return $@"using System;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using {nsInfra}.Data;
using {nsInfra}.Identity;
using {nsInfra}.Logging;
using {nsDomain}.Abstractions;
using {nsApp}.Interfaces;
using {nsInfra}.Repositories;
using {nsInfra}.Services;

namespace {nsInfra};
public static class DependencyInjection
{{
    public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
    {{
        // ---------------- Database ----------------
        services.AddDbContext<AppDbContext>(opt => 
            opt.UseSqlServer(configuration.GetConnectionString(""DefaultConnection"")));

        // ---------------- Identity ----------------
        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<AppDbContext>()
            .AddDefaultTokenProviders();

        // ---------------- JWT Authentication ----------------
        var key = Encoding.UTF8.GetBytes(configuration[""Jwt:Key""]!);
        services.AddAuthentication(options =>
        {{
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }})
        .AddJwtBearer(options =>
        {{
            options.SaveToken = true;
            options.TokenValidationParameters = new TokenValidationParameters
            {{
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidIssuer = configuration[""Jwt:Issuer""],
                ValidAudience = configuration[""Jwt:Audience""],
                ClockSkew = TimeSpan.FromMinutes(2)
            }};
        }});

        // ---------------- Authorization ----------------
        services.AddAuthorization();

        // ---------------- Logging (Singleton) ----------------
        services.AddSingleton<ILoggingService>(LoggingService.Instance);

        // ---------------- Dynamic Service/Repository Registrations ----------------
{registrations}
        return services;
    }}
}}";
        }


        public static string GenerateInfrastructureLoggingService(string nsInfra)
        {
            return $@"using NLog;
using NLog.Config;
using NLog.Targets;

namespace {nsInfra}.Logging;
public interface ILoggingService
{{
    void LogInfo(string message);
    void LogError(string message, Exception ex);
}}

public sealed class LoggingService : ILoggingService
{{
    private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
    private static readonly Lazy<LoggingService> _instance = new(() => new LoggingService());
    public static LoggingService Instance => _instance.Value;

    private LoggingService()
    {{
        var config = new LoggingConfiguration();
        var fileTarget = new FileTarget(""logfile"")
        {{
            FileName = ""logs/logfile.txt"",
            Layout = ""${{longdate}} | ${{level:uppercase=true}} | ${{message}} | ${{exception}}"" 
        }};
        var consoleTarget = new ConsoleTarget(""logconsole"")
        {{
            Layout = ""${{longdate}} | ${{level}} | ${{message}}"" 
        }};
        config.AddRule(LogLevel.Info, LogLevel.Fatal, consoleTarget);
        config.AddRule(LogLevel.Debug, LogLevel.Fatal, fileTarget);
        LogManager.Configuration = config;
    }}

    public void LogInfo(string message) => _logger.Info(message);
    public void LogError(string message, Exception ex) => _logger.Error(ex, message);
}}";
        }

        // ------------------- GLOBAL EXCEPTION -------------------
        public static string GenerateGlobalExceptionMiddleware(string nsApi)
        {
            var nsInfra = nsApi.Replace(".Api", ".Infrastructure");
            return $@"using System.Net;
using System.Text.Json;
using Microsoft.AspNetCore.Http;
using {nsApi}.Models;
using {nsInfra}.Logging;

namespace {nsApi}.Middleware;
public class GlobalExceptionMiddleware
{{
    private readonly RequestDelegate _next;
    private readonly ILoggingService _logger;

    public GlobalExceptionMiddleware(RequestDelegate next, ILoggingService logger)
    {{
        _next = next;
        _logger = logger;
    }}

    public async Task InvokeAsync(HttpContext context)
    {{
        try
        {{
            await _next(context);
        }}
        catch (Exception ex)
        {{
            _logger.LogError(""Unhandled Exception"", ex);
            await HandleExceptionAsync(context, ex);
        }}
    }}

    private static Task HandleExceptionAsync(HttpContext context, Exception ex)
    {{
        var response = new ErrorResponse
        {{
            StatusCode = (int)HttpStatusCode.InternalServerError,
            Message = ex.Message,
            Details = ex.InnerException?.Message
        }};
        context.Response.ContentType = ""application/json"";
        context.Response.StatusCode = response.StatusCode;
        return context.Response.WriteAsync(JsonSerializer.Serialize(response));
    }}
}}";
        }

        public static string GenerateErrorResponseModel(string nsApi)
        {
            return $@"namespace {nsApi}.Models;
public class ErrorResponse
{{
    public int StatusCode {{ get; set; }}
    public string Message {{ get; set; }} = string.Empty;
    public string? Details {{ get; set; }}
}}";
        }

        public static string GenerateExceptionMiddlewareExtension(string nsApi)
        {
            return $@"using Microsoft.AspNetCore.Builder;

namespace {nsApi}.Extensions;
public static class ExceptionMiddlewareExtensions
{{
    public static IApplicationBuilder UseGlobalException(this IApplicationBuilder app)
    {{
        app.UseMiddleware<{nsApi}.Middleware.GlobalExceptionMiddleware>();
        return app;
    }}
}}";
        }

        // ------------------- NLOG CONFIG -------------------
        public static string GenerateNLogConfig()
        {
            return @"<?xml version=""1.0"" encoding=""utf-8"" ?>
<nlog xmlns=""http://www.nlog-project.org/schemas/NLog.xsd"" 
      xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
      autoReload=""true"" throwConfigExceptions=""true"" internalLogLevel=""Off"">
  <targets>
    <target xsi:type=""File"" name=""logfile"" fileName=""logs/logfile.txt"" layout=""${longdate} | ${level:uppercase=true} | ${logger} | ${message} | ${exception}"" />
    <target xsi:type=""Console"" name=""logconsole"" layout=""${longdate} | ${level} | ${message}"" />
  </targets>
  <rules>
    <logger name=""*"" minlevel=""Debug"" writeTo=""logfile"" />
    <logger name=""*"" minlevel=""Info"" writeTo=""logconsole"" />
  </rules>
</nlog>";
        }

        // ------------------- SWAGGER AUTO LAUNCH -------------------
        public static string GenerateLaunchSettings(string projectName)
        {
            return $@"{{
  ""profiles"": {{
    ""http"": {{
      ""commandName"": ""Project"",
      ""dotnetRunMessages"": true,
      ""launchBrowser"": true,
      ""launchUrl"": ""swagger"",
      ""applicationUrl"": ""http://localhost:5000"",
      ""environmentVariables"": {{
        ""ASPNETCORE_ENVIRONMENT"": ""Development""
      }}
    }},
    ""https"": {{
      ""commandName"": ""Project"",
      ""dotnetRunMessages"": true,
      ""launchBrowser"": true,
      ""launchUrl"": ""swagger"",
      ""applicationUrl"": ""https://localhost:5001"",
      ""environmentVariables"": {{
        ""ASPNETCORE_ENVIRONMENT"": ""Development""
      }}
    }}
  }}
}}";
        }

        // ------------------- HELPER -------------------
        public static string MapCSharpType(string sqlType)
        {
            sqlType = sqlType.ToLower();
            if (sqlType.Contains("int")) return "int";
            if (sqlType.Contains("char") || sqlType.Contains("text")) return "string";
            if (sqlType.Contains("date") || sqlType.Contains("time")) return "DateTime";
            if (sqlType.Contains("decimal") || sqlType.Contains("numeric")) return "decimal";
            if (sqlType.Contains("float") || sqlType.Contains("real")) return "double";
            if (sqlType.Contains("bit")) return "bool";
            return "string";
        }


        // ---------------- Infrastructure Repository ----------------
        public static string GenerateInfrastructureRepository(string nsInfra, string nsDomain, TableSchema tbl)
        {
            var entity = tbl.Name;
            var pkCol = tbl.Columns.FirstOrDefault(c =>
                string.Equals(c.Name, tbl.PrimaryKey ?? "Id", StringComparison.OrdinalIgnoreCase))
                ?? tbl.Columns.First();
            var pkType = MapCSharpType(pkCol.DataType);

            return $@"using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using {nsInfra}.Data;
using {nsDomain}.Entities;
using {nsDomain}.Abstractions;

namespace {nsInfra}.Repositories;
public class {entity}Repository : I{entity}Repository
{{
    private readonly AppDbContext _db;
    public {entity}Repository(AppDbContext db) => _db = db;

    public async Task<{entity}?> GetAsync({pkType} id) => await _db.{entity}.FindAsync(id);
    public IQueryable<{entity}> Query() => _db.{entity}.AsNoTracking();
    public async Task<{entity}> AddAsync({entity} e) {{ _db.{entity}.Add(e); await _db.SaveChangesAsync(); return e; }}
    public async Task UpdateAsync({entity} e) {{ _db.{entity}.Update(e); await _db.SaveChangesAsync(); }}
    public async Task DeleteAsync({entity} e) {{ _db.{entity}.Remove(e); await _db.SaveChangesAsync(); }}
    public async Task SaveAsync() => await _db.SaveChangesAsync();
}}";
        }

        // ---------------- Infrastructure Service ----------------
        public static string GenerateInfrastructureService(string nsInfra, string nsApp, string nsDomain, TableSchema tbl)
        {
            var entity = tbl.Name;
            var pkCol = tbl.Columns.FirstOrDefault(c =>
                string.Equals(c.Name, tbl.PrimaryKey ?? "Id", StringComparison.OrdinalIgnoreCase))
                ?? tbl.Columns.First();
            var pkType = MapCSharpType(pkCol.DataType);

            return $@"using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;
using {nsApp}.DTOs;
using {nsApp}.Interfaces;
using {nsDomain}.Entities;
using {nsDomain}.Abstractions;

namespace {nsInfra}.Services;
public class {entity}Service : I{entity}Service
{{
    private readonly I{entity}Repository _repo;
    private readonly IMapper _mapper;
    public {entity}Service(I{entity}Repository repo, IMapper mapper)
    {{
        _repo = repo;
        _mapper = mapper;
    }}

    public async Task<{entity}ReadDto?> GetAsync({pkType} id)
        => _mapper.Map<{entity}ReadDto?>(await _repo.GetAsync(id));

    public async Task<IEnumerable<{entity}ReadDto>> GetAllAsync()
        => await _repo.Query().ProjectTo<{entity}ReadDto>(_mapper.ConfigurationProvider).ToListAsync();

    public async Task<{entity}ReadDto> CreateAsync({entity}CreateDto dto)
    {{
        var e = _mapper.Map<{entity}>(dto);
        await _repo.AddAsync(e);
        return _mapper.Map<{entity}ReadDto>(e);
    }}

    public async Task<{entity}ReadDto?> UpdateAsync({pkType} id, {entity}UpdateDto dto)
    {{
        var e = await _repo.GetAsync(id);
        if (e is null) return null;
        _mapper.Map(dto, e);
        await _repo.SaveAsync();
        return _mapper.Map<{entity}ReadDto>(e);
    }}

    public async Task<bool> DeleteAsync({pkType} id)
    {{
        var e = await _repo.GetAsync(id);
        if (e is null) return false;
        await _repo.DeleteAsync(e);
        return true;
    }}
}}";
        }

        // ---------------- Infrastructure DbContext ----------------
        public static string GenerateInfrastructureDbContext(string nsInfra, string nsDomain, List<TableSchema> tables)
        {
            var dbSets = string.Join(Environment.NewLine,
                tables.Select(t => $"    public DbSet<{nsDomain}.Entities.{t.Name}> {t.Name} {{ get; set; }}"));

            return $@"using Microsoft.EntityFrameworkCore;
using {nsDomain}.Entities;

namespace {nsInfra}.Data;
public class AppDbContext : DbContext
{{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {{ }}

{dbSets}
}}";
        }

        // ---------------- Identity User ----------------
        public static string GenerateIdentityUser(string nsInfra)
        {
            return $@"using Microsoft.AspNetCore.Identity;

namespace {nsInfra}.Identity;
public class ApplicationUser : IdentityUser
{{
}}";
        }

        // ---------------- AppSettings ----------------
        public static string GenerateAppSettings(string connString, string dbName)
        {
            string safeConn = connString.Replace("\\", "\\\\");
            return $@"{{
  ""ConnectionStrings"": {{
    ""DefaultConnection"": ""{safeConn};Initial Catalog={dbName};TrustServerCertificate=True""
  }},
  ""Jwt"": {{
    ""Key"": ""CHANGE_THIS_TO_A_LONG_RANDOM_SECRET_KEY_AT_LEAST_64_CHARS"",
    ""Issuer"": ""Generated.Api"",
    ""Audience"": ""Generated.Client"",
    ""ExpiryMinutes"": 120
  }},
  ""Logging"": {{
    ""LogLevel"": {{
      ""Default"": ""Information"",
      ""Microsoft.AspNetCore"": ""Warning""
    }}
  }},
  ""AllowedHosts"": ""*""
}}";
        }

        // ---------------- API Controller ----------------
        public static string GenerateApiController(string nsApi, string nsApp, TableSchema tbl)
        {
            var entity = tbl.Name;
            var pkCol = tbl.Columns.FirstOrDefault(c =>
                string.Equals(c.Name, tbl.PrimaryKey ?? "Id", StringComparison.OrdinalIgnoreCase))
                ?? tbl.Columns.First();
            var pkType = MapCSharpType(pkCol.DataType);

            return $@"using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using {nsApp}.DTOs;
using {nsApp}.Interfaces;

namespace {nsApi}.Controllers;
[ApiController]
[Route(""api/[controller]"")]
public class {entity}Controller : ControllerBase
{{
    private readonly I{entity}Service _svc;
    public {entity}Controller(I{entity}Service svc) => _svc = svc;

    [HttpGet]
    public async Task<IEnumerable<{entity}ReadDto>> GetAll() => await _svc.GetAllAsync();

    [HttpGet(""{{id}}"", Name = ""Get{entity}ById"")]
    public async Task<ActionResult<{entity}ReadDto>> Get({pkType} id)
    {{
        var r = await _svc.GetAsync(id);
        return r is null ? NotFound() : Ok(r);
    }}

    [Authorize(Roles = ""Admin"")]
    [HttpPost]
    public async Task<ActionResult<{entity}ReadDto>> Create({entity}CreateDto dto)
    {{
        var r = await _svc.CreateAsync(dto);
        var id = typeof({entity}ReadDto).GetProperty(""Id"")?.GetValue(r);
        return Created($""/api/{entity}/{{id}}"", r);
    }}

    [Authorize(Roles = ""Admin"")]
    [HttpPut(""{{id}}"")]
    public async Task<ActionResult<{entity}ReadDto>> Update({pkType} id, {entity}UpdateDto dto)
    {{
        var r = await _svc.UpdateAsync(id, dto);
        return r is null ? NotFound() : Ok(r);
    }}

    [Authorize(Roles = ""Admin"")]
    [HttpDelete(""{{id}}"")]
    public async Task<IActionResult> Delete({pkType} id)
    {{
        var ok = await _svc.DeleteAsync(id);
        return ok ? NoContent() : NotFound();
    }}
}}";
        }

        // ---------------- API Program.cs ----------------
        public static string GenerateApiProgramCs(string nsApp, string nsInfra, string nsApi)
        {
            return $@"using Microsoft.OpenApi.Models;
using {nsApp};
using {nsInfra};
using {nsApi}.Extensions;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{{
    c.SwaggerDoc(""v1"", new OpenApiInfo {{ Title = ""API"", Version = ""v1"" }});
    var scheme = new OpenApiSecurityScheme
    {{
        Name = ""Authorization"",
        Type = SecuritySchemeType.Http,
        Scheme = ""bearer"",
        BearerFormat = ""JWT"",
        In = ParameterLocation.Header
    }};
    c.AddSecurityDefinition(""Bearer"", scheme);
    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {{
        {{
            new OpenApiSecurityScheme
            {{
                Reference = new OpenApiReference
                {{
                    Type = ReferenceType.SecurityScheme,
                    Id = ""Bearer""
                }}
            }},
            new string[] {{ }}
        }}
    }});
}});

builder.Services.AddApplication();
builder.Services.AddInfrastructure(builder.Configuration);

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI();

app.UseHttpsRedirection();
app.UseGlobalException();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();";
        }

        // ---------------- API .csproj ----------------
        public static string GenerateApiCsproj(string projectName)
        {
            return $@"<Project Sdk=""Microsoft.NET.Sdk.Web"">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include=""..\\{projectName}.Application\\{projectName}.Application.csproj"" />
    <ProjectReference Include=""..\\{projectName}.Infrastructure\\{projectName}.Infrastructure.csproj"" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include=""Microsoft.AspNetCore.OpenApi"" Version=""8.0.7"" />
    <PackageReference Include=""Swashbuckle.AspNetCore"" Version=""6.6.2"" />
    <PackageReference Include=""Microsoft.AspNetCore.Authentication.JwtBearer"" Version=""8.0.7"" />
  </ItemGroup>
</Project>";
        }


    }
}



0 comments:

Post a Comment