You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C# ASP.NET中正确映射关联查询结果到DTO?

C# ASP.NET Web API关联查询字段映射问题

我正在开发一个C# ASP.NET简易Web API,需要对数据库执行关联查询,但查询返回的部分字段(如DefaultArtefactName)不存在于现有的实体模型中,导致无法完成映射。

现有实体模型:

public class Artefact
{
    public Guid Id { get; set; }
    public string Status { get; set; }
    public Guid Completed_By { get; set; }
    public DateTime Completed_At { get; set; }
    public Guid PhaseId { get; set; }
    public int DefaultArtefactId { get; set; }
}
public class DefaultArtefact
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Artefact_Type { get; set; }
    public string Phase { get; set; }
}

关联查询方法:

public async Task<IEnumerable<Artefact>> GetArtefactsFromProject(Guid projectId)
{
    const string query = @"SELECT a.Id, a.Status, a.Completed_By, a.Completed_At, da.Name AS DefaultArtefactName, da.Artefact_Type FROM artefact a JOIN defaultartefacts da ON a.DefaultArtefactId = da.Id
                            JOIN Phase ph ON a.PhaseId = ph.Id JOIN Project p ON ph.ProjectId = p.Id WHERE p.Id = @ProjectId";
    using var connection = CreateConnection();
    return await connection.QueryAsync<Artefact>(query, new { ProjectId = projectId});
}

当前模型与数据库表一一对应,我希望通过关联查询同时获取Artefact信息和DefaultArtefact的名称,但由于Artefact模型没有DefaultArtefactName字段,无法完成映射。我纠结的点在于:是为每个关联查询新建对应的模型,还是使用DAL中现有方法先遍历Artefacts,通过ID获取名称后在业务逻辑层映射到包含所需字段的新DTO?


两种方案分析与建议

方案1:为关联查询新建专用模型(或DTO)

  • 做法:创建包含查询返回所有字段的类,作为查询的映射目标,比如:
    public class ArtefactWithDefaultName
    {
        public Guid Id { get; set; }
        public string Status { get; set; }
        public Guid Completed_By { get; set; }
        public DateTime Completed_At { get; set; }
        public Guid PhaseId { get; set; }
        public int DefaultArtefactId { get; set; }
        public string DefaultArtefactName { get; set; }
        public string Artefact_Type { get; set; }
    }
    
    修改查询方法的返回类型:
    public async Task<IEnumerable<ArtefactWithDefaultName>> GetArtefactsFromProject(Guid projectId)
    {
        const string query = @"SELECT a.Id, a.Status, a.Completed_By, a.Completed_At, da.Name AS DefaultArtefactName, da.Artefact_Type FROM artefact a JOIN defaultartefacts da ON a.DefaultArtefactId = da.Id
                                JOIN Phase ph ON a.PhaseId = ph.Id JOIN Project p ON ph.ProjectId = p.Id WHERE p.Id = @ProjectId";
        using var connection = CreateConnection();
        return await connection.QueryAsync<ArtefactWithDefaultName>(query, new { ProjectId = projectId});
    }
    
  • 优势:
    • 一次数据库查询完成所有数据获取,避免N+1查询问题(遍历Artefact再逐个查DefaultArtefact会触发多次数据库请求),性能更优。
    • 模型与查询结果直接对应,映射逻辑清晰,维护成本低。
  • 劣势:如果有多个不同的关联查询场景,会新增多个专用模型,类数量增多。

方案2:先查Artefact再补全名称(业务层映射)

  • 做法:先用现有DAL方法获取所有Artefact,收集所有DefaultArtefactId后批量查询对应的名称,最后在业务层映射到目标DTO:
    // DAL新增批量查询方法
    public async Task<Dictionary<int, string>> GetDefaultArtefactNamesByIds(IEnumerable<int> ids)
    {
        const string query = "SELECT Id, Name FROM defaultartefacts WHERE Id IN @Ids";
        using var connection = CreateConnection();
        var result = await connection.QueryAsync<(int Id, string Name)>(query, new { Ids = ids });
        return result.ToDictionary(x => x.Id, x => x.Name);
    }
    
    // 业务层逻辑
    public async Task<IEnumerable<ArtefactDto>> GetArtefactsWithNames(Guid projectId)
    {
        var artefacts = await _artefactDal.GetArtefactsFromProject(projectId);
        var defaultIds = artefacts.Select(a => a.DefaultArtefactId).Distinct();
        var nameMap = await _artefactDal.GetDefaultArtefactNamesByIds(defaultIds);
        
        return artefacts.Select(a => new ArtefactDto
        {
            Id = a.Id,
            Status = a.Status,
            Completed_By = a.Completed_By,
            Completed_At = a.Completed_At,
            PhaseId = a.PhaseId,
            DefaultArtefactId = a.DefaultArtefactId,
            DefaultArtefactName = nameMap.TryGetValue(a.DefaultArtefactId, out var name) ? name : null
        });
    }
    
  • 优势:复用现有实体模型,不需要新增过多专用类。
  • 劣势:需要至少两次数据库查询,性能不如一次关联查询;业务层需要额外的映射逻辑,代码量增加。

推荐方案

优先选择方案1,原因如下:

  1. 性能是Web API的核心指标之一,一次关联查询的效率远高于多次查询,数据量越大差距越明显。
  2. 专用模型的新增是合理的,它代表了业务场景下的特定数据视图,符合单一职责原则,后续维护时能清晰对应到查询场景。
  3. 若担心类数量过多,可以将这类专用模型统一放在ViewModels或Dtos文件夹下分类管理,避免项目结构混乱。

内容的提问来源于stack exchange,提问作者Coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 21:47:37