.NET 6 Core Web API能否生成带CRUD的服务类与接口类?
生成.NET 6 Web API CRUD服务类与接口的几种方案
针对你的场景,有几种高效的自动生成方式,能避免重复编码:
1. 自定义dotnet-aspnet-codegenerator模板
默认的dotnet-aspnet-codegenerator只生成控制器,但你可以自定义模板来同时生成服务接口和实现类:
- 找到模板路径:通常在
%USERPROFILE%\.dotnet\tools\.store\dotnet-aspnet-codegenerator\<版本>\content\Templates\AspNetCore - 复制现有控制器模板,新增服务接口(如
I{{ModelName}}Service.cs)和服务实现类({{ModelName}}Service.cs)的模板,写入基础CRUD方法逻辑(GetAll、GetById、Create、Update、Delete) - 替换模板中的占位符为实体类名、主键属性等动态内容
- 运行原控制器生成命令时,就能同步生成对应的服务类和接口
2. T4文本模板
T4是.NET原生代码生成工具,适合自定义生成逻辑:
- 在项目中添加T4模板文件(如
ServiceGenerator.tt) - 通过反射读取EF实体类和DbContext,自动生成接口与服务类代码
- 示例模板片段:
<#@ template language="C#" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Reflection" #> <#@ import namespace="System.Linq" #> <# var assembly = Assembly.LoadFrom(@"bin\Debug\net6.0\YourProject.dll"); var entityTypes = assembly.GetTypes().Where(t => t.Namespace == "YourProject.Entities" && !t.IsAbstract); foreach (var entityType in entityTypes) { var entityName = entityType.Name; var primaryKey = entityType.GetProperties().First(p => p.Name.EndsWith("Id")); #> namespace YourProject.Services { public interface I<#= entityName #>Service { Task<IEnumerable<<#= entityName #>>> GetAllAsync(); Task<<#= entityName #>?> GetByIdAsync(<#= primaryKey.PropertyType.Name #> id); Task<<#= entityName #>> CreateAsync(<#= entityName #> entity); Task UpdateAsync(<#= entityName #> entity); Task DeleteAsync(<#= primaryKey.PropertyType.Name #> id); } public class <#= entityName #>Service : I<#= entityName #>Service { private readonly YourDbContext _context; public <#= entityName #>Service(YourDbContext context) { _context = context; } public async Task<IEnumerable<<#= entityName #>>> GetAllAsync() { return await _context.<#= entityName #>s.ToListAsync(); } // 其余CRUD方法按相同逻辑生成 } } <# } #>
- 保存模板后,VS会自动生成对应代码,实体更新时重新运行模板即可同步更新
3. 开源代码生成工具
有不少开源工具专门适配.NET CRUD代码生成:
- Roslyn源代码生成器:编写生成器集成到编译流程,编译时自动为EF实体生成服务接口与实现类,实体更新时自动同步代码
- 第三方脚手架扩展:部分NuGet包在默认脚手架基础上增加了服务类生成功能,安装后通过命令行即可快速生成
4. 简易控制台生成工具
如果上述方法太复杂,可快速写一个控制台程序:
- 引用你的Web API项目
- 通过反射读取DbContext中的
DbSet属性,获取所有实体类型 - 用字符串拼接生成服务接口和实现类代码,写入项目目录
- 核心逻辑示例:
var dbContextType = typeof(YourDbContext); var dbSets = dbContextType.GetProperties() .Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)); foreach (var dbSet in dbSets) { var entityType = dbSet.PropertyType.GetGenericArguments()[0]; var entityName = entityType.Name; var primaryKey = entityType.GetProperties().First(p => p.Name.EndsWith("Id")); // 拼接接口代码 var interfaceCode = $@"using System.Collections.Generic; using System.Threading.Tasks; using YourProject.Entities; namespace YourProject.Services {{ public interface I{entityName}Service {{ Task<IEnumerable<{entityName}>> GetAllAsync(); Task<{entityName}?> GetByIdAsync({primaryKey.PropertyType.Name} id); Task<{entityName}> CreateAsync({entityName} entity); Task UpdateAsync({entityName} entity); Task DeleteAsync({primaryKey.PropertyType.Name} id); }} }}"; // 拼接服务实现代码 var serviceCode = $@"using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using YourProject.Entities; using YourProject.Data; namespace YourProject.Services {{ public class {entityName}Service : I{entityName}Service {{ private readonly YourDbContext _context; public {entityName}Service(YourDbContext context) {{ _context = context; }} public async Task<IEnumerable<{entityName}>> GetAllAsync() {{ return await _context.{entityName}s.ToListAsync(); }} public async Task<{entityName}?> GetByIdAsync({primaryKey.PropertyType.Name} id) {{ return await _context.{entityName}s.FindAsync(id); }} public async Task<{entityName}> CreateAsync({entityName} entity) {{ _context.{entityName}s.Add(entity); await _context.SaveChangesAsync(); return entity; }} public async Task UpdateAsync({entityName} entity) {{ _context.Entry(entity).State = EntityState.Modified; await _context.SaveChangesAsync(); }} public async Task DeleteAsync({primaryKey.PropertyType.Name} id) {{ var entity = await _context.{entityName}s.FindAsync(id); if (entity != null) {{ _context.{entityName}s.Remove(entity); await _context.SaveChangesAsync(); }} }} }} }}"; // 写入文件 Directory.CreateDirectory("../YourProject/Services"); File.WriteAllText($"../YourProject/Services/I{entityName}Service.cs", interfaceCode); File.WriteAllText($"../YourProject/Services/{entityName}Service.cs", serviceCode); }
注意事项
- 生成代码后,需在
Program.cs中注册服务:builder.Services.AddScoped<I{{EntityName}}Service, {{EntityName}}Service>();,也可通过反射批量注册所有服务 - 若实体有特殊业务逻辑,生成后手动调整即可,基础CRUD部分可完全自动化
内容的提问来源于stack exchange,提问作者ErickTreetops
相关产品推荐
相关产品推荐

