You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过代码生成类似Visual Studio的CRUD样板代码及完整应用包?

嘿,我完全懂你想要的那种开箱即用的CRUD生成体验——就像VS自带的Account Controller样板那样,一键搞定从数据库实体到API端点的全套代码,连引用、配置都帮你弄好对吧?下面我一步步给你拆解怎么用T4模板实现这个目标:

一、先搞懂T4模板的核心逻辑

T4本质是一种文本生成工具,它能通过C#代码读取数据库元数据(表结构、字段、主键这些),然后根据你定义的模板格式,批量生成对应的代码文件。核心思路就是:读取数据库结构 → 填充到预定义的代码模板 → 输出完整的应用代码包。

二、分步实现完整CRUD生成包

1. 第一步:读取数据库元数据(核心基础)

要生成对应表的代码,首先得让T4能连接你的数据库,获取表、列、主键等信息。下面是一段基础的T4代码示例,用来读取SQL Server的表结构:

<#@ assembly name="System.Data" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ output extension=".cs" #>

<#
// 替换成你的数据库连接字符串
string connectionString = @"Server=.;Database=YourTargetDB;Trusted_Connection=True;";
// 替换成你的应用名称
string appName = "YourAwesomeApp";
List<string> tableNames = new List<string>();

using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    // 只获取用户自定义的基础表,排除系统表
    DataTable tables = conn.GetSchema("Tables", new string[] { null, null, null, "BASE TABLE" });
    
    foreach (DataRow tableRow in tables.Rows)
    {
        string tableName = tableRow["TABLE_NAME"].ToString();
        // 跳过系统表(比如sys开头的)
        if (tableName.StartsWith("sys")) continue;
        tableNames.Add(tableName);
        
        // 获取当前表的列信息
        DataTable columns = conn.GetSchema("Columns", new string[] { null, null, tableName });
        // 获取当前表的主键信息
        DataTable primaryKeys = conn.GetSchema("PrimaryKeys", new string[] { null, null, tableName });
        string pkColumnName = primaryKeys.Rows.Count > 0 ? primaryKeys.Rows[0]["COLUMN_NAME"].ToString() : "Id";
        
        // 接下来就用这些元数据生成各类代码
        GenerateModel(tableName, columns, appName);
        GenerateRepository(tableName, pkColumnName, appName);
        GenerateController(tableName, pkColumnName, appName);
    }
    // 生成DbContext
    GenerateDbContext(appName, tableNames);
}

// 辅助方法:把SQL数据类型转成C#类型
string GetCSharpType(string sqlType, object maxLength)
{
    return sqlType switch
    {
        "int" => "int",
        "varchar" or "nvarchar" => "string",
        "datetime" or "datetime2" => "DateTime",
        "bit" => "bool",
        "uniqueidentifier" => "Guid",
        "bigint" => "long",
        // 可以根据你的数据库类型扩展更多映射
        _ => "object"
    };
}

// 生成实体Model的方法
void GenerateModel(string tableName, DataTable columns, string appName)
{
#>
namespace <#= appName #>.Models
{
    public class <#= tableName #>
    {
<# foreach (DataRow col in columns.Rows) {
    string colName = col["COLUMN_NAME"].ToString();
    string csharpType = GetCSharpType(col["DATA_TYPE"].ToString(), col["CHARACTER_MAXIMUM_LENGTH"]);
#>
        public <#= csharpType #> <#= colName #> { get; set; }
<# } #>
    }
}
<#
}
#>

这段代码的核心是通过SqlConnection.GetSchema获取数据库的元数据,然后调用不同的生成方法输出各类文件。

2. 生成Repository层(数据访问逻辑)

接着写GenerateRepository方法,生成包含CRUD异步方法的Repository,和VS样板的风格保持一致:

<#
// 生成Repository的方法
void GenerateRepository(string tableName, string pkName, string appName)
{
    string pkType = GetCSharpType(columns.Rows.Find(row => row["COLUMN_NAME"].ToString() == pkName)["DATA_TYPE"].ToString(), null);
#>
namespace <#= appName #>.Repositories
{
    public class <#= tableName #>Repository
    {
        private readonly <#= appName #>.Data.AppDbContext _context;

        public <#= tableName #>Repository(<#= appName #>.Data.AppDbContext context)
        {
            _context = context;
        }

        public async Task<<#= tableName #>> GetByIdAsync(<#= pkType #> id)
        {
            return await _context.<#= tableName #>.FindAsync(id);
        }

        public async Task<IEnumerable<<#= tableName #>>> GetAllAsync()
        {
            return await _context.<#= tableName #>.ToListAsync();
        }

        public async Task CreateAsync(<#= tableName #> entity)
        {
            _context.<#= tableName #>.Add(entity);
            await _context.SaveChangesAsync();
        }

        public async Task UpdateAsync(<#= tableName #> entity)
        {
            _context.Entry(entity).State = EntityState.Modified;
            await _context.SaveChangesAsync();
        }

        public async Task DeleteAsync(<#= pkType #> id)
        {
            var entity = await GetByIdAsync(id);
            if (entity != null)
            {
                _context.<#= tableName #>.Remove(entity);
                await _context.SaveChangesAsync();
            }
        }
    }
}
<#
}
#>

这里默认用EF Core作为数据访问框架,你也可以改成ADO.NET或者其他ORM,只需要调整方法内的实现逻辑。

3. 生成API Controller(带完整端点)

然后是GenerateController方法,生成类似Account Controller的API控制器,包含标准的HTTP端点:

<#
// 生成Controller的方法
void GenerateController(string tableName, string pkName, string appName)
{
    string pkType = GetCSharpType(columns.Rows.Find(row => row["COLUMN_NAME"].ToString() == pkName)["DATA_TYPE"].ToString(), null);
#>
using Microsoft.AspNetCore.Mvc;
using <#= appName #>.Models;
using <#= appName #>.Repositories;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace <#= appName #>.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class <#= tableName #>Controller : ControllerBase
    {
        private readonly <#= tableName #>Repository _repository;

        public <#= tableName #>Controller(<#= tableName #>Repository repository)
        {
            _repository = repository;
        }

        /// <summary>获取所有<#= tableName #>数据</summary>
        [HttpGet]
        public async Task<ActionResult<IEnumerable<<#= tableName #>>>> GetAll()
        {
            var items = await _repository.GetAllAsync();
            return Ok(items);
        }

        /// <summary>根据ID获取单个<#= tableName #></summary>
        [HttpGet("{id}")]
        public async Task<ActionResult<<#= tableName #>>> GetById(<#= pkType #> id)
        {
            var item = await _repository.GetByIdAsync(id);
            if (item == null)
            {
                return NotFound();
            }
            return item;
        }

        /// <summary>创建新的<#= tableName #></summary>
        [HttpPost]
        public async Task<IActionResult> Create(<#= tableName #> entity)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            await _repository.CreateAsync(entity);
            return CreatedAtAction(nameof(GetById), new { id = entity.<#= pkName #> }, entity);
        }

        /// <summary>更新<#= tableName #>数据</summary>
        [HttpPut("{id}")]
        public async Task<IActionResult> Update(<#= pkType #> id, <#= tableName #> entity)
        {
            if (!id.Equals(entity.<#= pkName #>))
            {
                return BadRequest();
            }
            await _repository.UpdateAsync(entity);
            return NoContent();
        }

        /// <summary>删除<#= tableName #>数据</summary>
        [HttpDelete("{id}")]
        public async Task<IActionResult> Delete(<#= pkType #> id)
        {
            await _repository.DeleteAsync(id);
            return NoContent();
        }
    }
}
<#
}
#>

这段代码生成的控制器包含了标准的RESTful端点,还加上了XML注释和基本的参数校验,和VS自带的样板风格一致。

4. 生成应用配置与引用

最后,你还可以在模板里生成一些基础配置文件的片段:

<#
// 生成DbContext的方法
void GenerateDbContext(string appName, List<string> tableNames)
{
#>
using Microsoft.EntityFrameworkCore;
using <#= appName #>.Models;

namespace <#= appName #>.Data
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
        {
        }
<# foreach (string tableName in tableNames) { #>
        public DbSet<<#= tableName #>> <#= tableName #> { get; set; }
<# } #>
    }
}
<#
}
#>

另外还可以生成Program.cs里的依赖注入代码片段,比如:

// 注册DbContext和Repository
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<ProductRepository>();
// 其他Repository注册...
三、优化模板,让它更贴近VS样板的体验
  • 动态命名空间:通过参数传入应用名称,自动生成对应命名空间(比如YourApp.Models、YourApp.Controllers)
  • 处理主键类型:自动识别主键的数据类型,动态调整方法参数(比如GetByIdAsync(Guid id))
  • 添加异步支持:默认生成异步方法,符合ASP.NET Core的最佳实践
  • 注释自动生成:给方法和属性添加XML注释,方便后续开发
  • 过滤系统表:自动跳过sys开头的系统表,只生成用户自定义表的代码
  • 依赖注入配置:生成Program.cs里的服务注册代码,减少手动配置的工作量
四、运行模板的步骤
  1. 在Visual Studio里新建一个T4文件(右键项目 → 添加 → 新建项 → 搜索“Text Template”)
  2. 把上面的代码整合到.tt文件里,替换你的数据库连接字符串和应用名称
  3. 右键T4文件,选择“Run Custom Tool”,VS就会自动生成所有对应的代码文件
  4. 如果需要批量生成多个表的代码,模板会自动遍历数据库里的所有用户表,生成对应的Model、Repository和Controller
五、进阶技巧:让模板更灵活
  • 添加T4参数:在模板开头添加<#@ parameter name="AppName" type="System.String" #>,这样可以在运行模板时传入参数,不用每次修改模板代码
  • 生成DTO类:除了实体类,还可以生成用于API请求/响应的DTO,避免直接暴露数据库实体
  • 处理外键关联:读取外键信息,自动生成实体的导航属性,以及关联的CRUD逻辑
  • 自定义模板片段:把常用的代码片段抽成单独的T4模板文件,通过<#@ include file="CommonTemplates.ttinclude" #>引用,方便维护

内容的提问来源于stack exchange,提问作者I Love Stackoverflow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:00:56