16.AutoMapper 之可查询扩展(Queryable Extensions)
可查询扩展(Queryable Extensions)
当在像NHibernate
或者Entity Framework
之类的ORM
框架中使用AutoMapper的标准方法Mapper.Map
时,您可能会注意到,当AutoMapper
尝试将结果映射到目标类型时,ORM
将查询图形中所有对象的所有字段。
如果你的ORM
表达式是IQueryable
的,你可以使用AutoMapper
的QueryableExtensions
帮助方法去解决这个痛点。
以Entity Framework
为例,比如说你有一个实体OrderLine
,它的成员Item
与另外一个实体有关联。如果你想用Item
的Name
属性将它映射到OrderLneDTO
,标准的Mapper.Map
调用将导致实体框架查询整个OrderLine
和Item
表。
使用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 });
ProjectUsing
比ConvertUsing
限制略多,因为只有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)的更多相关文章
- AutoMapper queryable extensions 只找需要的字段
http://jahav.com/blog/automapper-queryable-extensions/ How to generate a LINQ query for your DTOs Au ...
- Queryable查询扩展
/// <summary> /// 查询扩展 /// </summary> /// <typeparam name="T"></typep ...
- sql的行转列(PIVOT)与列转行(UNPIVOT) webapi 跨域问题 Dapper 链式查询 扩展 T4 代码生成 Demo (抽奖程序)
sql的行转列(PIVOT)与列转行(UNPIVOT) 在做数据统计的时候,行转列,列转行是经常碰到的问题.case when方式太麻烦了,而且可扩展性不强,可以使用 PIVOT,UNPIVOT比 ...
- aufomaper Queryable Extensions ProjectTo
When using an ORM such as NHibernate or Entity Framework with AutoMapper's standard Mapper.Map funct ...
- swift学习笔记之-扩展(Extensions)
//扩展(Extensions) import UIKit /*扩展(Extensions):扩展 就是为一个已有的类.结构体.枚举类型或者协议类型添加新功能.这包括在没有权限获取原始源代码的情况下扩 ...
- Dapper 链式查询 扩展
Dapper 链式查询扩展 DapperSqlMaker Github地址:https://github.com/mumumutou/DapperSqlMaker 欢迎大佬加入 Demo: 查询 ...
- MySQL -- 全文检索(查询扩展检索)
通常用在查询的关键词太短,用户需要隐含知识进行扩展.例如,查单词database时,用户可能还希望不仅仅包含database的文档,可能还指包含mysql.oracle.db2等单词.这时就需要查询扩 ...
- mysql数据库优化课程---16、mysql慢查询和优化表空间
mysql数据库优化课程---16.mysql慢查询和优化表空间 一.总结 一句话总结: a.慢查询的话找到存储慢查询的那个日志文件 b.优化表空间的话可以用optimize table sales; ...
- XAML实例教程系列 - 标记扩展(Markup Extensions) 六
XAML实例教程系列 - 标记扩展(Markup Extensions) 分类: Windows 8 Silverlight2012-06-21 13:00 1139人阅读 评论(0) 收藏 举报 扩 ...
随机推荐
- Spring Boot教程(二十七)整合Spring Security
在这一节,我们将对/hello页面进行权限控制,必须是授权用户才能访问.当没有权限的用户访问后,跳转到登录页面. 添加依赖 在pom.xml中添加如下配置,引入对Spring Security的依赖. ...
- JavaWeb_初识监听器Listener
监听器(listener):对项目起到监听的作用,它能感知到包括request(请求域),session(会话域)和applicaiton(应用程序)的初始化和属性的变化 监听器是Servlet规范中 ...
- JS框架_(Popup.js)3D对话框窗口插件
百度云盘 传送门 密码:afdo 3D对话框窗口插件效果: <!doctype html> <html lang="zh"> <head> &l ...
- sqli-labs(39)
0X01 这关和38关一样 ?id= and =1 正确 ?id=1 and 1=2 错误 不需要闭合 构造语法 0X02 ?id=;insert into users values(,"z ...
- ArrayList类源码浅析(三)
1.看一个示例 运行上述代码,抛出一个异常: 这是一个典型的并发修改异常,如果把上述代码中的125行注释,把126行打开,运行就能通过了: 原因: 1)因为在迭代的时候,使用的是Itr类的对象,在调用 ...
- docker —宝塔面板
下载个单独的系统镜像 [root@git opt]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/nginx-tomcat ...
- CentOS 6.5 安装OSA监控精灵监控主机
OSA监控是一个开源的图形化免费好用的监控,安装之前首先要配置好PHP环境, yum install httpd mysql mysql-server php-mysql php* -y 编辑http ...
- Linux高级调试与优化——同时抓取coredump和maps文件
Linux内核源码 Documentation/sysctl/kernel.txt core_pattern: core_pattern: core_pattern is used to specif ...
- DP练习题——洛谷P1970花匠
目录 题目描述: 输入输出格式: 输入格式: 输出格式: 输入输出样例: 输入样例: 输出样例: 题目分析: 解法一: 解法二: 结语: 题目描述: 洛谷\(P1970\) 花匠栋栋种了一排花,每株花 ...
- Pycharm 常用快捷键-Windows版
常用快捷键: Ctrl + / 行注释(可选中多行) Ctrl + Alt + L 代码格式化 Tab / Shift + Tab 缩进.不缩进当前行(可选中多行) Ctrl + D 复制选定的区域 ...