【开源】跨语言·跨平台·跨数据库(1) —— 用宝技术轻量框架 AI 开发指南
把本文档直接丢给 AI,即可自动生成 C#/Java/ArkTS 三端完整 CRUD 代码
💡 一句话说清本文价值:把这份文档全文复制给 AI,它就能根据你的表结构,自动生成 C#、Java、华为鸿蒙ArkTS 三端完整 CRUD 代码。我们的底层架构是封装好的 DLL 可直接调用,也可以根据文档直接生成。同理,Java、ArkTS 都可以直接生成底层、WebAPI、服务层及前端全部代码。
作者:周方勇/咏方舟-长江支流(金质打印通、用宝框架开源作者)
简述:用宝框架(UserBaoTech)是一套轻量级企业级分层架构基架,.NET Standard 2.0,零依赖,支持 MySQL/SQL Server/Oracle/SQLite,SQL就是最好的跨平台,依据标准 SQL 自动生成,可跨语言无缝迁移至鸿蒙 ArkTS 及 Java 技术栈,接口名、类名、方法签名三端完全一致。

🔁 跨语言迁移能力说明
核心结论:完全一样,不是"接近",不是"类似"
用宝框架的设计与语言无关,其核心是一组接口契约 + 模板方法 + 手写 SQL 的抽象体系。从 Java 的语言能力和我们现有 C# 代码的写法来看,可以实现 接口名、类名、方法签名三端完全一样。 点击AI前端生成产品CRUD见博文。
具体表现在以下几个方面:
1. 语法层面:完全对等
| C#(用宝框架) | Java 对应 | ArkTS 对应 |
|---|---|---|
IEntity<TKey> | IEntity<TKey> | IEntity<TKey> |
IRepository<T> | IRepository<T> | IRepository<T> |
RepositoryBase<T> | RepositoryBase<T> | RepositoryBase<T> |
ServiceBase<TParam,TResult> | ServiceBase<TParam,TResult> | ServiceBase<TParam,TResult> |
KeyServiceBase<TResult> | KeyServiceBase<TResult> | KeyServiceBase<TResult> |
MessageResult<T> | MessageResult<T> | MessageResult<T> |
QueryParametersBase | QueryParametersBase | QueryParametersBase |
IDataAccessExecutor | IDataAccessExecutor | IDataAccessExecutor |
async/await Task<T> | CompletableFuture<T> | Promise<T> |
Dictionary<string, object> | Map<String, Object> | Record<string, object> |
using(var ...) | try-with-resources | using(ArkTS 支持) |
2. 架构层面:设计模式完全移植
用宝框架的核心设计模式:
- 仓储模式(Repository)
- 工作单元(UnitOfWork)
- 模板方法模式(ServiceBase / KeyServiceBase)
- 依赖注入(DI)
这些模式在 Java 和 ArkTS 中都有对应的标准实现方式,且写法几乎一模一样。
3. 数据访问层:只有底层驱动不同
| 层 | C# 用宝框架 | Java 对应 | ArkTS 对应 |
|---|---|---|---|
| 接口抽象 | IDataAccessExecutor | IDataAccessExecutor | IDataAccessExecutor |
| 连接对象 | IDbConnection | Connection(JDBC) | relationalStore |
| 命令对象 | IDbCommand | PreparedStatement | relationalStore API |
| 读取器 | IDataReader | ResultSet | ResultSet |
| 参数占位符 | @参数名 | ? 或 #{}(MyBatis) | ?(适配中) |
| 事务管理 | IDbTransaction | Connection.setAutoCommit(false) | relationalStore 事务 |
| 方言处理 | switch(DatabaseType) | switch(DatabaseType) | switch(DatabaseType) |
除了底层驱动和占位符写法外,上层业务代码一字不改。
4. 代码可直接"翻译",结构完全一样
以 ProductRepository 为例:
C#:
protected override string GetTableNameCore() => "Products";
protected override Product MapToEntity(IDataReader reader) { ... }
protected override IDictionary<string, object> GetInsertValues(Product entity) { ... }
Java:
@Override
protected String getTableNameCore() { return "Products"; }
@Override
protected Product mapToEntity(ResultSet rs) { ... }
@Override
protected Map<String, Object> getInsertValues(Product entity) { ... }
ArkTS:
protected getTableNameCore(): string { return "Products"; }
protected mapToEntity(rs: ResultSet): Product { ... }
protected getInsertValues(entity: Product): Record<string, object> { ... }
结构完全一样,只是语法大小写和数据类型名称不同。但完全可以和 C# 保持完全一样的统一命名风格。
5. 迁移后的一致性体验
用户拿到 Java 版或 ArkTS 版用宝框架后,看到的会是:
| 维度 | 三端一致的内容 |
|---|---|
| 接口名 | IRepository<T>、IEntity<TKey>、IService<TParam, TResult> |
| 基类名 | RepositoryBase<T>、ServiceBase<TParam, TResult>、KeyServiceBase<TResult> |
| 返回结构 | MessageResult<T> |
| 查询参数 | QueryParametersBase |
| 业务写法 | GetXxxListService、GetXxxByIdKeyService、DeleteXxxKeyService |
| Controller 风格 | C#: [HttpGet("{id}")] / Java: @GetMapping("/{id}") / ArkTS: 装饰器风格 |
也就是说,一个熟悉用宝框架 C# 版的开发者,切换到 Java 或 ArkTS 版时,只需要适应语言语法差异,架构认知完全迁移,学习成本几乎为零。
总结
用宝框架的代码可以无缝对应到 Java 和 ArkTS:
- 接口/类名:完全相同
- 继承关系:完全相同
- 方法签名:完全相同
- 业务写法:完全相同
- 底层驱动:仅替换数据访问实现
完全一样,不是"接近",不是"类似"。这是用宝框架作为"用户宝贝技术"的核心价值所在。
💡 把这份文档全文复制给 AI,它就能根据你的表结构,自动生成 C#、Java、ArkTS 三端完整 CRUD 代码。框架底层已封装好,也可根据文档直接生成。一次设计,三端共用。
📌 本文档使用说明
用宝技术轻量框架是一个已编译好的现成类库(.NET Standard 2.0),作为 NuGet 包或 DLL 直接引用即可,不需要新建或生成其源码。
你只需:
- 新建
YourProject.Core(类库 .NET Standard 2.0) - 新建
YourProject.WebAPI(ASP.NET Core .NET 10) - 通过 NuGet 引用
UserBaoTech.Foundation(用宝框架)
然后,把本文档全文复制给 AI(或新对话框),AI 会根据你提供的表结构自动生成完整模块代码(Entity → Query → Repository → Service → Controller),无需再沟通架构背景。
⚠️ 特别说明:文档内的接口和基类代码仅供理解底层机制,不是你要手动编写的内容。用宝框架已提供全部基架实现,你只需写业务代码。
目录
- 第一部分:架构总览
- 第二部分:从零创建项目
- 第三部分:核心接口与基类
- 第四部分:主键策略(三种模式)
- 第五部分:服务层(Service)
- 第六部分:查询构建器(QueryBuilder)
- 第七部分:分页索引转换规则(重要)
- 第八部分:统一返回格式(MessageResult)
- 第九部分:依赖注入注册
- 第十部分:新模块开发标准流程(5 步法)
- 第十一部分:禁止事项(红线)
- 第十二部分:完整 Product 模块代码模板
- 附录一:Product 模块完整代码
好的,现在开始输出 《UserBaoTech.Foundation 架构开发文档 · 最终完整版》。
UserBaoTech.Foundation 架构开发文档
最终完整版
版本:v3.0
发布日期:2026年7月11日
适用范围:所有基于 UserBaoTech.Foundation 的新项目开发
阅读对象:开发人员、AI 编程助手、新项目初始化人员
第一部分:架构总览
1.1 这是什么?
UserBaoTech.Foundation 是一套轻量级企业级分层架构基架,核心目标:
- 提供一套永不淘汰的底层数据访问框架(基于 .NET Standard 2.0)
- 让业务代码干净、可测试、可跨语言迁移(C# ↔ 鸿蒙 ArkTS ↔ Java)
- 不依赖任何重量级 ORM(无 EF、无 Dapper),手写 SQL,完全可控
- 数据库方言适配(MySQL / SQL Server / Oracle / SQLite)自动处理
1.2 核心设计哲学
| 原则 | 说明 |
|---|---|
| SQL 是唯一标准 | 手写 SQL,确保跨数据库、跨语言(C#/ArkTS/Java)完全一致 |
| Foundation 是只读基架 | 底层代码视为三方组件,业务层绝不越界修改 |
| 分层隔离 | 业务层仅依赖接口(IRepository、IService),不依赖具体实现 |
| 主键统一为 string | 兼容所有数据库(int/GUID/字符串),避免类型转换风险 |
| 参数前缀自动适配 | 业务层写 @参数名,执行器自动转换为 :(Oracle)或 ?(ODBC) |
1.3 三个项目边界
| 项目 | 定位 | 目标框架 | 依赖 |
|---|---|---|---|
UserBaoTech.Foundation | 公司级技术基架(只读) | .NET Standard 2.0 | 零三方依赖 |
YourProject.Core | 业务领域层(可写) | .NET Standard 2.0 | 引用 Foundation |
YourProject.WebAPI | 接入层(可写) | .NET 10 | 引用 Foundation + Core |
1.4 整体分层架构图
┌─────────────────────────────────────────────────────┐
│ 表现层(WebAPI / WinForms / MAUI) │
│ - Controller 接收 HTTP 请求 │
│ - 依赖注入 Service,不直接 new │
│ - 返回 MessageResult<T> 统一格式 │
└─────────────────────────────────────────────────────┘
│ 调用
▼
┌─────────────────────────────────────────────────────┐
│ 业务服务层(Core.Services) │
│ - 继承 ServiceBase<TParam, TResult> │
│ - 单参数主键场景继承 KeyServiceBase<TResult> │
│ - 负责业务校验、事务控制、调用仓储 │
│ - 纯 C# 逻辑,无 .NET 特有类型(可迁移 ArkTS/Java)│
└─────────────────────────────────────────────────────┘
│ 依赖接口
▼
┌─────────────────────────────────────────────────────┐
│ 仓储接口层(Foundation.Data.IRepository<T>) │
│ - 定义 CRUD 契约(Query/GetById/Insert/Update/Delete)│
│ - 单泛型默认 string 主键,双泛型支持任意主键类型 │
└─────────────────────────────────────────────────────┘
│ 实现
▼
┌─────────────────────────────────────────────────────┐
│ 仓储实现层(Core.Data.XxxRepository) │
│ - 继承 RepositoryBase<TEntity> │
│ - 必须实现:表名、MapToEntity、GetInsertValues、GetUpdateValues│
│ - 可重写:Delete(软删除)、GenerateId、NeedGetIdentityAfterInsert│
└─────────────────────────────────────────────────────┘
│ 内部使用
▼
┌─────────────────────────────────────────────────────┐
│ 轻量数据执行器(Foundation.DataAccessExecutor) │
│ - 封装 ADO.NET(IDbConnection/IDbCommand/IDataReader)│
│ - 统一处理方言:参数前缀(@/:)、分页 SQL、自增 ID 回读│
│ - 上层(Repository)完全感知不到数据库差异 │
└─────────────────────────────────────────────────────┘
│ 连接
▼
┌─────────────────────────────────────────────────────┐
│ 物理数据库(MySQL / SQL Server / Oracle / SQLite)│
└─────────────────────────────────────────────────────┘
第二部分:从零创建项目
2.1 Visual Studio 2026 操作步骤
步骤 1:创建空白解决方案
打开 Visual Studio 2026 → 新建项目 → 搜索"空白解决方案" → 命名为 YourProject.sln → 创建。
步骤 2:添加类库项目(Core)
右键解决方案 → 添加 → 新建项目 → 搜索"类库 (.NET Standard)" → 命名为 YourProject.Core → 目标框架选择 .NET Standard 2.0 → 创建。
步骤 3:添加 WebAPI 项目
右键解决方案 → 添加 → 新建项目 → 搜索"ASP.NET Core Web API" → 命名为 YourProject.WebAPI → 目标框架 .NET 10 → 创建。
步骤 4:添加项目引用
右键 YourProject.WebAPI → 添加 → 项目引用 → 勾选 YourProject.Core → 确定。
步骤 5:测试 API 连通性
在 YourProject.WebAPI 的 Controllers 文件夹下创建 TestController.cs:
using Microsoft.AspNetCore.Mvc;
namespace YourProject.WebAPI.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
[HttpGet("ping")]
public IActionResult Ping()
{
return Ok(new { message = "API works!" });
}
}
按 F5 启动,访问 https://localhost:xxxx/api/test/ping,返回 JSON 即成功。
第三部分:核心接口与基类
3.1 实体接口:IEntity
所有实体必须实现此接口。
using System;
namespace UserBaoTech.Foundation.Entities
{
public interface IEntity<TKey>
{
TKey Id { get; set; }
DateTime CreatedAt { get; set; }
DateTime UpdatedAt { get; set; }
bool IsDeleted { get; set; } // 软删除标记
}
}
实体实现示例:
using System;
using UserBaoTech.Foundation.Entities;
namespace YourProject.Core.Entities
{
public class Product : IEntity<string>
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsDeleted { get; set; }
}
}
3.2 查询参数基类:QueryParametersBase
提供分页、排序公共属性,子类仅需实现 GetFilters()。
using System.Collections.Generic;
namespace UserBaoTech.Foundation.Data
{
public abstract class QueryParametersBase : IQueryParameters
{
public int PageIndex { get; set; } = 1; // 从 1 开始
public int PageSize { get; set; } = 20;
public string OrderBy { get; set; } = "CreatedAt";
public bool IsAscending { get; set; } = false;
public abstract IDictionary<string, object> GetFilters();
}
}
查询参数实现示例:
using System.Collections.Generic;
using UserBaoTech.Foundation.Data;
namespace YourProject.Core.Models
{
public class ProductQuery : QueryParametersBase
{
public string Name { get; set; }
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
public bool? IsDeleted { get; set; }
public override IDictionary<string, object> GetFilters()
{
var dict = new Dictionary<string, object>();
if (!string.IsNullOrEmpty(Name))
dict["Name LIKE"] = $"%{Name}%";
if (MinPrice.HasValue)
dict["Price >= "] = MinPrice.Value;
if (MaxPrice.HasValue)
dict["Price <= "] = MaxPrice.Value;
if (IsDeleted.HasValue)
dict["IsDeleted"] = IsDeleted.Value;
return dict;
}
}
}
3.3 仓储接口:IRepository
业务层仅依赖此接口。
using System.Collections.Generic;
using UserBaoTech.Foundation.Entities;
namespace UserBaoTech.Foundation.Data
{
public interface IRepository<TEntity> where TEntity : IEntity<string>
{
string GetTableName();
IEnumerable<TEntity> Query(string sql);
IEnumerable<TEntity> Query(string sql, IDictionary<string, object> parameters);
int Execute(string sql, IDictionary<string, object> parameters);
TEntity GetById(string id);
string Insert(TEntity entity);
int Update(TEntity entity);
int Delete(string id);
}
}
3.4 仓储基类:RepositoryBase
子类继承后仅需实现四个抽象方法。
必须实现的方法:
protected abstract string GetTableNameCore();
protected abstract TEntity MapToEntity(IDataReader reader);
protected abstract IDictionary<string, object> GetInsertValues(TEntity entity);
protected abstract IDictionary<string, object> GetUpdateValues(TEntity entity);
可重写的虚方法:
| 方法 | 默认值 | 说明 |
|---|---|---|
GenerateId() | Guid.NewGuid().ToString() | 程序生成主键 |
NeedGetIdentityAfterInsert | false | 是否插入后回读自增 ID |
GetIdFieldName() | "Id" | 主键字段名 |
Delete(string id) | 物理删除 | 可重写为软删除 |
仓储实现示例:
using System;
using System.Collections.Generic;
using System.Data;
using YourProject.Core.Entities;
using UserBaoTech.Foundation.Data;
namespace YourProject.Core.Data
{
public class ProductRepository : RepositoryBase<Product>
{
public ProductRepository(IDataAccessExecutor executor) : base(executor) { }
protected override string GetTableNameCore() => "Products";
protected override Product MapToEntity(IDataReader reader)
{
return new Product
{
Id = reader["Id"].ToString(),
Name = reader["Name"].ToString(),
Price = Convert.ToDecimal(reader["Price"]),
Stock = Convert.ToInt32(reader["Stock"]),
CreatedAt = Convert.ToDateTime(reader["CreatedAt"]),
UpdatedAt = Convert.ToDateTime(reader["UpdatedAt"]),
IsDeleted = Convert.ToBoolean(reader["IsDeleted"])
};
}
protected override IDictionary<string, object> GetInsertValues(Product entity)
{
return new Dictionary<string, object>
{
["Id"] = entity.Id,
["Name"] = entity.Name,
["Price"] = entity.Price,
["Stock"] = entity.Stock,
["CreatedAt"] = DateTime.Now,
["UpdatedAt"] = DateTime.Now,
["IsDeleted"] = false
};
}
protected override IDictionary<string, object> GetUpdateValues(Product entity)
{
return new Dictionary<string, object>
{
["Name"] = entity.Name,
["Price"] = entity.Price,
["Stock"] = entity.Stock,
["UpdatedAt"] = DateTime.Now,
["IsDeleted"] = entity.IsDeleted
};
}
// 软删除:重写 Delete 方法
public override int Delete(string id)
{
var sql = $"UPDATE {GetTableName()} SET IsDeleted = 1 WHERE Id = @Id";
return Execute(sql, new Dictionary<string, object> { { "@Id", id } });
}
}
}
第四部分:主键策略(三种模式)
4.1 模式一:程序生成 GUID(默认,推荐)
| 设置 | 值 |
|---|---|
GenerateId() | 默认(返回 GUID) |
NeedGetIdentityAfterInsert | false(默认) |
GetInsertValues 是否包含 Id | ✅ 包含 |
特点:无需回读数据库,性能最优,跨数据库兼容性最好。
使用方式:子类无需任何额外配置,基类默认即可。
4.2 模式二:自定义格式主键
| 设置 | 值 |
|---|---|
GenerateId() | 重写,返回自定义格式 |
NeedGetIdentityAfterInsert | false(默认) |
GetInsertValues 是否包含 Id | ✅ 包含 |
示例:生成 PROD_XXXXXXXX 格式主键
protected override string GenerateId()
{
return "PROD_" + Guid.NewGuid().ToString("N").Substring(0, 8).ToUpper();
}
4.3 模式三:数据库自增 INT
| 设置 | 值 |
|---|---|
GenerateId() | 返回 null |
NeedGetIdentityAfterInsert | true |
GetInsertValues 是否包含 Id | ❌ 不包含 |
特点:插入后自动执行 SELECT LAST_INSERT_ID() 回读 ID。
仓储实现示例:
public class ProductAutoIntRepository : ProductRepository
{
protected override string GetTableNameCore() => "Products_AutoInt";
protected override string GenerateId() => null;
protected override bool NeedGetIdentityAfterInsert => true;
protected override IDictionary<string, object> GetInsertValues(Product entity)
{
// 注意:不包含 Id 字段
return new Dictionary<string, object>
{
["Name"] = entity.Name,
["Price"] = entity.Price,
["Stock"] = entity.Stock,
["CreatedAt"] = DateTime.Now,
["UpdatedAt"] = DateTime.Now,
["IsDeleted"] = false
};
}
}
4.4 三种模式快速对比
| 模式 | 主键生成方 | 是否回读 | 适用场景 |
|---|---|---|---|
| 程序 GUID | 程序(C#) | ❌ 否 | 默认推荐,跨库兼容 |
| 自定义格式 | 程序(重写) | ❌ 否 | 需要业务前缀 |
| 自增 INT | 数据库 | ✅ 是 | 旧系统迁移或强制自增 |
第五部分:服务层(Service)
5.1 通用服务基类:ServiceBase<TParam, TResult>
封装日志、缓存、异常处理,子类仅需实现 DoExecuteAsync。
using System.Threading.Tasks;
using UserBaoTech.Foundation.Infrastructure;
namespace UserBaoTech.Foundation.Services
{
public abstract class ServiceBase<TParam, TResult> : IService<TParam, TResult>
where TParam : class
where TResult : class
{
protected readonly ILogger _logger;
protected readonly ICacheProvider _cache;
protected readonly IConfigProvider _config;
protected ServiceBase(ILogger logger, ICacheProvider cache, IConfigProvider config)
{
_logger = logger ?? new NullLogger();
_cache = cache ?? new NullCacheProvider();
_config = config ?? new MemoryConfigProvider();
}
public virtual async Task<TResult> ExecuteAsync(TParam request)
{
// 自动处理:日志记录、缓存读写、异常捕获
var result = await DoExecuteAsync(request);
return result;
}
protected abstract Task<TResult> DoExecuteAsync(TParam request);
}
}
5.2 主键操作专用基类:KeyServiceBase
针对"按 ID 查询/删除"场景,直接接收 KeyRequest。
using System.Threading.Tasks;
using UserBaoTech.Foundation.Data;
using UserBaoTech.Foundation.Entities;
using UserBaoTech.Foundation.Infrastructure;
namespace UserBaoTech.Foundation.Services
{
public abstract class KeyServiceBase<TResult> : ServiceBase<KeyRequest, TResult>
where TResult : class
{
protected KeyServiceBase(ILogger logger, ICacheProvider cache, IConfigProvider config)
: base(logger, cache, config) { }
protected sealed override async Task<TResult> DoExecuteAsync(KeyRequest request)
{
if (request == null || string.IsNullOrEmpty(request.Id))
throw new ArgumentException("主键不能为空");
return await DoExecuteAsync(request.Id);
}
protected abstract Task<TResult> DoExecuteAsync(string id);
// 默认删除实现(含钩子方法)
protected async Task<MessageResult<bool>> DeleteAsync<TEntity>(
IRepository<TEntity> repository, string id) where TEntity : IEntity<string>
{
var entity = repository.GetById(id);
var (canDelete, msg) = CanDeleteWithMessage(entity);
if (!canDelete)
return new MessageResult<bool> { IsSuccess = false, Message = msg, Code = 0 };
var rows = repository.Delete(id);
if (rows > 0)
{
var (success, successMsg) = OnDeletedWithMessage(entity);
return new MessageResult<bool> { IsSuccess = true, Message = successMsg ?? "删除成功", Data = true };
}
return new MessageResult<bool> { IsSuccess = false, Message = "删除失败", Code = 0 };
}
protected virtual (bool success, string message) CanDeleteWithMessage(IEntity<string> entity)
=> (entity != null && !entity.IsDeleted, entity == null ? "实体不存在" : "实体已被删除");
protected virtual (bool success, string message) OnDeletedWithMessage(IEntity<string> entity)
=> (true, null);
protected virtual (bool success, string message) OnDeleteFailedWithMessage(string id)
=> (false, null);
}
}
5.3 KeyRequest 统一主键请求
所有单参数主键操作统一使用 KeyRequest,不定义重复 DTO。
namespace UserBaoTech.Foundation.Services
{
public class KeyRequest
{
public string Id { get; set; }
}
}
5.4 服务实现示例
多参数场景(Create/Update/List): 继承 ServiceBase<YourRequest, MessageResult<T>>
using System.Threading.Tasks;
using YourProject.Core.DTOs.Products;
using YourProject.Core.Entities;
using UserBaoTech.Foundation.Data;
using UserBaoTech.Foundation.Infrastructure;
using UserBaoTech.Foundation.Services;
namespace YourProject.Core.Services.Products
{
public class CreateProductService : ServiceBase<CreateProductRequest, MessageResult<string>>
{
private readonly IRepository<Product> _repository;
public CreateProductService(
IRepository<Product> repository,
ILogger logger,
ICacheProvider cache,
IConfigProvider config) : base(logger, cache, config)
{
_repository = repository;
}
protected override async Task<MessageResult<string>> DoExecuteAsync(CreateProductRequest request)
{
if (string.IsNullOrWhiteSpace(request.Name))
return new MessageResult<string> { IsSuccess = false, Message = "产品名称不能为空", Code = 0 };
var product = new Product
{
Name = request.Name.Trim(),
Price = request.Price,
Stock = request.Stock,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now,
IsDeleted = false
};
var id = _repository.Insert(product);
return new MessageResult<string> { IsSuccess = true, Data = id, Message = "创建成功" };
}
}
}
单参数场景(GetById/Delete): 继承 KeyServiceBase<MessageResult<T>>
using System.Threading.Tasks;
using YourProject.Core.Entities;
using UserBaoTech.Foundation.Data;
using UserBaoTech.Foundation.Infrastructure;
using UserBaoTech.Foundation.Services;
namespace YourProject.Core.Services.Products
{
public class GetProductByIdKeyService : KeyServiceBase<MessageResult<Product>>
{
private readonly IRepository<Product> _repository;
public GetProductByIdKeyService(
IRepository<Product> repository,
ILogger logger,
ICacheProvider cache,
IConfigProvider config) : base(logger, cache, config)
{
_repository = repository;
}
protected override async Task<MessageResult<Product>> DoExecuteAsync(string id)
{
var product = _repository.GetById(id);
if (product == null || product.IsDeleted)
return new MessageResult<Product> { IsSuccess = false, Message = "产品不存在", Code = 0 };
return new MessageResult<Product> { IsSuccess = true, Data = product };
}
}
}
第六部分:查询构建器(QueryBuilder)
6.1 核心方法
using System.Collections.Generic;
using UserBaoTech.Foundation.Data;
namespace UserBaoTech.Foundation.Utils
{
public static class QueryBuilder
{
// 生成 SELECT 基础 SQL(不含分页)
public static string BuildSelect(
string tableName,
IQueryParameters query,
out IDictionary<string, object> parameters,
out string orderBy);
// 生成 COUNT SQL
public static string BuildCount(
string tableName,
IQueryParameters query,
out IDictionary<string, object> parameters);
}
}
6.2 分页查询标准用法
// 在 Service 中使用
var tableName = _repository.GetTableName();
var baseSql = QueryBuilder.BuildSelect(tableName, query, out var parameters, out var orderBy);
// 分页索引转换:前端传 1 开始,底层期望 0 开始
var pageIndexForExecutor = query.PageIndex - 1;
if (pageIndexForExecutor < 0) pageIndexForExecutor = 0;
var pagedSql = _executor.GetPagedSql(baseSql, orderBy, pageIndexForExecutor, query.PageSize);
var items = _repository.Query(pagedSql, parameters);
var countSql = QueryBuilder.BuildCount(tableName, query, out var countParams);
var total = _executor.ExecuteScalar(countSql, countParams) ?? 0;
第七部分:分页索引转换规则(重要)
规则:前端传 1 开始,底层 DataAccessExecutor 期望 0 开始。
转换位置:在 Core 层的 Service 中完成,不动 Foundation。
// 正确做法:在 GetProductListService 中适配
var pageIndexForExecutor = query.PageIndex - 1;
if (pageIndexForExecutor < 0) pageIndexForExecutor = 0;
var pagedSql = _executor.GetPagedSql(baseSql, orderBy, pageIndexForExecutor, query.PageSize);
// 错误做法:修改 DataAccessExecutor(Foundation 层)
// Foundation 是只读基架,不能改
第八部分:统一返回格式(MessageResult)
8.1 结构定义
using System;
using System.Text.Json.Serialization;
namespace UserBaoTech.Foundation.Infrastructure
{
public class MessageResult
{
[JsonPropertyName("success")]
public bool IsSuccess { get; set; } = true;
[JsonPropertyName("message")]
public string Message { get; set; }
[JsonPropertyName("code")]
public int Code { get; set; } = 1;
[JsonPropertyName("timestamp")]
public DateTime Timestamp { get; set; } = DateTime.Now;
}
public class MessageResult<T> : MessageResult
{
[JsonPropertyName("data")]
public T Data { get; set; }
[JsonPropertyName("total")]
public int RecordCount { get; set; }
[JsonPropertyName("rows")]
public T[] Rows { get; set; }
}
}
8.2 使用规范
| 场景 | 赋值方式 |
|---|---|
| 分页列表 | 设置 Rows + RecordCount,Data 为 null |
| 单条/非分页 | 设置 Data,Rows 为 null,RecordCount 为 0 |
| 增删改操作 | 设置 Data(如新 ID 或 bool),RecordCount 为 1/0 |
第九部分:依赖注入注册
9.1 基础设施手动注册
// 在 Program.cs 中
builder.Services.AddSingleton<ILogger, NullLogger>();
builder.Services.AddSingleton<ICacheProvider, NullCacheProvider>();
builder.Services.AddSingleton<IConfigProvider, MemoryConfigProvider>();
var connectionString = builder.Configuration.GetConnectionString("Default");
builder.Services.AddScoped<IDbConnection>(sp => new MySqlConnection(connectionString));
builder.Services.AddScoped<IDataAccessExecutor>(sp =>
{
var conn = sp.GetRequiredService<IDbConnection>();
var logger = sp.GetRequiredService<ILogger>();
return new DataAccessExecutor(conn, DatabaseType.MySql, logger);
});
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
9.2 自动注册(ServiceRegistrar 工具)
引用 UserBaoTech.Foundation.Tools.ServiceRegistrar,一行代码完成所有仓储和服务注册:
builder.Services.AddFoundationServices<Product>(ServiceScanningOptions.Development);
前提条件:仓储类以 Repository 结尾,服务类以 Service 结尾。
第十部分:新模块开发标准流程(5 步法)
假设要新增 Order(订单)模块:
| 步骤 | 文件路径 | 操作 | 模板参考 |
|---|---|---|---|
| 1 | Core/Entities/Order.cs | 创建实体,实现 IEntity<string> | Product 实体 |
| 2 | Core/Models/OrderQuery.cs | 创建查询模型,继承 QueryParametersBase | ProductQuery |
| 3 | Core/Data/OrderRepository.cs | 创建仓储,继承 RepositoryBase<Order> | ProductRepository |
| 4 | Core/Services/Orders/ | 创建服务 | 见下方清单 |
| 5 | WebAPI/Controllers/OrderController.cs | 创建控制器,注入服务 | 见下方示例 |
步骤 4 服务清单:
| 服务 | 继承基类 | 用途 |
|---|---|---|
GetOrderListService | ServiceBase<GetOrderListRequest, MessageResult<Order>> | 分页列表 |
GetOrderByIdKeyService | KeyServiceBase<MessageResult<Order>> | 按 ID 查询 |
CreateOrderService | ServiceBase<CreateOrderRequest, MessageResult<string>> | 新增 |
UpdateOrderService | ServiceBase<UpdateOrderRequest, MessageResult<bool>> | 更新 |
DeleteOrderKeyService | KeyServiceBase<MessageResult<bool>> | 删除(主键) |
Controller 标准写法:
using Microsoft.AspNetCore.Mvc;
using YourProject.Core.DTOs.Orders;
using YourProject.Core.Entities;
using YourProject.Core.Models;
using YourProject.Core.Services.Orders;
using UserBaoTech.Foundation.Infrastructure;
using UserBaoTech.Foundation.Services;
namespace YourProject.WebAPI.Controllers;
[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase
{
private readonly GetOrderListService _listService;
private readonly GetOrderByIdKeyService _getByIdService;
private readonly CreateOrderService _createService;
private readonly UpdateOrderService _updateService;
private readonly DeleteOrderKeyService _deleteService;
public OrderController(
GetOrderListService listService,
GetOrderByIdKeyService getByIdService,
CreateOrderService createService,
UpdateOrderService updateService,
DeleteOrderKeyService deleteService)
{
_listService = listService;
_getByIdService = getByIdService;
_createService = createService;
_updateService = updateService;
_deleteService = deleteService;
}
[HttpGet]
public async Task<MessageResult<Order>> GetList([FromQuery] OrderQuery query)
=> await _listService.ExecuteAsync(new GetOrderListRequest { Query = query });
[HttpGet("{id}")]
public async Task<MessageResult<Order>> GetById(string id)
=> await _getByIdService.ExecuteAsync(new KeyRequest { Id = id });
[HttpPost]
public async Task<MessageResult<string>> Create([FromBody] CreateOrderRequest request)
=> await _createService.ExecuteAsync(request);
[HttpPut]
public async Task<MessageResult<bool>> Update([FromBody] UpdateOrderRequest request)
=> await _updateService.ExecuteAsync(request);
[HttpDelete("{id}")]
public async Task<MessageResult<bool>> Delete(string id)
=> await _deleteService.ExecuteAsync(new KeyRequest { Id = id });
}
第十一部分:禁止事项(红线)
| 序号 | 禁止事项 | 正确做法 |
|---|---|---|
| 1 | ❌ 在 Foundation 层添加业务实体或业务逻辑 | Foundation 只放纯技术抽象 |
| 2 | ❌ 在业务层直接使用 IDbConnection 或 DataTable | 使用 IRepository<T> 接口 |
| 3 | ❌ 在 Controller 中写业务逻辑 | 所有业务逻辑在 Service 层 |
| 4 | ❌ 定义仅含 Id 字段的 Request 类 | 统一使用 KeyRequest |
| 5 | ❌ 修改 Foundation 层源码 | 在 Core/WebAPI 层做适配 |
| 6 | ❌ 在业务层编写 SQL 时硬编码数据库方言 | 统一用 @参数名,由执行器自动适配 |
| 7 | ❌ 在 Service 中直接 new 仓储对象 | 通过构造函数依赖注入 |
| 8 | ❌ 分页查询直接传 PageIndex 给 GetPagedSql | 在 Service 层做 -1 转换 |
第十二部分:完整 Product 模块代码模板
见附录一(文档末尾)。
附录一:Product 模块完整代码
Product.cs(实体)
using System;
using UserBaoTech.Foundation.Entities;
namespace YourProject.Core.Entities
{
public class Product : IEntity<string>
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsDeleted { get; set; }
}
}
ProductQuery.cs(查询参数)
using System.Collections.Generic;
using UserBaoTech.Foundation.Data;
namespace YourProject.Core.Models
{
public class ProductQuery : QueryParametersBase
{
public string Name { get; set; }
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
public bool? IsDeleted { get; set; }
public override IDictionary<string, object> GetFilters()
{
var dict = new Dictionary<string, object>();
if (!string.IsNullOrEmpty(Name))
dict["Name LIKE"] = $"%{Name}%";
if (MinPrice.HasValue)
dict["Price >= "] = MinPrice.Value;
if (MaxPrice.HasValue)
dict["Price <= "] = MaxPrice.Value;
if (IsDeleted.HasValue)
dict["IsDeleted"] = IsDeleted.Value;
return dict;
}
}
}
DTOs/Products/CreateProductRequest.cs
namespace YourProject.Core.DTOs.Products
{
public class CreateProductRequest
{
public string Name { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
}
}
DTOs/Products/UpdateProductRequest.cs
namespace YourProject.Core.DTOs.Products
{
public class UpdateProductRequest
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
}
}
DTOs/Products/GetProductListRequest.cs
using YourProject.Core.Models;
namespace YourProject.Core.DTOs.Products
{
public class GetProductListRequest
{
public ProductQuery Query { get; set; }
}
}
ProductRepository.cs(仓储)
using System;
using System.Collections.Generic;
using System.Data;
using YourProject.Core.Entities;
using UserBaoTech.Foundation.Data;
namespace YourProject.Core.Data
{
public class ProductRepository : RepositoryBase<Product>
{
public ProductRepository(IDataAccessExecutor executor) : base(executor) { }
protected override string GetTableNameCore() => "Products";
protected override Product MapToEntity(IDataReader reader)
{
return new Product
{
Id = reader["Id"].ToString(),
Name = reader["Name"].ToString(),
Price = Convert.ToDecimal(reader["Price"]),
Stock = Convert.ToInt32(reader["Stock"]),
CreatedAt = Convert.ToDateTime(reader["CreatedAt"]),
UpdatedAt = Convert.ToDateTime(reader["UpdatedAt"]),
IsDeleted = Convert.ToBoolean(reader["IsDeleted"])
};
}
protected override IDictionary<string, object> GetInsertValues(Product entity)
{
return new Dictionary<string, object>
{
["Id"] = entity.Id,
["Name"] = entity.Name,
["Price"] = entity.Price,
["Stock"] = entity.Stock,
["CreatedAt"] = DateTime.Now,
["UpdatedAt"] = DateTime.Now,
["IsDeleted"] = false
};
}
protected override IDictionary<string, object> GetUpdateValues(Product entity)
{
return new Dictionary<string, object>
{
["Name"] = entity.Name,
["Price"] = entity.Price,
["Stock"] = entity.Stock,
["UpdatedAt"] = DateTime.Now,
["IsDeleted"] = entity.IsDeleted
};
}
public override int Delete(string id)
{
var sql = $"UPDATE {GetTableName()} SET IsDeleted = 1 WHERE Id = @Id";
return Execute(sql, new Dictionary<string, object> { { "@Id", id } });
}
}
}
Services/Products/GetProductListService.cs
using System;
using System.Linq;
using System.Threading.Tasks;
using YourProject.Core.DTOs.Products;
using YourProject.Core.Entities;
using YourProject.Core.Models;
using UserBaoTech.Foundation.Data;
using UserBaoTech.Foundation.Infrastructure;
using UserBaoTech.Foundation.Services;
using UserBaoTech.Foundation.Utils;
namespace YourProject.Core.Services.Products
{
public class GetProductListService : ServiceBase<GetProductListRequest, MessageResult<Product>>
{
private readonly IRepository<Product> _repository;
private readonly IDataAccessExecutor _executor;
public GetProductListService(
IRepository<Product> repository,
IDataAccessExecutor executor,
ILogger logger,
ICacheProvider cache,
IConfigProvider config) : base(logger, cache, config)
{
_repository = repository;
_executor = executor;
}
protected override async Task<MessageResult<Product>> DoExecuteAsync(GetProductListRequest request)
{
var query = request.Query ?? new ProductQuery();
// 默认只显示未删除记录
if (!query.IsDeleted.HasValue)
query.IsDeleted = false;
var tableName = _repository.GetTableName();
var baseSql = QueryBuilder.BuildSelect(tableName, query, out var parameters, out var orderBy);
// 分页索引转换:前端 1 开始 → 底层 0 开始
var pageIndexForExecutor = query.PageIndex - 1;
if (pageIndexForExecutor < 0) pageIndexForExecutor = 0;
var pagedSql = _executor.GetPagedSql(baseSql, orderBy, pageIndexForExecutor, query.PageSize);
var items = _repository.Query(pagedSql, parameters);
var countSql = QueryBuilder.BuildCount(tableName, query, out var countParams);
var total = _executor.ExecuteScalar(countSql, countParams) ?? 0;
return new MessageResult<Product>
{
IsSuccess = true,
Message = "查询成功",
RecordCount = Convert.ToInt32(total),
Rows = items.ToArray()
};
}
}
}
Services/Products/CreateProductService.cs
using System;
using System.Threading.Tasks;
using YourProject.Core.DTOs.Products;
using YourProject.Core.Entities;
using UserBaoTech.Foundation.Data;
using UserBaoTech.Foundation.Infrastructure;
using UserBaoTech.Foundation.Services;
namespace YourProject.Core.Services.Products
{
public class CreateProductService : ServiceBase<CreateProductRequest, MessageResult<string>>
{
private readonly IRepository<Product> _repository;
public CreateProductService(
IRepository<Product> repository,
ILogger logger,
ICacheProvider cache,
IConfigProvider config) : base(logger, cache, config)
{
_repository = repository;
}
protected override async Task<MessageResult<string>> DoExecuteAsync(CreateProductRequest request)
{
if (string.IsNullOrWhiteSpace(request.Name))
return new MessageResult<string> { IsSuccess = false, Message = "产品名称不能为空", Code = 0 };
var product = new Product
{
Name = request.Name.Trim(),
Price = request.Price,
Stock = request.Stock,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now,
IsDeleted = false
};
var id = _repository.Insert(product);
return new MessageResult<string> { IsSuccess = true, Data = id, Message = "创建成功" };
}
}
}
Services/Products/UpdateProductService.cs
using System;
using System.Threading.Tasks;
using YourProject.Core.DTOs.Products;
using YourProject.Core.Entities;
using UserBaoTech.Foundation.Data;
using UserBaoTech.Foundation.Infrastructure;
using UserBaoTech.Foundation.Services;
namespace YourProject.Core.Services.Products
{
public class UpdateProductService : ServiceBase<UpdateProductRequest, MessageResult<bool>>
{
private readonly IRepository<Product> _repository;
public UpdateProductService(
IRepository<Product> repository,
ILogger logger,
ICacheProvider cache,
IConfigProvider config) : base(logger, cache, config)
{
_repository = repository;
}
protected override async Task<MessageResult<bool>> DoExecuteAsync(UpdateProductRequest request)
{
if (string.IsNullOrWhiteSpace(request.Id))
return new MessageResult<bool> { IsSuccess = false, Message = "产品ID不能为空", Code = 0 };
if (string.IsNullOrWhiteSpace(request.Name))
return new MessageResult<bool> { IsSuccess = false, Message = "产品名称不能为空", Code = 0 };
var existing = _repository.GetById(request.Id);
if (existing == null || existing.IsDeleted)
return new MessageResult<bool> { IsSuccess = false, Message = "产品不存在", Code = 0 };
existing.Name = request.Name.Trim();
existing.Price = request.Price;
existing.Stock = request.Stock;
existing.UpdatedAt = DateTime.Now;
var rows = _repository.Update(existing);
return new MessageResult<bool>
{
IsSuccess = rows > 0,
Message = rows > 0 ? "更新成功" : "更新失败",
Data = rows > 0
};
}
}
}
Services/Products/GetProductByIdKeyService.cs
using System.Threading.Tasks;
using YourProject.Core.Entities;
using UserBaoTech.Foundation.Data;
using UserBaoTech.Foundation.Infrastructure;
using UserBaoTech.Foundation.Services;
namespace YourProject.Core.Services.Products
{
public class GetProductByIdKeyService : KeyServiceBase<MessageResult<Product>>
{
private readonly IRepository<Product> _repository;
public GetProductByIdKeyService(
IRepository<Product> repository,
ILogger logger,
ICacheProvider cache,
IConfigProvider config) : base(logger, cache, config)
{
_repository = repository;
}
protected override async Task<MessageResult<Product>> DoExecuteAsync(string id)
{
var product = _repository.GetById(id);
if (product == null || product.IsDeleted)
return new MessageResult<Product> { IsSuccess = false, Message = "产品不存在", Code = 0 };
return new MessageResult<Product> { IsSuccess = true, Data = product };
}
}
}
Services/Products/DeleteProductKeyService.cs
using System.Threading.Tasks;
using YourProject.Core.Entities;
using UserBaoTech.Foundation.Data;
using UserBaoTech.Foundation.Infrastructure;
using UserBaoTech.Foundation.Services;
namespace YourProject.Core.Services.Products
{
public class DeleteProductKeyService : KeyServiceBase<MessageResult<bool>>
{
private readonly IRepository<Product> _repository;
public DeleteProductKeyService(
IRepository<Product> repository,
ILogger logger,
ICacheProvider cache,
IConfigProvider config) : base(logger, cache, config)
{
_repository = repository;
}
protected override async Task<MessageResult<bool>> DoExecuteAsync(string id)
{
return await DeleteAsync(_repository, id);
}
protected override (bool success, string message) CanDeleteWithMessage(IEntity<string> entity)
{
if (entity == null)
return (false, "产品不存在");
if (entity.IsDeleted)
return (false, "产品已被删除");
// 示例:系统数据保护
if (int.TryParse(entity.Id, out int id) && id < 10)
return (false, "系统数据不可删除");
return (true, null);
}
protected override (bool success, string message) OnDeletedWithMessage(IEntity<string> entity)
{
var product = entity as Product;
return (true, product != null ? $"产品 {product.Name} 已成功删除" : "删除成功");
}
}
}
ProductController.cs(控制器)
using Microsoft.AspNetCore.Mvc;
using YourProject.Core.DTOs.Products;
using YourProject.Core.Entities;
using YourProject.Core.Models;
using YourProject.Core.Services.Products;
using UserBaoTech.Foundation.Infrastructure;
using UserBaoTech.Foundation.Services;
namespace YourProject.WebAPI.Controllers;
[ApiController]
[Route("api/[controller]")]
public class ProductController : ControllerBase
{
private readonly GetProductListService _listService;
private readonly GetProductByIdKeyService _getByIdService;
private readonly CreateProductService _createService;
private readonly UpdateProductService _updateService;
private readonly DeleteProductKeyService _deleteService;
public ProductController(
GetProductListService listService,
GetProductByIdKeyService getByIdService,
CreateProductService createService,
UpdateProductService updateService,
DeleteProductKeyService deleteService)
{
_listService = listService;
_getByIdService = getByIdService;
_createService = createService;
_updateService = updateService;
_deleteService = deleteService;
}
[HttpGet]
public async Task<MessageResult<Product>> GetList([FromQuery] ProductQuery query)
=> await _listService.ExecuteAsync(new GetProductListRequest { Query = query });
[HttpGet("{id}")]
public async Task<MessageResult<Product>> GetById(string id)
=> await _getByIdService.ExecuteAsync(new KeyRequest { Id = id });
[HttpPost]
public async Task<MessageResult<string>> Create([FromBody] CreateProductRequest request)
=> await _createService.ExecuteAsync(request);
[HttpPut]
public async Task<MessageResult<bool>> Update([FromBody] UpdateProductRequest request)
=> await _updateService.ExecuteAsync(request);
[HttpDelete("{id}")]
public async Task<MessageResult<bool>> Delete(string id)
=> await _deleteService.ExecuteAsync(new KeyRequest { Id = id });
}
Program.cs(完整启动配置)
using YourProject.Core.Data;
using YourProject.Core.Entities;
using YourProject.Core.Services.Products;
using MySqlConnector;
using System.Data;
using UserBaoTech.Foundation.Data;
using UserBaoTech.Foundation.Infrastructure;
using UserBaoTech.Foundation.Tools.ServiceRegistrar;
// 别名解决命名冲突
using FoundationLogger = UserBaoTech.Foundation.Infrastructure.ILogger;
using FoundationNullLogger = UserBaoTech.Foundation.Infrastructure.NullLogger;
var builder = WebApplication.CreateBuilder(args);
// 1. 数据库连接
var connectionString = builder.Configuration.GetConnectionString("Default");
builder.Services.AddScoped<IDbConnection>(sp => new MySqlConnection(connectionString));
// 2. 数据访问执行器
builder.Services.AddScoped<IDataAccessExecutor>(sp =>
{
var conn = sp.GetRequiredService<IDbConnection>();
var logger = sp.GetRequiredService<FoundationLogger>();
return new DataAccessExecutor(conn, DatabaseType.MySql, logger);
});
// 3. 基础设施
builder.Services.AddSingleton<FoundationLogger, FoundationNullLogger>();
builder.Services.AddSingleton<ICacheProvider, NullCacheProvider>();
builder.Services.AddSingleton<IConfigProvider, MemoryConfigProvider>();
// 4. 工作单元
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
// 5. 自动注册仓储和服务
builder.Services.AddFoundationServices<Product>(ServiceScanningOptions.Development);
// 6. 控制器
builder.Services.AddControllers();
// 7. CORS
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
});
// 8. 构建应用
var app = builder.Build();
app.UseStaticFiles();
app.UseCors("AllowAll");
app.MapControllers();
app.Run();
appsettings.json
{
"ConnectionStrings": {
"Default": "Server=localhost;Database=yourdb;Uid=root;Pwd=yourpassword;CharSet=utf8mb4;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
launchSettings.json(参考配置)
{
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7222;http://localhost:5196",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
文档结束
本份文档是唯一的、最终的、完整的架构开发指南。
- 小白开发人员:按文档从零创建项目、复制模板、修改字段,即可完成业务模块开发。
- AI 新对话框:读取本份文档后,即可理解全部设计逻辑、分层边界、代码规范,独立完成符合架构要求的新模块生成。
- 无需任何其他历史文档、聊天记录、源码文件作为补充。
版本:v3.0(最终版)
发布日期:2026年7月11日
作者:周方勇/咏方舟-长江支流(金质打印通、用宝框架开源作者)
适用范围:所有基于 UserBaoTech.Foundation 的新项目开发
反馈与交流:欢迎转载,不限拷贝,欢迎在评论区留言或私信交流。如果你正在寻找一套 不用 EF、不用 Dapper、可跨语言迁移 的轻量级基架,用宝框架或许是一个值得尝试的选择。

368

被折叠的 条评论
为什么被折叠?



