# NVelocity中怎么實現代碼生成功能
## 一、NVelocity簡介
### 1.1 什么是NVelocity
NVelocity是Apache Velocity模板引擎的.NET移植版本,它是一個基于Java的模板引擎的C#實現。作為一款強大的模板引擎,NVelocity允許開發者將業務邏輯與展示層分離,通過簡單的模板語法實現動態內容生成。
### 1.2 NVelocity的核心特性
- **模板與代碼分離**:保持業務邏輯與顯示邏輯的獨立性
- **簡單語法**:類似HTML的模板語法,學習成本低
- **高性能**:模板編譯后執行效率高
- **可擴展性**:支持自定義指令和擴展點
- **多領域應用**:適用于網頁生成、代碼生成、郵件模板等場景
### 1.3 代碼生成的應用場景
- 數據庫實體類自動生成
- 重復性高的CRUD代碼生成
- 項目腳手架創建
- 協議代碼生成(如gRPC、Thrift)
- 自動化測試用例生成
## 二、環境準備與基礎配置
### 2.1 安裝NVelocity
通過NuGet包管理器安裝最新版本:
```bash
Install-Package NVelocity
或使用.NET CLI:
dotnet add package NVelocity
using Commons.Collections;
using NVelocity.App;
using NVelocity.Runtime;
// 初始化引擎
var props = new ExtendedProperties();
props.AddProperty(RuntimeConstants.RESOURCE_LOADER, "file");
props.AddProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
Path.Combine(Directory.GetCurrentDirectory(), "Templates"));
var velocityEngine = new VelocityEngine();
velocityEngine.Init(props);
ProjectRoot/
├── Templates/ # 模板存放目錄
│ ├── Entities/ # 實體類模板
│ ├── Repositories/ # 倉儲接口模板
│ └── Services/ # 服務類模板
├── Generated/ # 生成代碼輸出目錄
└── CodeGenerator.cs # 生成器主程序
## 單行注釋
#*
多行注釋
*#
#set($name = "value") ## 變量定義
${variable} ## 變量輸出
#set($userName = "John Doe")
#set($age = 30)
#set($isAdmin = true)
用戶信息:${userName},年齡:$age
#if($isAdmin)
(管理員權限)
#end
#if($condition)
...
#elseif($otherCondition)
...
#else
...
#end
#foreach($item in $items)
當前項:$item
#if($foreach.hasNext),#end
#end
#macro(renderUser $user)
<div class="user">
<span>$!user.Name</span>
<span>$!user.Age</span>
</div>
#end
## 使用宏
#renderUser($currentUser)
using System;
namespace ${nameSpace}.Entities
{
/// <summary>
/// ${tableComment}
/// </summary>
public class ${entityName}
{
#foreach($column in $columns)
/// <summary>
/// ${column.Comment}
/// </summary>
public ${column.Type} ${column.Name} { get; set; }
#end
}
}
var context = new VelocityContext();
context.Put("nameSpace", "MyProject");
context.Put("entityName", "User");
context.Put("tableComment", "用戶信息表");
var columns = new List<object> {
new { Name = "Id", Type = "int", Comment = "主鍵ID" },
new { Name = "UserName", Type = "string", Comment = "用戶名" },
// 其他字段...
};
context.Put("columns", columns);
var writer = new StringWriter();
velocityEngine.MergeTemplate("Entities/EntityTemplate.vm", Encoding.UTF8.HeaderName, context, writer);
File.WriteAllText("Generated/User.cs", writer.ToString());
using System.Collections.Generic;
namespace ${nameSpace}.Repositories
{
public interface I${entityName}Repository
{
${entityName} GetById(int id);
IEnumerable<${entityName}> GetAll();
void Add(${entityName} entity);
void Update(${entityName} entity);
void Delete(int id);
}
}
using ${nameSpace}.Entities;
using ${nameSpace}.Repositories;
namespace ${nameSpace}.Services
{
public class ${entityName}Service
{
private readonly I${entityName}Repository _repository;
public ${entityName}Service(I${entityName}Repository repository)
{
_repository = repository;
}
public ${entityName} GetById(int id) => _repository.GetById(id);
// 其他CRUD方法...
#foreach($method in $customMethods)
public ${method.ReturnType} ${method.Name}(${method.Parameters})
{
// 自定義方法實現
throw new System.NotImplementedException();
}
#end
}
}
## 基礎模板(BaseTemplate.vm)
<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
#parse("Includes/Header.vm")
</head>
<body>
#parse($contentTemplate)
</body>
</html>
## 子模板
#set($title = "用戶管理")
#set($contentTemplate = "User/List.vm")
#parse("Templates/BaseTemplate.vm")
public class UpperDirective : NVelocity.Runtime.Directive.Directive
{
public override string Name => "upper";
public override DirectiveType Type => DirectiveType.LINE;
public override bool Render(IInternalContextAdapter context, TextWriter writer, INode node)
{
var param = node.GetChild(0).Value(context).ToString();
writer.Write(param.ToUpper());
return true;
}
}
// 注冊指令
props.AddProperty(RuntimeConstants.CUSTOM_DIRECTIVES, "upper,Namespace.UpperDirective");
模板緩存:啟用模板緩存減少IO
props.AddProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE, "true");
props.AddProperty(RuntimeConstants.RESOURCE_MANAGER_CACHE_ENABLED, "true");
預編譯常用模板:對高頻使用模板進行預編譯
合理設計模板結構:避免過度嵌套和復雜邏輯
try
{
velocityEngine.MergeTemplate(templateName, Encoding.UTF8.HeaderName, context, writer);
}
catch (ResourceNotFoundException ex)
{
// 處理模板未找到
}
catch (ParseErrorException ex)
{
// 處理語法錯誤
}
catch (MethodInvocationException ex)
{
// 處理方法調用異常
}
using Microsoft.EntityFrameworkCore;
using ${nameSpace}.Entities;
namespace ${nameSpace}.Data
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options) { }
#foreach($entity in $entities)
public DbSet<${entity}> ${entity}Set { get; set; }
#end
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 配置實體關系...
}
}
}
using Microsoft.AspNetCore.Mvc;
using ${nameSpace}.Services;
namespace ${nameSpace}.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class ${entityName}Controller : ControllerBase
{
private readonly ${entityName}Service _service;
public ${entityName}Controller(${entityName}Service service)
{
_service = service;
}
[HttpGet("{id}")]
public IActionResult Get(int id)
{
var entity = _service.GetById(id);
return Ok(entity);
}
// 其他API方法...
}
}
using Xunit;
using ${nameSpace}.Services;
using Moq;
namespace ${nameSpace}.Tests
{
public class ${entityName}ServiceTests
{
private readonly ${entityName}Service _service;
private readonly Mock<I${entityName}Repository> _mockRepo;
public ${entityName}ServiceTests()
{
_mockRepo = new Mock<I${entityName}Repository>();
_service = new ${entityName}Service(_mockRepo.Object);
}
[Fact]
public void GetById_ShouldReturnEntity_WhenExists()
{
// 測試代碼...
}
// 其他測試方法...
}
}
問題現象:收到ResourceNotFoundException
- 檢查文件路徑是否正確
- 確認FILE_RESOURCE_LOADER_PATH
配置
- 驗證模板文件是否存在且有讀取權限
使用$!variable
語法可避免變量為空時輸出:
Hello $!userName ## 當userName為null時輸出"Hello "而不是"Hello null"
#set($dollar = "$")
輸出美元符號:${dollar}
#set($hash = "#")
輸出井號:${hash}
Stopwatch
監控模板渲染時間通過合理應用NVelocity,開發者可以顯著提升代碼編寫效率,將精力集中在核心業務邏輯的實現上。建議從簡單模板開始,逐步構建復雜的代碼生成系統。 “`
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。