SelectAll 投影到 DTO/VO 时,数据库列名与属性名不一致的字段无法映射 返回
你好,我不确定这是我没有找到对应文档,还是这个场景目前不支持,或者确实缺少相关处理,所以想先提一个问题咨询一下。
问题概述
在联表查询中使用 SelectAll(),并且把查询结果直接投影到 DTO/VO 类型时,如果某些字段的数据库列名和 C# 属性名不一致,这些字段不会被正确赋值。
实体类上有 [SugarColumn(ColumnName = "...")],所以直接查询实体类本身时可以正常映射。但是 DTO/VO 类没有,也不希望加 SqlSugar 的特性。此时 SelectAll() 似乎返回的是数据库列名,而 DTO/VO 结果绑定时按属性名匹配,导致字段值丢失。
示例
数据库表
CREATE TABLE OrderLine (
Id INT,
ProductCode NVARCHAR(50),
EXT_MaterialCode NVARCHAR(50),
EXT_Thickness DECIMAL(18, 4)
);
CREATE TABLE Product (
ProductCode NVARCHAR(50),
ImageUrl NVARCHAR(500)
);
实体类
[SugarTable("OrderLine")]
public class OrderLineEntity
{
[SugarColumn(ColumnName = "Id")]
public int? Id { get; set; }
[SugarColumn(ColumnName = "ProductCode")]
public string? ProductCode { get; set; }
[SugarColumn(ColumnName = "EXT_MaterialCode")]
public string? MaterialCode { get; set; }
[SugarColumn(ColumnName = "EXT_Thickness")]
public decimal? Thickness { get; set; }
}
[SugarTable("Product")]
public class ProductEntity
{
[SugarColumn(ColumnName = "ProductCode")]
public string? ProductCode { get; set; }
[SugarColumn(ColumnName = "ImageUrl")]
public string? ImageUrl { get; set; }
}
DTO / VO
DTO/VO 是接口或视图模型,不希望依赖 ORM 特性:
public class OrderLineDto
{
public int Id { get; set; }
public string ProductCode { get; set; }
public string MaterialCode { get; set; }
public decimal Thickness { get; set; }
public string ImageUrl { get; set; }
}
查询代码
var list = await db.Queryable<OrderLineEntity>("line")
.LeftJoin<ProductEntity>((line, product) => line.ProductCode == product.ProductCode)
.Select((line, product) => new OrderLineDto
{
Id = line.Id.SelectAll(),
ImageUrl = product.ImageUrl
})
.ToListAsync();
实际结果
数据库列名和 C# 属性名一致的字段可以正常赋值:
Id // 正常
ProductCode // 正常
ImageUrl // 正常
但数据库列名和 C# 属性名不一致的字段没有被正确赋值:
MaterialCode // null,但数据库列 EXT_MaterialCode 有值
Thickness // 默认值,但数据库列 EXT_Thickness 有值
期望结果
因为 SelectAll() 是从 line 调用的,而 line 的类型是 OrderLineEntity,实体类上已经有 [SugarColumn] 映射,所以我原本期望 SqlSugar 可以根据源实体的映射信息生成别名,例如:
line.EXT_MaterialCode AS MaterialCode,
line.EXT_Thickness AS Thickness
这样查询结果就可以直接投影到 OrderLineDto,而不需要在 DTO/VO 上再加 [SugarColumn]。
为什么不希望在 DTO/VO 上加 SugarColumn
很多项目中实体类和 DTO/VO 的职责是分开的:
实体类用于描述数据库结构,可以包含 ORM 特性。
DTO/VO 用于接口返回、视图展示或客户端模型,不希望依赖 ORM。
同一个 DTO/VO 可能会被序列化、前端、客户端或其他层复用。
因此,把 [SugarColumn] 加到 DTO/VO 上并不总是合适。
我尝试或考虑过的 workaround
1. 先查询实体类,再手动映射到 DTO/VO
这样可以让实体类上的 [SugarColumn] 生效,但如果还需要联表字段,就需要额外处理。
var rows = await db.Queryable<OrderLineEntity>("line")
.LeftJoin<ProductEntity>((line, product) => line.ProductCode == product.ProductCode)
.Select((line, product) => new SomeIntermediateType
{
// 在这里组合 line 和 product 的字段
})
.ToListAsync();
2. 显式写出所有字段
这种方式可靠,但当主表字段很多时会非常繁琐:
.Select((line, product) => new OrderLineDto
{
Id = line.Id,
ProductCode = line.ProductCode,
MaterialCode = line.MaterialCode,
Thickness = line.Thickness,
ImageUrl = product.ImageUrl
})
3. 给 DTO/VO 注册运行时映射
例如:
db.MappingColumns.Add("MaterialCode", "EXT_MaterialCode", nameof(OrderLineDto));
db.MappingColumns.Add("Thickness", "EXT_Thickness", nameof(OrderLineDto));
这种方式可能可以解决问题,但我不确定这是不是推荐做法。它会改变当前 SqlSugar Context 中该 DTO 类型的映射行为,如果 DTO/VO 后续也被其他查询或操作使用,可能会有副作用。
想咨询的问题
是否有已存在的文档或推荐写法,可以让
SelectAll()使用源实体类的[SugarColumn]映射,并自动给目标 DTO/VO 属性生成别名?如果没有,这种“源实体有列映射,目标 DTO/VO 无 ORM 特性”的场景是否属于不支持的设计?
SelectAll()在投影到不同结果类型时,是否可以考虑基于源实体映射自动生成AS 属性名?当主表字段很多,同时又需要少量联表字段时,推荐的写法是什么?
环境
SqlSugar 版本:5.1.2.7
数据库:SQL Server
运行环境:.NET 6