AutoMapper10.0 Queryable Extensions

AutoMapper10.0 Queryable Extensions

Queryable Extensions 

当使用 ORM,如 NHibernate 或实体框架与 AutoMapper 的标准映射器。在 Map 函数中,您可能会注意到当 AutoMapper 试图将结果映射到目标类型时,ORM 将查询图中所有对象的所有字段。

如果 ORM 公开 IQueryables,您可以使用 AutoMapper 的 QueryableExtensions 助手方法来解决这个关键问题。

使用实体框架作为示例,假设您有一个与实体 Item 存在关系的实体 OrderLine。如果要使用 Item’s Name 属性将其映射到 OrderLineDTO,则使用标准映射器。Map 调用将导致实体框架查询整个 OrderLine 和 Item 表。

用这种方法代替。

给定下列实体:

public class OrderLine
{
  public int Id { get; set; }
  public int OrderId { get; set; }
  public Item Item { get; set; }
  public decimal Quantity { get; set; }
}

public class Item
{
  public int Id { get; set; }
  public string Name { get; set; }
}

以及以下的 DTO:

public class OrderLineDTO
{
  public int Id { get; set; }
  public int OrderId { get; set; }
  public string Item { get; set; }
  public decimal Quantity { get; set; }
}

你可以像这样使用可查询扩展:

var configuration = new MapperConfiguration(cfg =>
    cfg.CreateMap<OrderLine, OrderLineDTO>()
    .ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name)));

public List<OrderLineDTO> GetLinesForOrder(int orderId)
{
  using (var context = new orderEntities())
  {
    return context.OrderLines.Where(ol => ol.OrderId == orderId)
             .ProjectTo<OrderLineDTO>(configuration).ToList();
  }
}

这个。ProjectTo < OrderLineDTO > ()将告诉 AutoMapper 的映射引擎向 IQueryable 发出一个 Select 子句,该子句将告知实体框架,它只需要查询 Item 表的 Name 列,就像使用 Select 子句手动将 IQueryable 映射到 OrderLineDTO 一样。

请注意,要使这个特性发挥作用,所有类型转换都必须在 Mapping 中显式处理。例如,您不能依靠 Item 类的 ToString ()覆盖来通知实体框架只从 Name 列中进行选择,任何数据类型更改(如 Double to Decimal)也必须显式处理。

The instance API 

从8.0开始,IMapper 上也有类似的 ProjectTo 方法,当你在 DI 中使用 IMapper 时会感觉更自然。

Preventing lazy loading/SELECT N+1 problems

由于 AutoMapper 构建的 LINQ 投影由查询提供者直接转换为 SQL 查询,因此映射发生在 SQL/ADO.NET 级别,而不涉及您的实体。所有数据都急切地获取并加载到您的 dto 中。

嵌套集合使用 Select 来投影子 dto:

from i in db.Instructors
orderby i.LastName
select new InstructorIndexData.InstructorModel
{
    ID = i.ID,
    FirstMidName = i.FirstMidName,
    LastName = i.LastName,
    HireDate = i.HireDate,
    OfficeAssignmentLocation = i.OfficeAssignment.Location,
    Courses = i.Courses.Select(c => new InstructorIndexData.InstructorCourseModel
    {
        CourseID = c.CourseID,
        CourseTitle = c.Title
    }).ToList()
};

这个映射通过 AutoMapper 将导致一个 SELECT n + 1问题,因为每个子课程将一次查询一个,除非通过 ORM 指定急切地获取。使用 LINQ 投影,ORM 不需要特殊的配置或规范。ORM 使用 LINQ 投影来构建所需的确切的 SQL 查询。

Custom projection 

如果成员名称不一致,或者您想创建计算属性,您可以使用 MapFrom (基于表达式的重载)为目标成员提供自定义表达式:

var configuration = new MapperConfiguration(cfg => cfg.CreateMap<Customer, CustomerDto>()
    .ForMember(d => d.FullName, opt => opt.MapFrom(c => c.FirstName + " " + c.LastName))
    .ForMember(d => d.TotalContacts, opt => opt.MapFrom(c => c.Contacts.Count()));

AutoMapper 传递带有内置投影的所提供的表达式。只要查询提供程序能够解释提供的表达式,所有内容都将一直传递到数据库。

如果表达式被查询提供程序(实体框架、 NHibernate 等)拒绝,您可能需要调整表达式,直到找到一个被接受的表达式。

Custom Type Conversion 

有时,您需要完全替换从源类型到目标类型的类型转换。在正常运行时映射中,这是通过 ConvertUsing 方法实现的。要执行 LINQ 投影中的模拟,请使用 ConvertUsing 方法:

cfg.CreateMap<Source, Dest>().ConvertUsing(src => new Dest { Value = 10 });

基于表达式的 ConvertUsing 比基于 func 的 ConvertUsing 稍微有些限制,它只使用 Expression 中允许的重载,并且底层的 LINQ 提供程序可以工作。

Custom destination type constructors 

如果你的目标类型有一个自定义的构造函数,但是你不想覆盖整个映射,那么使用基于表达式的构造函数重载:

cfg.CreateMap<Source, Dest>()
    .ConstructUsing(src => new Dest(src.Value + 10));

自动调整器会根据匹配的名称自动匹配目标构造函数参数和源成员,所以只有当自动调整器不能正确匹配目标构造函数,或者在构建过程中需要额外的自定义时,才使用这个方法。

String conversion 

当目标成员类型是字符串而源成员类型不是时,AutoMapper 将自动添加 ToString ()。

public class Order {
    public OrderTypeEnum OrderType { get; set; }
}
public class OrderDto {
    public string OrderType { get; set; }
}
var orders = dbContext.Orders.ProjectTo<OrderDto>(configuration).ToList();
orders[0].OrderType.ShouldEqual("Online");

Explicit expansion 

在某些情况下,比如 OData,通过 IQueryable 控制器操作返回一个通用 DTO。如果没有明确的指令,AutoMapper 将展开结果中的所有成员。要控制在投影期间展开哪些成员,请在配置中设置 ExplicitExpansion,然后传入要显式展开的成员:

dbContext.Orders.ProjectTo<OrderDto>(configuration,
    dest => dest.Customer,
    dest => dest.LineItems);
// or string-based
dbContext.Orders.ProjectTo<OrderDto>(configuration,
    null,
    "Customer",
    "LineItems");
// for collections
dbContext.Orders.ProjectTo<OrderDto>(configuration,
    null,
    dest => dest.LineItems.Select(item => item.Product));

有关更多信息,请参见测试。

Aggregations 

LINQ 可以支持聚合查询,AutoMapper 支持 LINQ 扩展方法。在自定义投影示例中,如果我们将 TotalContacts 属性重命名为 ContactsCount,AutoMapper 将匹配 Count ()扩展方法,LINQ 提供程序将把计数转换为相关的子查询以聚合子记录。

如果 LINQ 提供者支持,AutoMapper 还可以支持复杂的聚合和嵌套限制:

cfg.CreateMap<Course, CourseModel>()
    .ForMember(m => m.EnrollmentsStartingWithA,
          opt => opt.MapFrom(c => c.Enrollments.Where(e => e.Student.LastName.StartsWith("A")).Count()));

这个查询返回每门课程的学生总数,这些学生的姓氏以字母“ a”开头。

Parameterization 

有时候,投影的值需要运行时参数。考虑一个需要引入当前用户名作为其数据一部分的投影。我们可以将 MapFrom 配置参数化,而不是使用后映射代码:

string currentUserName = null;
cfg.CreateMap<Course, CourseModel>()
    .ForMember(m => m.CurrentUserName, opt => opt.MapFrom(src => currentUserName));

当我们投影时,我们会在运行时替换参数:

dbContext.Courses.ProjectTo<CourseModel>(Config, new { currentUserName = Request.User.Name });

这是通过在原始表达式中捕获闭包字段名的名称,然后在将查询发送到查询提供程序之前,使用匿名对象/字典将值应用到参数值来实现的。

你也可以使用字典来建立投影值:

dbContext.Courses.ProjectTo<CourseModel>(Config, new Dictionary<string, object> { {"currentUserName", Request.User.Name} });

但是,使用字典将导致查询中出现硬编码值,而不是参数化查询,因此要谨慎使用。

Supported mapping options 

并非所有的映射选项都可以支持,因为生成的表达式必须由 LINQ 提供程序解释。只有 LINQ 提供者支持的东西才能被 AutoMapper 支持:

  • MapFrom (Expression-based) MapFrom (基于表达式的)
  • ConvertUsing (Expression-based) ConvertUsing (基于表达式的)
  • Ignore 忽略
  • NullSubstitute
  • Value transformers 价值转换器

Not supported:

不支持:

  • Condition 环境监察及审核手册
  • SetMappingOrder 图片来源: SetMappingOrder
  • UseDestinationValue 使用目的地/值
  • MapFrom (Func-based) MapFrom (基于 func 的)
  • Before/AfterMap 之前/之后地图
  • Custom resolvers 自定义解析器
  • Custom type converters 自定义类型转换器
  • ForPath
  • Value converters 价值转换器
  • Any calculated property on your domain object 域对象上的任何计算属性

此外,不支持递归或自引用目标类型,因为 LINQ 提供程序不支持这种类型。通常,分层关系数据模型需要公用表表达式(cte)来正确解析递归连接。Next 下一个 Previous 上一页

技术交流Q群: 1012481075 群内有各种流行书籍资料
原文链接:https://docs.automapper.org/en/stable/Queryable-Extensions.html

发表评论

您的电子邮箱地址不会被公开。

WeChat
WeChat
QQ
QQ
返回顶部