When using an ORM such as NHibernate or Entity Framework with AutoMapper's standard Mapper.Map functions, you may notice that the ORM will query all the fields of all the objects within a graph when AutoMapper is attempting to map the results to a destination type.

If your ORM exposes IQueryables, you can use AutoMapper's QueryableExtensions helper methods to address this key pain.

Using Entity Framework for an example, say that you have an entity OrderLine with a relationship with an entity Item. If you want to map this to an OrderLineDTO with the Item's Name property, the standard Mapper.Map call will result in Entity Framework querying the entire OrderLine and Item table.

Use this approach instead.

Given the following entities:

    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; }
}
And the following DTO: public class OrderLineDTO
{
public int Id { get; set; }
public int OrderId { get; set; }
public string Item { get; set; }
public decimal Quantity { get; set; }
}
You can use the Queryable Extensions like so: 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();
}
}
The .ProjectTo<OrderLineDTO>() will tell AutoMapper's mapping engine to emit a select clause to the IQueryable that will inform entity framework that it only needs to query the Name column of the Item table, same as if you manually projected your IQueryable to an OrderLineDTO with a Select clause. Note that for this feature to work, all type conversions must be explicitly handled in your Mapping. For example, you can not rely on the ToString() override of the Item class to inform entity framework to only select from the Name column, and any data type changes, such as Double to Decimal must be explicitly handled as well. Preventing lazy loading/SELECT N+ problems Because the LINQ projection built by AutoMapper is translated directly to a SQL query by the query provider, the mapping occurs at the SQL/ADO.NET level, and not touching your entities. All data is eagerly fetched and loaded into your DTOs. Nested collections use a Select to project child DTOs: 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()
};
This map through AutoMapper will result in a SELECT N+ problem, as each child Course will be queried one at a time, unless specified through your ORM to eagerly fetch. With LINQ projection, no special configuration or specification is needed with your ORM. The ORM uses the LINQ projection to build the exact SQL query needed. Custom projection In the case where members names don't line up, or you want to create calculated property, you can use MapFrom (and not ResolveUsing) to supply a custom expression for a destination member: 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 passes the supplied expression with the built projection. As long as your query provider can interpret the supplied expression, everything will be passed down all the way to the database. If the expression is rejected from your query provider (Entity Framework, NHibernate, etc.), you might need to tweak your expression until you find one that is accepted. Custom Type Conversion Occasionally, you need to completely replace a type conversion from a source to a destination type. In normal runtime mapping, this is accomplished via the ConvertUsing method. To perform the analog in LINQ projection, use the ProjectUsing method: cfg.CreateMap<Source, Dest>().ProjectUsing(src => new Dest { Value = });
ProjectUsing is slightly more limited than ConvertUsing as only what is allowed in an Expression and the underlying LINQ provider will work. Custom destination type constructors If your destination type has a custom constructor but you don't want to override the entire mapping, use the ConstructProjectionUsing method: cfg.CreateMap<Source, Dest>()
.ConstructProjectionUsing(src => new Dest(src.Value + ));
AutoMapper will automatically match up destination constructor parameters to source members based on matching names, so only use this method if AutoMapper can't match up the destination constructor properly, or if you need extra customization during construction. String conversion AutoMapper will automatically add ToString() when the destination member type is a string and the source member type is not. public class Order {
public OrderTypeEnum OrderType { get; set; }
}
public class OrderDto {
public string OrderType { get; set; }
}
var orders = dbContext.Orders.ProjectTo<OrderDto>().ToList();
orders[].OrderType.ShouldEqual("Online");
Explicit expansion In some scenarios, such as OData, a generic DTO is returned through an IQueryable controller action. Without explicit instructions, AutoMapper will expand all members in the result. To control which members are expanded during projection, pass in the members you want to explicitly expand: dbContext.Orders.ProjectTo<OrderDto>(
parameters = null,
dest => dest.Customer,
dest => dest.LineItems);
// or string-based
dbContext.Orders.ProjectTo<OrderDto>(
parameters = null,
"Customer",
"LineItems");
Aggregations LINQ can support aggregate queries, and AutoMapper supports LINQ extension methods. In the custom projection example, if we renamed the TotalContacts property to ContactsCount, AutoMapper would match to the Count() extension method and the LINQ provider would translate the count into a correlated subquery to aggregate child records. AutoMapper can also support complex aggregations and nested restrictions, if the LINQ provider supports it: cfg.CreateMap<Course, CourseModel>()
.ForMember(m => m.EnrollmentsStartingWithA,
opt => opt.MapFrom(c => c.Enrollments.Where(e => e.Student.LastName.StartsWith("A")).Count()));
This query returns the total number of students, for each course, whose last name starts with the letter 'A'. Parameterization Occasionally, projections need runtime parameters for their values. Consider a projection that needs to pull in the current username as part of its data. Instead of using post-mapping code, we can parameterize our MapFrom configuration: string currentUserName = null;
cfg.CreateMap<Course, CourseModel>()
.ForMember(m => m.CurrentUserName, opt => opt.MapFrom(src => currentUserName));
When we project, we'll substitute our parameter at runtime: dbContext.Courses.ProjectTo<CourseModel>(Config, new { currentUserName = Request.User.Name });
This works by capturing the name of the closure's field name in the original expression, then using an anonymous object/dictionary to apply the value to the parameter value before the query is sent to the query provider. Supported mapping options Not all mapping options can be supported, as the expression generated must be interpreted by a LINQ provider. Only what is supported by LINQ providers is supported by AutoMapper: MapFrom
Ignore
UseValue
NullSubstitute
Not supported: Condition
DoNotUseDestinationValue
SetMappingOrder
UseDestinationValue
ResolveUsing
Before/AfterMap
Custom resolvers
Custom type converters
Any calculated property on your domain object
Additionally, recursive or self-referencing destination types are not supported as LINQ providers do not support this. Typically hierarchical relational data models require common table expressions (CTEs) to correctly resolve a recursive join.

https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions

aufomaper Queryable Extensions ProjectTo的更多相关文章

  1. 16.AutoMapper 之可查询扩展(Queryable Extensions)

    https://www.jianshu.com/p/4b23e94a7825 可查询扩展(Queryable Extensions) 当在像NHibernate或者Entity Framework之类 ...

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

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

  3. AutoMapper 10.0使用教程

    这里有个目录 什么是AutoMapper 配置 使用MapperConfiguration配置 使用Profile Instances配置 Naming Conventions(命名约定) Repla ...

  4. 【AutoMapper官方文档】DTO与Domin Model相互转换(下)

    写在前面 AutoMapper目录: [AutoMapper官方文档]DTO与Domin Model相互转换(上) [AutoMapper官方文档]DTO与Domin Model相互转换(中) [Au ...

  5. Repository 仓储,你的归宿究竟在哪?(三)-SELECT 某某某。。。

    写在前面 首先,本篇博文主要包含两个主题: 领域服务中使用仓储 SELECT 某某某(有点晕?请看下面.) 上一篇:Repository 仓储,你的归宿究竟在哪?(二)-这样的应用层代码,你能接受吗? ...

  6. 【记录】AutoMapper Project To OrderBy Skip Take 正确写法

    AutoMapper:Queryable Extensions 示例代码: using (var context = new orderEntities()) { return context.Ord ...

  7. 【记录】AutoMapper Project To not support ResolveUsing

    示例代码: public List<OrderLineDTO> GetLinesForOrder(int orderId) { Mapper.CreateMap<OrderLine, ...

  8. AutoMapper 使用实践

    一.   使用意图 常常在开发过程中,碰到一个实体上的属性值,要赋值给另外一个相类似实体属性时,且属性有很多的情况.一般不利用工具的话,就要实例化被赋值实体B,然后再将实体A的字段一个个赋值给B的属性 ...

  9. DTO学习系列之AutoMapper(四)

    本篇目录: Mapping Inheritance-映射继承 Queryable Extensions (LINQ)-扩展查询表达式 Configuration-配置 Conditional Mapp ...

随机推荐

  1. 哈希 poj 3349

    n个雪花 判断有没有相同的 正的和倒的相同都可以 哈希一下  比的少了就可以 #include<stdio.h> #include<algorithm> #include< ...

  2. Java算法-冒泡排序

    冒泡排序是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成.这个算法的名字 ...

  3. mac下卸载MySQL

    所有跟mysql相关进程都停止掉, 然后终端输入: cd ~/ sudo rm /usr/local/mysqlsudo rm -rf /usr/local/var/mysqlsudo rm -rf ...

  4. java,js,jstl,EL的简单交互

    EL全名Expression Language.EL提供了在JSP脚本编制元素范围外使用运行时表达式的功能. 脚本编制元素是指页面中能够用于在JSP文件中嵌入java代码的元素. JSP标准标记库(j ...

  5. python模块介绍二。

    全局变量 全局变量 python在一个.py文件内部自动添加了一些全局变量 print(vars()) #查看当前的全局变量 执行结果: {'__package__': None, '__loader ...

  6. pyhthon --递归,装饰器

    递归: 递归的概念很简单,如果函数包含了对其自身的调用,该函数就是递归.拗口一点的定义是,如果一个新的调用能在相同过程中较早的调用结束之前开始,那么该过程就是递归. """ ...

  7. Eclipse中Jquery报错

    在网上看到很多 jQuery-xxx.js 在eclipse中报错的解决方案大多是说 项目右键 Properties->Validation->JSP Content Validator ...

  8. SQL Server修改代理作业的下次运行时间

    有这个现象,如果我把服务器时间调快2天运行作业,那么会发现作业的下次运行时间会变成两天+1的时间,即使是把服务器时间调正常后,这个下次运行时间也是无法调回来的 那么,要修改会正常的下次作业时间,可以这 ...

  9. NAnt0.92版本首次在windows 8.1的机子上运行报错的问题解决

    在官网上下载的0.92版本,各方面都配置好之后,用命令行运行,却提示报错,如下: 具体的错误提示文字是这样的: 获取ConfigurationFileLocation异常. System.Securi ...

  10. Windows Directory ACL Security Check By ACL Baseline

    catalog . Windows NTFS ACL(MAC) Permission . How the System Uses ACLs . 服务器不安全ACL配置带来的攻击向量 . NTFS AC ...