可查询扩展(Queryable Extensions)

当在像NHibernate或者Entity Framework之类的ORM框架中使用AutoMapper的标准方法Mapper.Map 时,您可能会注意到,当AutoMapper尝试将结果映射到目标类型时,ORM将查询图形中所有对象的所有字段。

如果你的ORM表达式是IQueryable的,你可以使用AutoMapperQueryableExtensions帮助方法去解决这个痛点。

Entity Framework为例,比如说你有一个实体OrderLine,它的成员Item与另外一个实体有关联。如果你想用ItemName属性将它映射到OrderLneDTO,标准的Mapper.Map调用将导致实体框架查询整个OrderLineItem表。

使用QueryableExtensions帮助方法代替。

相关实体:

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; }
}

你可以像这样使用Queryable Extensions

Mapper.Initialize(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>().ToList();
}
}

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

请注意,要使此功能起作用,必须在Mapping中显式处理所有类型转换。举个例子,你不能通过重写Item 类的ToString()方法来告诉实体框架只查询Name 列,并且必须明确处理数据类型转换,例如“Double”转“Decimal”。

防止延迟加载/SELECT N+1 问题

因为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()
};

以上例子将导致SELECT N + 1问题,因为每个子成员Course都将执行一次查询,除非通过ORM指定立即获取。使用LINQ投影,ORM不需要特殊配置或规范。ORM使用LINQ投影来构建所需的确切SQL查询。

自定义投影

如果成员名称不对应,或者您想要创建计算属性,则可以使用MapFrom(而不是ResolveUsing)为目标成员提供自定义表达式:

Mapper.Initialize(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使用构建的投影传递提供的表达式. 只要您的查询提供器可以解析提供的表达式,所有内容都将一直传递到数据库。

如果表达式被您的查询提供器(Entity Framework,NHibernate等)拒绝,您可能需要调整表达式,直到找到一个被接受的表达式。

自定义类型转换

有时,你需要完全替换源类型到目标类型的类型转换。在正常的运行时映射中,通过ConvertUsing方法完成。要在LINQ投影中达到类似的目的,请使用ProjectUsing方法:

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

ProjectUsingConvertUsing限制略多,因为只有Expression中允许的内容和底层LINQ提供器支持的才有效。

自定义目标类型构造函数

如果你的目标类型有自定义的构造器,但你又不想重写整个映射,那么久使用ConstructProjectionUsing方法:

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

AutoMapper将根据匹配的名称自动将目标构造函数参数与源成员匹配,因此,如果AutoMapper无法正确匹配目标构造函数,或者在构造期间需要扩展定义,则只能使用此方法。

字符串转换

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

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

显式展开

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

dbContext.Orders.ProjectTo<OrderDto>(
dest => dest.Customer,
dest => dest.LineItems);
// 或者基于字符串类型的
dbContext.Orders.ProjectTo<OrderDto>(
null,
"Customer",
"LineItems");

聚合

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”开头的学生总数。

参数化

有时候,投影需要运行时的参数做为它的值。如果需要将当前用户名作为它数据的一部分时,可以使用参数化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 });

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

支持的映射选项

不是所有映射选项都被支持,因为生成的表达式最终由LINQ提供器来解析。所以只有被LINQ提供器支持的才会被AutoMapper支持:

  • MapFrom
  • Ignore
  • UseValue
  • NullSubstitute

不支持的:

  • Condition
  • DoNotUseDestinationValue
  • SetMappingOrder
  • UseDestinationValue
  • ResolveUsing
  • Before/AfterMap
  • 自定义解析器
  • 自定义类型转换器
  • 在程序域对象上的任何计算属性

另外,递归或自引用目标类型不被LINQ提供器支持,所以也不被支持。典型的层次关系数据模型需要公共表表达式参与(CTEs)以正确地解决递归问题。

16.AutoMapper 之可查询扩展(Queryable Extensions)的更多相关文章

  1. AutoMapper queryable extensions 只找需要的字段

    http://jahav.com/blog/automapper-queryable-extensions/ How to generate a LINQ query for your DTOs Au ...

  2. Queryable查询扩展

    /// <summary> /// 查询扩展 /// </summary> /// <typeparam name="T"></typep ...

  3. sql的行转列(PIVOT)与列转行(UNPIVOT) webapi 跨域问题 Dapper 链式查询 扩展 T4 代码生成 Demo (抽奖程序)

    sql的行转列(PIVOT)与列转行(UNPIVOT)   在做数据统计的时候,行转列,列转行是经常碰到的问题.case when方式太麻烦了,而且可扩展性不强,可以使用 PIVOT,UNPIVOT比 ...

  4. aufomaper Queryable Extensions ProjectTo

    When using an ORM such as NHibernate or Entity Framework with AutoMapper's standard Mapper.Map funct ...

  5. swift学习笔记之-扩展(Extensions)

    //扩展(Extensions) import UIKit /*扩展(Extensions):扩展 就是为一个已有的类.结构体.枚举类型或者协议类型添加新功能.这包括在没有权限获取原始源代码的情况下扩 ...

  6. Dapper 链式查询 扩展

    Dapper 链式查询扩展 DapperSqlMaker   Github地址:https://github.com/mumumutou/DapperSqlMaker  欢迎大佬加入 Demo: 查询 ...

  7. MySQL -- 全文检索(查询扩展检索)

    通常用在查询的关键词太短,用户需要隐含知识进行扩展.例如,查单词database时,用户可能还希望不仅仅包含database的文档,可能还指包含mysql.oracle.db2等单词.这时就需要查询扩 ...

  8. mysql数据库优化课程---16、mysql慢查询和优化表空间

    mysql数据库优化课程---16.mysql慢查询和优化表空间 一.总结 一句话总结: a.慢查询的话找到存储慢查询的那个日志文件 b.优化表空间的话可以用optimize table sales; ...

  9. XAML实例教程系列 - 标记扩展(Markup Extensions) 六

    XAML实例教程系列 - 标记扩展(Markup Extensions) 分类: Windows 8 Silverlight2012-06-21 13:00 1139人阅读 评论(0) 收藏 举报 扩 ...

随机推荐

  1. UE4 使用VaRest的最佳实践

    背景介绍: 用Node.js,express,Mongo搭建了一个简单后台,为项目提供REST风格的API服务. 第一个查询是通过Get进行,返回一个json字符串. 在虚幻里使用VaRest来进行访 ...

  2. Jmeter获取未来时间

    1.添加前置处理器:BeanShell PreProcessor import java.text.SimpleDateFormat; import java.util.Calendar; impor ...

  3. 2019秋Java学期课程总结

    眨眼间,Java这门课程就快要到了尾声,这门课程主要学习到 搭建了Java的编译环境,安装eclipse软件,会用Java写一些简单的程序. 主要学习到的知识点有以下几点 1:通过写pta上的作业知道 ...

  4. 在一般处理程序中使用session

    public class Handler1 : IHttpHandler, IRequiresSessionState 需要继承 IRequiresSessionState接口,告诉程序要使用sess ...

  5. 按模版导出Excel

    实现效果: excel模版: ExcelHandle.java package com.common.utils; import java.io.File; import java.io.FileIn ...

  6. kolla-ansible-----常用命令

    常用命令 kolla-ansible prechecks -i multinode #部署前环境检测 kolla-genpwd #生成/etc/kolla/password.yml密码配置文件 kol ...

  7. Radio 单选框

    Radio 单选框 在一组备选项中进行单选 ¶基础用法 由于选项默认可见,不宜过多,若选项过多,建议使用 Select 选择器. 要使用 Radio 组件,只需要设置v-model绑定变量,选中意味着 ...

  8. UEditor富文本编辑器时,插入图片没有任何反应

    1.信息: Unable to find 'struts.multipart.saveDir' property setting. Defaulting to javax.servlet.contex ...

  9. PyQt GUI--信号与槽

    目录 目录 前言 系统软件 GUI的主循环 信号与槽 信号的应用 使用控件类的内建信号 自定义信号 带参数的信号 槽的应用 创建槽 信号和槽的连接 最后 前言 PyQt中的信号和槽,就是一个触发和执行 ...

  10. python3.6+RF连接mysql

    接口自动化中会遇到有操作数据库的动作 目录 1.安装第三方库 2.安装pymysql 3.数据库操作 1.安装第三方库 使用在线安装:pip install robotframework_databa ...