我可以重用代码来为 EF Core 的子属性选择自定义 DTO 对象吗?

Posted

技术标签:

【中文标题】我可以重用代码来为 EF Core 的子属性选择自定义 DTO 对象吗?【英文标题】:Can I reuse code for selecting a custom DTO object for a child property with EF Core? 【发布时间】:2021-02-26 00:25:43 【问题描述】:

使用 Entity Framework Core 进行查询时,我使用表达式转换为 DTO 对象,这对对象和任何子集合都很有效。

一个简化的例子:

型号:

public class Model

    public int ModelId  get; set; 
    public string ModelName  get; set; 

    public virtual ICollection<ChildModel> ChildModels  get; set; 

    // Other properties, collections, etc.

    public static Expression<Func<Model, ModelDto>> AsDto =>
        model => new ModelDto
         
            ModelId = model.ModelId,
            ModelName = model.ModelName,
            ChildModels = model.ChildModels.AsQueryable().Select(ChildModel.AsDto).ToList()
        ;

查询:

dbContext.Models.Where(m => SomeCriteria).Select(Model.AsDto).ToList();

我的问题是试图找到一种方法来为一个不是集合的孩子做类似的事情。如果我已添加到我的模型中:

public AnotherChildModel AnotherChildModel  get; set; 

我可以在表达式中添加一个转换:

public static Expression<Func<Model, ModelDto>> AsDto =>
    model => new ModelDto
     
        ModelId = model.ModelId,
        ModelName = model.ModelName,
        ChildModels = model.ChildModels.AsQueryable().Select(ChildModel.AsDto).ToList(),
        AnotherChildModel = new AnotherChildModelDto
        
            AnotherChildModelId = model.AnotherChildModelId
        
    ;

但是,每次我需要将第二个子模型转换为 DTO 对象时,我还没有找到避免重复此代码的好方法。表达式适用于主对象和任何子集合,但不适用于单个实体。有没有办法为单个实体添加等效的 .Select() ?

【问题讨论】:

你看过 Automapper 吗? 我发现了类似的情况,也许这会有所帮助。 ***.com/questions/22434498/… @Dai - 我看过 Automapper 一些,但我不熟悉它,并且很难开始使用它,尤其是作为实体框架查询的一部分。它似乎确实可以做我正在寻找的东西。 @hijinxbassist - 这个答案很有帮助!我以为我过去曾尝试过类似的事情并且遇到了问题(编译的表达式导致每行结果有许多单独的查询),但是现在在测试与该答案类似的代码时,它似乎正在工作。谢谢! 【参考方案1】:

有几个库允许以直观的方式做到这一点:

LINQKit

[Expandable(nameof(AsDtoImpl))]
public static AsDto(Model model)

   _asDtoImpl ??= AsDtoImpl() .Compile();
   return _asDtoImpl(model);


private static Func<Model, ModelDto> _asDtoImpl;

private static Expression<Func<Model, ModelDto>> AsDtoImpl =>
    model => new ModelDto
     
        ModelId = model.ModelId,
        ModelName = model.ModelName,
        ChildModels = model.ChildModels.AsQueryable().Select(ChildModel.AsDto).ToList(),
        AnotherChildModel = new AnotherChildModelDto
        
            AnotherChildModelId = model.AnotherChildModelId
        
    ;
dbContext.Models
   .Where(m => SomeCriteria).Select(m => Model.AsDto(m))
   .AsExpandable()
   .ToList();

NeinLinq - 与 LINQKit 中的几乎相同

[InjectLambda]
public static AsDto(Model model)

   _asDto ??= AsDto() .Compile();
   return _asDto(model);


private static Func<Model, ModelDto> _asDto;

private static Expression<Func<Model, ModelDto>> AsDto =>
    model => new ModelDto
     
        ModelId = model.ModelId,
        ModelName = model.ModelName,
        ChildModels = model.ChildModels.AsQueryable().Select(ChildModel.AsDto).ToList(),
        AnotherChildModel = new AnotherChildModelDto
        
            AnotherChildModelId = model.AnotherChildModelId
        
    ;
dbContext.Models
   .Where(m => SomeCriteria).Select(m => Model.AsDto(m))
   .ToInjectable()
   .ToList();

DelegateDecompiler - 比其他人更简洁

[Compute]
public static AsDto(Model model)
  => new ModelDto
     
        ModelId = model.ModelId,
        ModelName = model.ModelName,
        ChildModels = model.ChildModels.AsQueryable().Select(ChildModel.AsDto).ToList(),
        AnotherChildModel = new AnotherChildModelDto
        
            AnotherChildModelId = model.AnotherChildModelId
        
    
dbContext.Models
   .Where(m => SomeCriteria).Select(m => Model.AsDto(m))
   .Decompile()
   .ToList();

所有库都做同样的事情 - 在 EF Core 处理之前更正表达式树。他们都需要额外的调用来注入它自己的IQueryProvider

【讨论】:

感谢您的详细解答!使用您最后提到的额外调用,例如,这将是 LINQKit 中的“AsExpandable()”调用吗?那么使用这些的任何查询都需要这样做吗? 是的,完全正确。但是这个调用可以在查询的根目录或存储库中完成。

以上是关于我可以重用代码来为 EF Core 的子属性选择自定义 DTO 对象吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 EF Core 5 中为自定义 SQL 配置导航属性

EF Core 5 - 如何将 EF.Functions.Like 与映射到 JSON 字符串的自定义属性一起使用?

使用 EF Core 保存附加实体时如何删除子实体

是否必须使用 .Include 加载 EF Core 5 中的子对象?

EF Core 中的一对一关系(无法确定一对一关系的子/依赖方)

实现 EF Core 6 自定义查询标记