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) 收藏 举报 扩 ...
随机推荐
- JS框架_(JQuery.js)模拟刮奖
百度云盘:传送门 密码:6p5q 纯CSS模拟刮奖效果 <!DOCTYPE html> <html lang="en"> <head> < ...
- docker删除容器再删除镜像
1. 查询镜像 docker images 现在想删除第一个,ID为 99f85991949f 的镜像. docker rmi ID 从上面看出,需要先停到ID为 67*** 的容器. 2. 查询容 ...
- 客户端浏览器向服务器发起http请求的全过程
http协议的参考:http://blog.csdn.net/hefeng6500/article/details/75081047 (1)浏览器先搜索自身的DNS缓存 (2)操作系统搜索自身的DNS ...
- js关闭当前页面(窗口)的几种方式
1. 不带任何提示关闭窗口的js代码 代码如下: <a href="javascript:window.opener=null;window.open('','_self');win ...
- 查询Oracle表空间使用情况
,),'990.99')||'%' "使用比(%)",F.TOTAL_BYTES "空闲空间(M)",F.MAX_BYTES "最大块(M)" ...
- track-by的使用
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- leetcode 29两数相除
我理解本题是考察基于加减实现除法,代码如下: class Solution { public: //只用加减号实现除法, //不用加减号实现除法: int divide(int dividend, i ...
- nodejs之fs 模块
1.fs模块函数 * .fs.stat 检测是文件还是目录 * .fs.mkdir 创建目录 * .fs.writeFile 创建写入文件 * .fs.appendFile 追加文件 * .fs.re ...
- python 2.7 error: Microsoft Visual C++ 9.0 is required
参考:https://stackoverflow.com/questions/43645519/microsoft-visual-c-9-0-is-required 解决方法: 下载并安装Micros ...
- zabbix日志报错解决
[root@bogon ldap]# cat /tmp/zabbix_server.log 9135:20181204:085433.351 using configuration file: /us ...