OpenRobot.Framework.DynamicModel
1.3.7
dotnet add package OpenRobot.Framework.DynamicModel --version 1.3.7
NuGet\Install-Package OpenRobot.Framework.DynamicModel -Version 1.3.7
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OpenRobot.Framework.DynamicModel" Version="1.3.7" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OpenRobot.Framework.DynamicModel" Version="1.3.7" />
<PackageReference Include="OpenRobot.Framework.DynamicModel" />
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OpenRobot.Framework.DynamicModel --version 1.3.7
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: OpenRobot.Framework.DynamicModel, 1.3.7"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package OpenRobot.Framework.DynamicModel@1.3.7
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OpenRobot.Framework.DynamicModel&version=1.3.7
#tool nuget:?package=OpenRobot.Framework.DynamicModel&version=1.3.7
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
OpenRobot.Framework.DynamicModel
OpenRobot.Framework.DynamicModel 是一个 .NET 8.0 类库,用于在强类型 DTO 和动态属性模型之间进行双向转换。主要应用于数据模型的扁平化存储和恢复场景。
特性
- DTO 到 OpenModel: 将强类型 DTO 转换为扁平化的
OpenModel集合,便于数据库存储 - OpenModel 到 Entity: 将
OpenModel集合转换为 ORM 实体,用于数据库操作 - IOpenModel 到自定义对象: 通过基于特性的灵活配置,将
IOpenModel集合映射回自定义对象 - 模板同步: 支持模板新增属性的智能同步,自动保持 ParentID 编号的连续性
- 嵌套对象支持: 无缝处理复杂的嵌套对象和集合
- 验证元数据: 保留源 DTO 的验证规则和显示属性
- 自定义转换器: 使用
ICustomApressConverter扩展自定义转换逻辑
安装
dotnet add package OpenRobot.Framework.DynamicModel
快速开始
1. 定义 DTO
using OpenRobot.Framework.DynamicModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
public class UserDto : IDynamicTemplateDto
{
[MainKey]
public int Id { get; set; }
[Description("用户名")]
public string Name { get; set; }
[RegularExpression(@"^\d{6}$", ErrorMessage = "必须是6位数字")]
public string Code { get; set; }
public AddressDto Address { get; set; }
public List<PhoneDto> Phones { get; set; }
}
public class AddressDto
{
public string City { get; set; }
public string Street { get; set; }
}
2. 转换 DTO 为 OpenModel
var dto = new UserDto
{
Id = 1,
Name = "张三",
Code = "123456",
Address = new AddressDto { City = "北京", Street = "长安街" }
};
// 转换为扁平化的 OpenModel 集合
var models = dto.ModelToDynamicProperties(mainId: 1);
3. 转换 OpenModel 为数据库实体
// 定义实现 IDynamicPropEntity 的实体
public class DynamicProperty : IDynamicPropEntity
{
public int Id { get; set; }
public int DynamicPropertyId { get; set; }
public string PropertyName { get; set; }
public string Value { get; set; }
// ... 其他属性
}
// 转换并保存到数据库
var entities = models.OpenModelConvertDynamicPropEntity<DynamicProperty>();
await dbContext.DynamicProperties.AddRangeAsync(entities);
await dbContext.SaveChangesAsync();
4. 映射回自定义对象
基础映射示例
基础映射示例
public class UserViewModel : ICustomPropConvert
{
[CustomPropMap("Name")]
public string UserName { get; set; }
[CustomPropMap("Address", HasObject = true)]
public AddressViewModel Address { get; set; }
}
public class AddressViewModel : ICustomPropConvert
{
[CustomPropMap("City")]
public string CityName { get; set; }
}
// 从数据库加载并映射回
var openModels = await dbContext.DynamicProperties
.Where(m => m.DynamicPropertyId == 1)
.ToListAsync();
var viewModel = new UserViewModel();
viewModel.OpenDynamicPropertryBuild(openModels);
使用 PropDetailNode 获取属性元数据
public class UserViewModel : ICustomPropConvert
{
[CustomPropMap("Name")]
public string UserName { get; set; }
// PropDetailNode 会自动填充属性的元数据(标题、验证规则等)
[CustomPropMap("Code")]
public PropDetailNode CodeDetail { get; set; }
[CustomPropMap("Email")]
public PropDetailNode EmailDetail { get; set; }
}
// 从数据库加载并映射
var viewModel = new UserViewModel();
viewModel.OpenDynamicPropertryBuild(openModels);
// 访问属性值和元数据
string code = viewModel.CodeDetail.Value; // 属性值
string title = viewModel.CodeDetail.Title; // 显示标题
string regex = viewModel.CodeDetail.Regex; // 验证规则
string errorMsg = viewModel.CodeDetail.ErrorMessage; // 错误消息
// 用于前端验证展示
var validationRules = new
{
Code = new
{
Value = viewModel.CodeDetail.Value,
Title = viewModel.CodeDetail.Title,
Regex = viewModel.CodeDetail.Regex,
ErrorMessage = viewModel.CodeDetail.ErrorMessage
}
};
使用索引过滤(1.3.0+ 新增)
当需要从数组中提取特定元素时,可以使用带 index 参数的重载方法:
// 场景:从包含多个地址的集合中提取第一个地址
var openModels = await dbContext.DynamicProperties
.Where(m => m.DynamicPropertyId == 1)
.ToListAsync();
// 只提取 Index = 0 的元素(第一个地址)
var firstAddress = new AddressViewModel();
firstAddress.OpenDynamicPropertryBuild(openModels, index: 0);
// 只提取 Index = 1 的元素(第二个地址)
var secondAddress = new AddressViewModel();
secondAddress.OpenDynamicPropertryBuild(openModels, index: 1);
注意事项:
- 使用
index参数时,过滤逻辑会同时匹配PropertyName和Index - 不使用
index参数时,默认匹配所有 Index 的元素 - 嵌套对象内部的映射会自动启用索引过滤,确保父子关系正确
特性说明
| 特性 | 用途 |
|---|---|
[MainKey] |
标记主键字段(使用 DynamicPropertyId) |
[KeyString] |
标记需要 JSON 序列化的复杂类型 |
[PropIngore] |
排除属性不参与转换 |
[CustomOpenConvert] |
指定自定义转换器(实现 ICustomApressConverter) |
[CustomPropMap] |
反向转换时映射属性名称 |
高级用法
模板同步 (Template Sync)
当模板对象新增属性后,使用 DynamicTemplateSyncExecutor 同步到数据库:
using OpenRobot.Framework.DynamicModel;
// 场景:数据库中已有部分属性
var existingEntities = await dbContext.DynamicProperties
.Where(m => m.DynamicPropertyId == 1)
.ToListAsync();
// 现有属性:Name (ParentID: #01), Age (ParentID: #02)
// 创建同步执行器
var syncExecutor = new DynamicTemplateSyncExecutor<UserDto, DynamicProperty>();
// 执行同步
var syncResult = syncExecutor.Sync(mainId: 1, entities: existingEntities);
// 处理结果
// 1. 新增属性(ParentID 从 #03 开始)
foreach (var newEntity in syncResult.InsertRanges)
{
Console.WriteLine($"新增属性: {newEntity.PropertyName}, ParentID: {newEntity.ParentID}");
}
await dbContext.DynamicProperties.AddRangeAsync(syncResult.InsertRanges);
// 2. 匹配的现有属性 ID
Console.WriteLine($"匹配的属性: {string.Join(", ", syncResult.MatchRanges)}");
// 3. 删除过期属性(可选)
var obsoleteEntities = syncExecutor.FindObsoleteEntities(
mainId: 1,
entities: existingEntities,
matchedIds: syncResult.MatchRanges
);
if (obsoleteEntities.Count > 0)
{
dbContext.DynamicProperties.RemoveRange(obsoleteEntities);
}
await dbContext.SaveChangesAsync();
ParentID 编号规则:
- 现有属性保持其原始 ParentID
- 新增属性从现有数据的最大索引继续编号(如现有最大是
#02,新增从#03开始) - 嵌套属性也遵循相同规则,在各自层级内保持编号连续性
自定义转换器
public class DateTimeConverter : ICustomApressConverter
{
public string PropertyConvert(object? value)
{
if (value is DateTime dt)
return dt.ToString("yyyy-MM-dd");
return "";
}
public object? ApressConvert(IList<OpenModel> models)
{
// 自定义转换逻辑
return null;
}
}
public class EventDto : IDynamicTemplateDto
{
[CustomOpenConvert(typeof(DateTimeConverter))]
public DateTime EventDate { get; set; }
}
选择性属性转换
// 仅转换指定属性
var models = dto.ModelToDynamicProperties(mainId: 1, "Name", "Address");
嵌套对象转换
// 带父 ID 和属性前缀的嵌套转换
var models = dto.ModelToDynamicProperties(mainId: 1, parentId: "P01", propNamePreTag: "Parent");
数据结构
OpenModel 使用扁平化结构表示嵌套对象:
public class OpenModel : IOpenModel
{
public int DynamicPropertyId { get; set; } // 主实体 ID
public string PropertyName { get; set; } // 点号表示路径(如 "Address.City")
public string Value { get; set; } // 序列化值
public string ParentID { get; set; } // 十六进制层级路径
public int Index { get; set; } // 数组元素索引
public bool IsBaseType { get; set; } // 基元类型标志
public bool IsArray { get; set; } // 数组标志
public int Level { get; set; } // 嵌套层级深度(自动计算)
public string Title { get; set; } // 来自 Description 的显示名
public string Regex { get; set; } // 验证正则表达式
public string ErrorMessage { get; set; } // 验证错误消息
}
Level 字段说明:
Level表示属性的嵌套层级深度,根据ParentID自动计算- 例如:
ParentID = "#01"→Level = 1(顶层属性) - 例如:
ParentID = "#01,#00"→Level = 2(嵌套一层) - 例如:
ParentID = "#01,#00,#02"→Level = 3(嵌套两层)
版本要求
- .NET 8.0 或更高版本
构建和发布
# 构建
dotnet build
# 打包
dotnet pack
# 发布到 NuGet
nuget push *.nupkg -Source https://api.nuget.org/v3/index.json
许可证
Copyright © OpenRobot
版本历史
1.3.7 (2026-06-23)
- 新增
GeneralPropUpdateDto和UpdatePropItemDto:将更新操作的数据模型从DynamicGeneralService迁移至核心库,现在这两个类型直接位于OpenRobot.Framework.DynamicModel命名空间下,便于跨项目复用 - 新增
Model/UpdatePropItemDto.cs文件,GeneralPropUpdateDto包含Items(变更列表)和DynamicPropertyId(父实体 ID)
1.3.6 (2026-06-16)
- 修复集合元素 ParentID 计算错误导致重复新增:
DynamicTemplateSyncExecutor.ProcessEnumerableTypeWithExistingParentID中复杂类型集合元素(List<T>)的 ParentID 误用GetMaxChildIndexForParentID计算的全局最大值而非元素循环索引elementIndex,导致第二次同步时元素 1+ 的 ParentID 与首轮不一致,被判定为新属性从而产生多余InsertCount。改为始终使用循环索引elementIndex构建 ParentID,保证幂等同步。
1.3.5 (2026-06-15)
- **修复数组 ParentID 问题
1.3.4 (2026-05-24)
- **修复数组 OpenModelConvertDynamicPropEntity转换方法无法添加正确的数据
1.3.3 (2026-05-12)
- 修复数组删除后构建数量缺失问题:修正数组层级深度计算逻辑,使用
ParentID(逗号分隔)替代PropertyName计算数组深度,确保删除数组元素后仍能正确构建子元素数量 - 修复过滤条件叠加错误:修正
parentID过滤条件错误地始终包含Index检查的问题,改为按需叠加过滤条件
1.3.0 (2026-05-06)
- 新增 Level 字段:
IOpenModel、OpenModel和IDynamicPropEntity接口新增Level属性,自动根据ParentID计算嵌套层级深度 - 索引过滤改进:
OpenDynamicPropertryBuild方法新增重载,支持更精确的数组元素索引过滤 - 模板同步增强:
DynamicTemplateSyncExecutor创建实体时自动计算并设置Level字段
1.2.0
- 新增
DynamicTemplateSyncExecutor模板同步功能,支持智能 ParentID 编号
1.1.0
- 重构架构,简化转换流程,新增
ICustomPropConvert支持
1.0.0
- 初始版本
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
net8.0
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on OpenRobot.Framework.DynamicModel:
| Package | Downloads |
|---|---|
|
OpenRobot.Framework.FormControl
基于数据驱动的 WPF 动态表单构建与渲染引擎,支持 12 种控件类型、级联下拉、条件显示、跨字段校验和多列布局 |
|
|
OpenRobot.Framework.DynamicGeneralService
动态属性管理服务库,提供动态属性 CRUD 操作、实体与 DTO 双向转换、嵌套属性树管理 |
GitHub repositories
This package is not used by any popular GitHub repositories.